3

self在对象上调用函数时,Python 是如何绑定的?我试图理解为什么会这样:

class A(object):
    def __init__(self):
        self.name = 'class A'

    def show_name(self):
        print(self, self.name)

class B(object):
    def __init__(self, a):
        self.name = 'class B'
        self.show_name = a.show_name

A().show_name()
B(A()).show_name()

输出

(<self.A object at 0x7f9d35a06e50>, 'class A')
(<self.A object at 0x7f9d35a06e50>, 'class A')

编辑:它怎么知道show_name应该将 class 的实例A作为其第一个 ( self) 参数,而不是 class 的实例B

4

2 回答 2

6

有关完整详细信息,请参阅此博客文章。但我可以在这里总结一下。

关键是理解绑定方法。绑定方法只是一个包含实际函数对象的对象,以及它绑定到的实例。像这样的东西:

class BoundMethod(object):
    def __init__(self, function, instance):
        self.__func__ = function
        self.__self__ = instance
    def __call__(self, *args, **kwargs):
        return self.__func__(self.__self__, *args, **kwargs)

绑定方法是一流的对象,因此您可以传递它们并检查它们:

>>> class C:
...     def foo(self):
...         print(self)
>>> C.foo
<function __main__.foo>
>>> c = C()
>>> c.foo
<bound method C.foo of <__main__.C object at 0x10ab90a90>>
>>> c.foo.__func__
<function __main__.foo>
>>> c.foo.__func__ is C.foo, c.foo.__self__ is c
(True, True)

你甚至可以手动构建它们:

>>> import types
>>> f = types.MethodType(C.foo, 2)
>>> f
<bound method int.foo of 2>
>>> f()
2

(虽然我上面的 Python 类显然不是真正的代码,但它并不遥远;如果你做一些愚蠢的事情,比如尝试绑定绑定方法或不可调用的方法,你甚至会得到大部分相同的错误。)


那么,c.foo最终如何绑定c?要真正理解这一点,您需要了解描述符。但简短的版本是这样的:

Python 中几乎每种类型都有一个额外的方法__get__。这用于属性访问。基本上,当您键入 时c.foo,它会执行以下操作(忽略基类、插槽、getattr 覆盖等):

try:
    return c.__dict__['foo']
except KeyError:
    value = type(c).__dict__['foo']
    try:
        return value.__get__(c)
    except AttributeError:
        return value

函数对象上的__get__方法返回一个绑定方法(绑定到它的参数)。


所以,回到你的例子,显然B(A()).show_name()打印出关于A因为B(A()).show_name被绑定到一个A实例的东西......但是这是怎么发生的?

好吧,让我们追溯一下:

>>> a = A()
>>> b = B(a)
>>> b.show_name

在你的__init__函数中,你这样做:

self.show_name = a.show_name

那只是将已经绑定的方法复制a.show_name到普通的实例变量self.show_name中。因此,当您稍后查找时b.show_name,它是相同的绑定方法,绑定到a. 如果要重新绑定它,则必须手动进行,例如:

self.show_name = types.MethodType(a.show_name.__func__, self)
于 2013-08-20T18:53:43.450 回答
4

当您a.show_nameB's 的构造函数中执行此操作时,它已被绑定。因此,当您show_name从 的实例中获取时B,它已经绑定到a,因此它不会绑定到 的实例B

于 2013-08-20T18:38:33.060 回答