10
>>> class A(object): pass
>>> def func(cls): pass
>>> A.func = func
>>> A.func
<unbound method A.func>

这个作业如何创建一个方法?分配对类执行以下操作似乎不直观:

  • 将函数变成未绑定的实例方法
  • 将封装的函数classmethod()转换为类方法(实际上,这非常直观)
  • 将封装的staticmethod()函数转换为函数

似乎对于第一个,应该有一个instancemethod(),而对于最后一个,根本不应该有一个包装函数。我知道这些是在一个class块内使用的,但为什么要在它之外应用呢?

但更重要的是,将函数分配到类中究竟是如何工作的?解决这三件事的魔法发生了什么?

更令人困惑的是:

>>> A.func
<unbound method A.func>
>>> A.__dict__['func']
<function func at 0x...>

但我认为在检索属性时,这与描述符有关。我认为这与这里的属性设置没有太大关系。

4

5 回答 5

4

没错,这与描述符协议有关。描述符是如何在 Python 中实现将接收者对象作为方法的第一个参数传递的。您可以从此处阅读有关 Python 属性查找的更多详细信息。以下显示在较低级别上,当您执行 A.func = func; 时会发生什么 A.功能:

# A.func = func
A.__dict__['func'] = func # This just sets the attribute
# A.func
#   The __getattribute__ method of a type object calls the __get__ method with
#   None as the first parameter and the type as the second.
A.__dict__['func'].__get__(None, A) # The __get__ method of a function object
                                    # returns an unbound method object if the
                                    # first parameter is None.
a = A()
# a.func()
#   The __getattribute__ method of object finds an attribute on the type object
#   and calls the __get__ method of it with the instance as its first parameter.
a.__class__.__dict__['func'].__get__(a, a.__class__)
#   This returns a bound method object that is actually just a proxy for
#   inserting the object as the first parameter to the function call.

因此,查找类或实例上的函数将其转换为方法,而不是将其分配给类属性。

classmethod并且staticmethod只是略有不同的描述符,classmethod 返回绑定到类型对象的绑定方法对象,而 staticmethod 只返回原始函数。

于 2010-02-22T01:23:20.877 回答
3

您必须考虑的是,在 Python 中一切都是对象。通过确定更容易理解正在发生的事情。如果你有一个函数def foo(bar): print bar,你可以做spam = foo和调用spam(1),当然,得到1

Python 中的对象将它们的实例属性保存在一个__dict__用指向其他对象的“指针”调用的字典中。由于Python 中的函数也是对象,因此可以将它们作为简单变量进行分配和操作,传递给其他函数等。Python 的面向对象实现利用了这一点,并将方法视为属性,将其__dict__视为物体。

实例方法的第一个参数始终是实例对象本身,通常称为self(但也可以称为thisbanana)。当直接在 上调用方法时class,它不会绑定到任何实例,因此您必须给它一个实例对象作为第一个参数 ( A.func(A()))。当您调用绑定函数( A().func()) 时,该方法的第一个参数self, 是隐式的,但在幕后 Python 的做法与直接调用未绑定函数并将实例对象作为第一个参数传递完全相同。

如果理解了这一点,那么分配A.func = func(幕后正在做的事情A.__dict__["func"] = func)给你留下了一个不受约束的方法,这一点不足为奇。

在您的示例中clsdef func(cls): pass实际上将传递的是selftype 的 instance() A。当您应用classmethodor装饰器时,只需在调用函数/方法期间获取第一个参数,并将其转换为其他内容,然后再调用函数。staticmethod

classmethod接受第一个参数,获取class实例的对象,并将其作为第一个参数传递给函数调用,同时staticmethod简单地丢弃第一个参数并在没有它的情况下调用函数。

于 2010-02-21T22:42:53.220 回答
0

第 1 点:您定义的函数在 Python 中func作为First-Class Object存在。

第 2 点:Python 中的类将它们的属性存储在它们的__dict__.

那么当你在 Python 中将函数作为类属性的值传递时会发生什么?该函数存储在类中__dict__,使其成为该类的方法,通过调用您为其分配的属性名称来访问。

于 2010-02-21T22:30:22.533 回答
0

关于 MTsoul 对 Gabriel Hurley 的回答的评论:

不同的是它func有一个__call__()方法,使其“可调用”,即您可以将()运算符应用于它。查看Python 文档(在该页面上搜索__call__)。

于 2010-02-21T22:39:41.050 回答