Запись свойств через __setattr__ в ООП в Python
Метод __setattr__ вызывается при каждой
попытке записать атрибут через точку -
и в новое имя, и в уже существующее, и
в приватное. В качестве параметров он
принимает имя свойства и значение, которое
ему пытаются присвоить.
Давайте посмотрим на практическом примере.
Пусть у нас дан вот такой класс Test:
class Test:
pass
Давайте сделаем в этом классе метод
__setattr__, который будет выводить
имя свойства, к которому произошло
обращение, и значение, которое этому
свойству пытаются установить:
class Test:
def __setattr__(self, name, value):
print(name, value)
Проверим работу нашего класса:
test = Test()
test.prop = 'value' # __setattr__ выведет 'prop value'
Давайте теперь будем устанавливать значение
свойству, имя которого пришло в параметре
name:
class Test:
def __setattr__(self, name, value):
super().__setattr__(name, value) # устанавливаем значение
Теперь мы сможем записывать данные снаружи, в том числе в новые имена:
test = Test()
test.prop1 = 1 # запишем 1
test.prop2 = 2 # запишем 2
Давайте попробуем записать данные в несуществующее свойство - это будет работать:
test = Test()
test.prop3 = 3 # запишем 3
print(test.prop3) # выведет 3
Пусть мы не хотим разрешать записывать в
несуществующие свойства. И, вообще, хотим
разрешить запись только в свойства prop1
и prop2.
Это легко сделать - достаточно в методе __setattr__
добавить соответствующее условие:
class Test:
def __setattr__(self, name, value):
# Напишем условие:
if name == 'prop1' or name == 'prop2':
super().__setattr__(name, value)
def __getattr__(self, name):
return super().__getattribute__(name)
Если таких свойств будет много, то не очень удобно перечислять их все в условии.
Давайте запишем разрешенные для записи свойства
в список и будем проверять наличие свойства
в этом списке с помощью оператора
in:
class Test:
def __setattr__(self, name, value):
properties = ['prop1', 'prop2'] # разрешенные свойства
if name in properties:
super().__setattr__(name, value)
def __getattr__(self, name):
return super().__getattribute__(name)
На самом деле, конечно же, не стоит разрешать всем подряд записывать в приватные свойства, иначе пропадает суть этих приватных свойств (проще сделать их публичными и все).
Поэтому метод __setattr__ следует применять
только тогда, когда в этом действительно
есть необходимость.