Перегрузка оператора += (iadd)
Что лучше возвращать из перегруженного магического метода __iadd__
: новый объект, или тот же самый, через self
?
Что лучше возвращать из перегруженного магического метода __iadd__
: новый объект, или тот же самый, через self
?
Возвращать нужно новый объект.
Это принципиально важный момент, потому что при использовании оператора +=
(и других) с изменяемыми объектами могут возникнуть нежелательные проблемы.
Покажу наглядно, что может произойти, если мы переопределим этот оператор и все-таки будем возвращать тот же самый объект (другими словами - сделаем его изменяемым):
class SomeObj:
def __init__(self, val):
self.val = val
def __iadd__(self, other):
self.val += other.val
return self
a = SomeObj(5)
b = SomeObj(6)
print(id(a)) # 19774544
a += b
print(id(a)) # 19774544
print(a.val) # 11
Как мы видим, адрес объекта в памяти остался тем же самым, потому что был возвращен исходный объект, и на первый взгляд все работает правильно и без ошибок, но если мы вдруг захотим написать как-то так:
a = SomeObj(5)
print(a.val) # 5
b = a
a += SomeObj(1)
print(a.val) # 6
print(b.val) # 6
То увидим, что вследствие возврата того же самого объекта - в переменной b
, которая содержит ссылку на исходный объект, изменилось значение атрибута val
, не смотря на то, что к этой переменной никто не обращался, в будущем это может быть чревато различными side-эффектами.
В случае же правильной реализации с созданием нового объекта:
class SomeObj:
def __init__(self, val):
self.val = val
def __iadd__(self, other):
return SomeObj(self.val + other.val)
a = SomeObj(5)
b = SomeObj(6)
print(id(a)) # 39177904
a += b
print(id(a)) # 39176240
print(a.val) # 11
Заметим, что в данном случае ссылка в переменной a
теперь указывает на другой, вновь созданный объект.
Теперь убедимся, что пример с копированием ссылки отрабатывает корректно:
a = SomeObj(5)
print(a.val) # 5
b = a
a += SomeObj(1)
print(a.val) # 6
print(b.val) # 5
Все правильно, значение в объекте переменной b
не изменилось, потому что в результате операции был создан новый объект.