2016-09-21 6 views
1

В настоящее время я пытаюсь понять, где разница между использованием a+b и a.__add__(b) - это когда дело доходит до пользовательских классов. Существует множество веб-сайтов, в которых говорится, что использование оператора «+» приводит к использованию специального метода __add__ - который пока прекрасен.Разница между a + b и a .__ add __ (b)

Но когда я запускаю следующий пример, я получаю два разных результата.

class C: 
    def __add__(self, other): 
    print("C.__add__", self, other) 
    return "result" 
    def __radd__(self, other): 
    print("C.__radd__", self, other)  
    return "reversed result" 

c = C() 
print(1+c) 
print() 
print(1 .__add__(c)) 
print(int.__add__(1,c)) 

Результат:

C.__radd__ <C object at 0x7f60b92e9550> 1 
reversed result 

NotImplemented 
NotImplemented 

Теперь от того, что я понял, при выполнении 1+c проверки Python/выполняет Int __add__ метод - считает, что нет реализации для добавления Int и объекты C - возвращает NotImplemented - который позволяет Python знать, проверить объект C для __radd__ и выполняет код внутри.

1+c Почему результат выполнения __radd__ кода, но две другие версии просто возвращающегося NotImplemented без проверки __radd__ ??

+7

Er, потому что вы назвали '__add__' напрямую? Как вы заявляете, Python делает резервную копию только при использовании оператора '+'. –

+0

Угадайте, что я не знал, что резервное копирование не будет использоваться, если я использовал '__add__' напрямую. – Korred

ответ

3

a+b равнозначно import operator; operator.add(a,b). Он начинается с вызова a.__add__(b), а затем, при необходимости, b.__radd__(a). Но ifsubclass(type(b), type(a)), затем b.__radd__(a) называется первым.

На основе docs on "special" methods:

  • Относительно __add__():

    __add__() вызывается для выполнения арифметических операций двоичного "+" операции. Например, для оценки выражения x + y, где x является экземпляром класса, который имеет метод __add__(), вызывается x.__add__(y).

    Если один из этих методов не поддерживает операцию с прилагаемыми аргументами, он должен вернуть NotImplemented.

  • Что касается __radd__():

    Эти функции вызываются только если левый операнд не поддерживает соответствующую операцию и операнды различных типов. Например, чтобы оценить выражение x + y, где y является экземпляром класса, который имеет метод __radd__(), вызывается y.__radd__(x), если x.__add__(y) возвращает NotImplemented.

    Если тип нужного операнда является подклассом типа левого операнда, и этот подкласс предоставляет отраженный метод для операции, этот метод будет вызываться перед неодушевленным методом левого операнда. Такое поведение позволяет подклассам переопределять операции своих предков.

Объяснение с примерами на основе поведения:

Случай 1:

>>> print 1+c 
('C.__radd__', <__main__.C instance at 0x7ff5631397a0>, 1) 
reversed result 

Эти функции radd называются только если левый операнд не поддерживает соответствующая операция и операнды имеют разные типы. В этом случае 1 не поддерживает добавление класса, следовательно, он возвращается к функции __radd__() класса C. В случае __radd__ не реализовать в C() классе, он упал бы обратно __add__()

Вариант 2:

>>> 1 .__add__(c) 
NotImplemented 
>>> c .__add__(1) 
('C.__add__', <__main__.C instance at 0x7ff563139830>, 1) 
'result' 

1 .__add__(c) дает NotImplemented, как 1 имеет int типа и add из int класса не поддерживает add с объектом класса C. Но c .__add(1) запустить, потому что класс C() поддерживает это.

Случай 3:

>>> int.__add__(1, c) 
NotImplemented 
>>> C.__add__(c, 1) 
('C.__add__', <__main__.C instance at 0x7ff5610add40>, 1) 
'result' 

Подобно case 2. Но здесь вызов выполняется через класс с первым аргументом как объектом этого класса. Поведение будет таким же.

Случай 4:

>>> int.__add__(c, 1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: descriptor '__add__' requires a 'int' object but received a 'instance' 
>>> C.__add__(1, c) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: unbound method __add__() must be called with C instance as first argument (got int instance instead) 

И наоборот из case 3. Как очищается от трассировки стека, __add__ ожидал, что объект вызывающего класса будет первым аргументом, в противном случае это приведет к исключению.

+0

Так что в основном происходит за сценой, когда используется оператор '+', ответственный за резерв? – Korred

+0

Обновлен ответ. Я изо всех сил пытался объяснить, как это работает. В случае сомнений, дайте мне знать. Я также сделаю редактирование и постараюсь быть более ясным. –

+0

Возможно, я должен перефразировать мой вопрос - в чем разница между использованием оператора '+' и функцией __add__? Чтение о специальных методах похоже, что их нет - но, по-видимому, дело «NotImplemented» обрабатывается по-разному – Korred

Смежные вопросы