classmethod对象是如何工作的?

9 投票
1 回答
4659 浏览
提问于 2025-04-15 15:40

我在理解Python中的类方法(classmethod)是怎么运作的时遇到了一些困难,尤其是在元类(metaclasses)和__new__的上下文中。在我的特殊情况下,我想在遍历传给__new__members时,获取类方法成员的名字。

对于普通的方法,它的名字简单地存储在一个叫__name__的属性里,但对于类方法来说,似乎没有这样的属性。我甚至看不到类方法是怎么被调用的,因为也没有__call__这个属性。

有没有人能给我解释一下类方法是怎么工作的,或者指引我去一些相关的文档?我在谷歌上找了很多,结果都没找到。谢谢!

1 个回答

24

一个 classmethod 对象是一个描述符。你需要了解描述符是怎么工作的。

简单来说,描述符就是一个有 __get__ 方法的对象,这个方法需要三个参数:self、一个 instance 和一个 instance type

在正常查找属性的时候,如果你查找的对象 A 有一个 __get__ 方法,那么这个方法会被调用,返回的结果会替代原来的对象 A。这就是为什么当你在一个对象上调用方法时,函数(它们也是描述符)会变成绑定方法的原因。

class Foo(object):
     def bar(self, arg1, arg2):
         print arg1, arg2

foo = Foo()
# this:
foo.bar(1,2)  # prints '1 2'
# does about the same thing as this:
Foo.__dict__['bar'].__get__(foo, type(foo))(1,2)  # prints '1 2'

一个 classmethod 对象的工作方式是一样的。当它被查找时,它的 __get__ 方法会被调用。classmethod__get__ 方法会忽略与 instance 对应的参数(如果有的话),只会把 instance_type 传递给被包装的函数的 __get__ 方法。

下面是一个简单的示意图:

In [14]: def foo(cls):
   ....:     print cls
   ....:     
In [15]: classmethod(foo)
Out[15]: <classmethod object at 0x756e50>
In [16]: cm = classmethod(foo)
In [17]: cm.__get__(None, dict)
Out[17]: <bound method type.foo of <type 'dict'>>
In [18]: cm.__get__(None, dict)()
<type 'dict'>
In [19]: cm.__get__({}, dict)
Out[19]: <bound method type.foo of <type 'dict'>>
In [20]: cm.__get__({}, dict)()
<type 'dict'>
In [21]: cm.__get__("Some bogus unused string", dict)()
<type 'dict'>

关于描述符的更多信息可以在这里找到(还有其他地方): http://users.rcn.com/python/download/Descriptor.htm

如果你想获取被 classmethod 包装的函数的名字,可以参考以下内容:

In [29]: cm.__get__(None, dict).im_func.__name__
Out[29]: 'foo'

撰写回答