Должен ли __iadd__ возвращать новый объект или манипулировать собой? - PullRequest
1 голос
/ 02 февраля 2020

Я новичок в python (в целом oop), и мне интересно, каковы ожидания по __iadd__. Должен ли он создать новый объект или изменить существующий?

Итак, если я рассмотрю, например, класс фракций и добавлю два, используя +=:

q1 = Fract(1, 2) #q1 = 1/2
q2 = Fract(2, 3) #q2 = 2/3
id1 = id(q1)
q1 += q2 #q1 = 5/6
id2 = id(q2)

Теперь ожидание id1 == id2? Это просто вопрос стиля или это отличает приложения реального мира?

Я попробовал это с классом build в complex, и там показалось, что создается новый объект.

Ответы [ 2 ]

1 голос
/ 02 февраля 2020

Если вам нужен изменяемый объект, вы должны манипулировать self и return self (не новым объектом). Если вы определите __iadd__ с чем-то вроде

def __iadd__(self, other):
    self.n += ...
    self.d += ...

, функция вернет None, что и присваивается имени q1. Если у вас есть другая ссылка на объект, вы увидите, что он был обновлен на месте, но q1 больше не будет ссылаться на него.

Чтобы создать небольшой пример, давайте посмотрите на Fract.__imul__, который не возвращает self (поскольку реализация проще, чем __iadd__):

class Fract:
    def __init__(self, x, y):
        self.n = x
        self.d = y

    def __imul__(self, other):
        self.n *= other.n
        self.d *= other.d

x1 = Fract(1,2)
x2 = x1

y = Fract(1,2)

x1 *= y

После запуска этого кода вы увидите, что x1 теперь привязан к None, но x2 по-прежнему привязан к исходному экземпляру Fract, который теперь имеет x2.d == 4, как и ожидалось.

Так что даже если вы хотите обновить левый операнд в место, __iadd__ и др. по-прежнему необходимо возвращать self, чтобы имя сохраняло то же значение.

Если вы хотите, чтобы Fract было неизменным (например, int или complex) , вместо этого верните новый объект.

def __imul__(self, other):
    return Fract(self.n*other.n, self.d*other.d)
0 голосов
/ 02 февраля 2020

, так как ваш класс Fract хранит число с плавающей точкой, тогда он должен вести себя как число с плавающей точкой, поэтому давайте посмотрим, что происходит с плавающей точкой:

q1 =  1 / 2
q2 = 2 / 3
id1 = id(q1)
q1 += q2
id2 = id(q1)

print(id1 == id2)
# ouptut: False

, поэтому в случае flaots __iadd__ возвращает новый объект Аналогично, ваш Fract класс должен возвращать новый объект.

конечно, если ваш Fract класс более сложен, сохраняя состояние других переменных, тогда имеет смысл манипулировать и возвращать self

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...