答案可以在 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+ 中已被删除)。不幸的是,在这种情况下,旧式类恰好适合您,而新式类将需要更多代码。
Edit:
您还可以按照最初尝试的方式执行此操作,方法是在class而不是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
恐怕有时我会在元类中思考,更简单的解决方案会更好:)