class MyClass:
def __init__(self, value):
self._value = value
@property
def value(self):
return self._value
Попытка присвоить значение `obj.value = new_value` вызовет `AttributeError`.
Есть несколько способов сделать атрибуты объектов доступными только для чтения в Python, предотвращая их изменение через сеттеры:
property
):
Это наиболее Pythonic подход. Вы определяете свойство с помощью декоратора @property
и предоставляете только геттер (метод для получения значения атрибута), но не определяете сеттер (метод для установки значения).
class MyClass:
def __init__(self, value):
self._my_attribute = value # Используем соглашение об именовании
@property
def my_attribute(self):
return self._my_attribute
# Пример использования:
obj = MyClass(10)
print(obj.my_attribute) # Вывод: 10
try:
obj.my_attribute = 20
except AttributeError as e:
print(e) # Вывод: can't set attribute
В этом примере, атрибут my_attribute
можно получить, но нельзя изменить напрямую. Попытка присвоить ему новое значение приведет к AttributeError
.
Соглашение об именовании (начинать имя атрибута с _
) говорит о том, что атрибут предназначен для внутреннего использования и не должен изменяться напрямую извне класса. Это, конечно, не делает атрибут "полностью приватным" в смысле ограничения доступа на уровне языка, но служит четким сигналом для других разработчиков.
__slots__
:
__slots__
не делает атрибуты неизменяемыми напрямую, но если вы не включите атрибут в __slots__
, то он не сможет быть добавлен динамически. Это полезно для контроля атрибутов, которые могут существовать в вашем объекте.
class MyClass:
__slots__ = ['my_attribute']
def __init__(self, value):
self.my_attribute = value
obj = MyClass(10)
print(obj.my_attribute)
try:
obj.new_attribute = 20 # Attempt to add a non-declared attribute
except AttributeError as e:
print(e) # Output: 'MyClass' object has no attribute 'new_attribute'
Важно отметить, что __slots__
в первую очередь предназначен для оптимизации использования памяти, а не для защиты атрибутов. Он предотвращает создание __dict__
для каждого экземпляра класса, что экономит память, но не делает атрибуты неизменяемыми в принципе. В данном примере my_attribute все еще можно изменить, просто нельзя добавить новый атрибут.
Можно добавить дополнительную логику для предотвращения изменений в сеттере (если он определен), например, выбрасывать исключение, если кто-то пытается установить новое значение.
class MyClass:
def __init__(self, value):
self._my_attribute = value
@property
def my_attribute(self):
return self._my_attribute
@my_attribute.setter
def my_attribute(self, value):
raise AttributeError("Атрибут доступен только для чтения")
# Пример использования:
obj = MyClass(10)
print(obj.my_attribute)
try:
obj.my_attribute = 20
except AttributeError as e:
print(e) # Вывод: Атрибут доступен только для чтения
В данном примере, хотя сеттер и существует, он всегда выбрасывает исключение, предотвращая любое изменение атрибута. Это дает более явный контроль над тем, что происходит при попытке установить атрибут.
attrs
(внешняя библиотека):
Библиотека attrs
предоставляет удобный способ определения классов данных с автоматической генерацией методов и возможностью определения атрибутов только для чтения.
import attr
@attr.s(frozen=True) # frozen=True делает все атрибуты неизменяемыми
class MyClass:
my_attribute = attr.ib()
# Пример использования:
obj = MyClass(my_attribute=10)
print(obj.my_attribute)
try:
obj.my_attribute = 20
except attr.exceptions.FrozenInstanceError as e:
print(e) # Вывод: cannot assign to field 'my_attribute'
attrs
требует установки (pip install attrs
) и предлагает мощные инструменты для управления атрибутами классов данных.
Выбор подхода зависит от конкретных требований вашего проекта. property
- наиболее распространенный и Pythonic способ. __slots__
- для оптимизации памяти и контроля динамических атрибутов. attrs
- для создания неизменяемых классов данных. Добавление логики в сеттер для property - наиболее гибкий подход.