type(MyClass.myclassmethod)返回"方法",而不是" classmethod"

时间:2018-01-11 15:32:54

标签: python python-3.x inheritance class-method

当子类化过程中方法的类型发生变化时,我偶然发现了这种奇怪的行为:

class A:
    def f(self, x):
        return x**2

class B(A):
    @classmethod
    def f(cls, x):            
        return x**2

如果我现在要求B.f的类型,我会得到(据称)错误的答案:

In [37]: type(B.f)
Out[37]: method

虽然这可以按预期工作:

In [39]: type(B.__dict__["f"])
Out[39]: classmethod

(见Python 3.4和3.6。)

这只是一个错误还是有特定的原因?

属性f.__dict__["f"]项之间的区别是什么?我以为他们是一样的。

在测试套件中,我试图支持要测试的类中的两种类型的方法。为了能够做到这一点,我需要知道类型,以传递正确数量的参数。如果它是一种常规方法(即self是第一个参数),我只是明确地传递None,无论如何都不应该在方法中使用它,因为它不依赖于实例。

也许有更好的方法来做这件事,比如打字方法的调用。但是可能会出现这种情况并不容易的情况,例如方法有*args**kwargs ......因此我选择了明确的类型检查,但此时却陷入困境。 / p>

1 个答案:

答案 0 :(得分:2)

不,这不是错误,这是正常行为。在类上访问时,classmethod会生成绑定方法。这正是classmethod的要点,将函数绑定到您访问它的类或您访问它的实例的类。

与函数和property对象类似,classmethoddescriptor object,它实现了__get__方法。访问实例或类的属性将委托给__getattribute__ method,该挂钩的默认实现不会返回它在object.__dict__[attributename]中找到的内容;它还将通过调用descriptor.__get__() method绑定描述符。这是Python非常重要的一个方面,正是这种机制使得方法和属性以及其他东西的负载都有效。

classmethod个对象,在由描述符协议绑定时,返回一个方法对象。方法对象是记录绑定到的对象的包装器,以及调用它们时调用的函数;调用方法确实使用绑定对象作为第一个参数调用底层方法:

>>> class Foo:
...     pass
...
>>> def bar(*args): print(args)
...
>>> classmethod(bar).__get__(None, Foo)  # decorate with classmethod and bind
<bound method bar of <class '__main__.Foo'>>
>>> method = classmethod(bar).__get__(None, Foo)
>>> method.__self__
<class '__main__.Foo'>
>>> method.__func__
<function bar at 0x1056f0e18>
>>> method()
(<class '__main__.Foo'>,)
>>> method('additional arguments')
(<class '__main__.Foo'>, 'additional arguments')

因此,为classmethod对象返回的方法对象引用了类(__get__的第二个参数,所有者)和原始函数。如果在实例上使用类方法,则仍会忽略第一个参数:

>>> classmethod(bar).__get__(Foo(), Foo).__self__  # called on an instance
<class '__main__.Foo'>

另一方面,函数想要将绑定到实例;因此,如果__get__的第一个参数设置为None,则只返回self

>>> bar.__get__(None, Foo)    # access on a class
<function bar at 0x1056f0e18>
>>> bar.__get__(Foo(), Foo)   # access on an instance
<bound method bar of <__main__.Foo object at 0x105833a90>>
>>> bar.__get__(Foo(), Foo).__self__
<__main__.Foo object at 0x105833160>

如果访问ClassObject.classmethod_object会返回classmethod对象本身,就像function对象那样,那么你就永远无法在类上使用类方法。这是毫无意义的。

所以不,object.attribute 并不总是object.__dict__['attribute']相同。如果object.__dict__['attribute']支持描述符协议,则可以调用它。