TLDR:不可能在实例上定义适当的、未绑定的方法;这也适用于特殊方法。由于绑定方法是一等对象,因此在某些情况下差异并不明显。但是,特殊方法总是在需要时被 Python 查找为正确的、未绑定的方法。
您始终可以手动回退到使用更通用属性访问的特殊方法。属性访问涵盖存储为属性的绑定方法以及根据需要绑定的未绑定方法。这类似于如何__repr__
或其他方法使用属性来定义其输出。
class A:
def __init__(self, name):
self.name = name
def __repr__(self):
# call attribute to derive __repr__
return self.__representation__()
def __representation__(self):
return f'{self.__class__.__name__}({self.name})'
def __str__(self):
# return attribute to derive __str__
return self.name
未绑定与绑定方法
Python中的方法有两种含义:类的未绑定方法和该类实例的绑定方法。
未绑定方法是类或其基类之一上的常规函数。它可以在类定义期间定义,也可以在以后添加。
>>> class Foo:
... def bar(self): print('bar on', self)
...
>>> Foo.bar
<function __main__.Foo.bar(self)>
一个未绑定的方法在类上只存在一次——它对所有实例都是相同的。
绑定方法是已绑定到特定实例的未绑定方法。这通常意味着该方法是通过实例查找的,该实例调用函数的__get__
方法。
>>> foo = Foo()
>>> # lookup through instance
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x10c3b6390>>
>>> # explicit descriptor invokation
>>> type(foo).bar.__get__(foo, type(Foo))
<bound method Foo.bar of <__main__.Foo object at 0x10c3b6390>>
就 Python 而言,“方法”一般是指根据需要绑定到其实例的未绑定方法。当 Python 需要特殊方法时,它直接调用未绑定方法的描述符协议。结果,在类上查找该方法;实例上的属性被忽略。
对象上的绑定方法
每次从其实例中获取绑定方法时,都会重新创建绑定方法。结果是一个具有标识的一流对象,可以存储和传递,并在以后调用。
>>> foo.bar is foo.bar # binding happens on every lookup
False
>>> foo_bar = foo.bar # bound methods can be stored
>>> foo_bar() # stored bound methods can be called later
bar on <__main__.Foo object at 0x10c3b6390>
>>> foo_bar()
bar on <__main__.Foo object at 0x10c3b6390>
能够存储绑定方法意味着它们也可以存储为属性。在其绑定实例上存储绑定方法使其看起来类似于未绑定方法。但实际上存储绑定方法的行为略有不同,可以存储在任何允许属性的对象上。
>>> foo.qux = foo.bar
>>> foo.qux
<bound method Foo.bar of <__main__.Foo object at 0x10c3b6390>>
>>> foo.qux is foo.qux # binding is not repeated on every lookup!
True
>>> too = Foo()
>>> too.qux = foo.qux # bound methods can be stored on other instances!
>>> too.qux # ...but are still bound to the original instance!
<bound method Foo.bar of <__main__.Foo object at 0x10c3b6390>>
>>> import builtins
>>> builtins.qux = foo.qux # bound methods can be stored...
>>> qux # ... *anywhere* that supports attributes
<bound method Foo.bar of <__main__.Foo object at 0x10c3b6390>>
就 Python 而言,绑定方法只是常规的、可调用的对象。正如它无法知道是否too.qux
是 的方法too
一样,它也无法推断是否too.__repr__
是方法。