Ответ находится в двух типах классов, которые есть в Python.
В первом предоставленном вами фрагменте кода используется устаревший класс "старого стиля" (вы можете сказать, что он ничего не подклассифицирует - перед двоеточием нет ничего). Его семантика своеобразна. В частности, вы можете добавить специальный метод к экземпляру:
class Foo:
def __init__(self, num):
self.num = num
def _fn(other):
return self.num + other.num
self.__add__ = _fn
и получите правильный ответ:
>>> f = Foo(2)
>>> g = Foo(1)
>>> f + g
3
Но, подкласс dict
означает, что вы генерируете класс нового стиля. И семантика перегрузки операторов различна:
class Foo (object):
def __init__(self, num):
self.num = num
def _fn(other):
return self.num + other.num
self.__add__ = _fn
>>> f = Foo(2)
>>> g = Foo(1)
>>> f + g
Traceback ...
TypeError: unsupported operand type(s) for +: 'Foo' and 'Foo'
Чтобы это работало с классами нового стиля (которые включают в себя подклассы dict
или почти с любым другим типом, который вы найдете), вы должны убедиться, что в классе определен специальный метод. Вы можете сделать это через метакласс:
class _MetaFoo(type):
def __init__(cls, name, bases, args):
def _fn(self, other):
return self.num + other.num
cls.__add__ = _fn
class Foo(object):
__metaclass__ = _MetaFoo
def __init__(self, num):
self.num = num
>>> f = Foo(2)
>>> g = Foo(1)
>>> f+g
3
Кроме того, семантическая разница означает, что в самом первом случае я мог определить свой локальный метод добавления с одним аргументом (self
, который он использует, захватывается из окружающей области, в которой он определен), но с новым стилем классы, Python ожидает явной передачи обоих значений, поэтому внутренняя функция имеет два аргумента.
Как упомянул предыдущий комментатор, лучше избегать классов старого стиля, если это возможно, и придерживаться классов нового стиля (классы старого стиля удалены в Python 3+). К сожалению, классы старого стиля сработали для вас в этом случае, когда классы нового стиля потребуют больше кода.
Редактировать:
Вы также можете сделать это больше, чем пытались изначально, установив метод для класса вместо instance :
class Foo(object):
def __init__(self, num):
self.num = num
setattr(Foo, '__add__', (lambda self, other: self.num + other.num))
>>> f = Foo(2)
>>> g = Foo(1)
>>> f+g
3
Боюсь, я иногда думаю в метаклассах, где более простые решения были бы лучше:)