对于classmethods / staticmethods,为什么cls .__ dict __ [meth]与getattr(cls,meth)不同?

时间:2017-06-16 18:42:30

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

我以前从未见过这样的事情。

还有其他事情吗?

>>> class NothingSpecial:
     @classmethod
     def meth(cls): pass

>>> NothingSpecial.meth
<bound method classobj.meth of <class __main__.NothingSpecial at 0x02C68C70>>
>>> NothingSpecial.__dict__['meth']
<classmethod object at 0x03F15FD0>

>>> getattr(NothingSpecial, 'meth')
<bound method NothingSpecial.meth of <class '__main__.NothingSpecial'>>

>>> object.__getattribute__(NothingSpecial, 'meth')
<classmethod object at 0x03FAFE90>

>>> type.__getattribute__(NothingSpecial, 'meth')
<bound method NothingSpecial.meth of <class '__main__.NothingSpecial'>>

3 个答案:

答案 0 :(得分:1)

Getattr使用描述符逻辑

主要区别在于字典查找没有额外处理,而属性提取包含额外的逻辑(有关所有细节,请参阅我的Descriptor How-To Guide)。

有两种不同的基础方法

1)调用NothingSpecial.__dict__['meth']使用方括号运算符调度到dict.__getitem__执行简单的哈希表查找,或者如果找不到则引发 KeyError

2)调用NothingSpecial.meth使用调度到type.__getattribute__的点运算符,它执行简单查找,然后是descriptors的特殊情况。如果查找失败,则会引发 AttributeError

如何运作

记录整体逻辑herehere

  

通常,描述符是具有“绑定”的对象属性   行为“,其属性访问权已被方法覆盖   在描述符协议中:__ get __(),__ set __()和/或__delete __()。如果   任何这些方法都是为一个对象定义的,据说是一个   描述符。

     

属性访问的默认行为是获取,设置或删除   来自对象字典的属性。例如,a.x有一个   查找链以.__ dict __ ['x']开头,然后   键入(a).__ dict __ ['x'],并继续通过基类   (a)不包括元类。

     

但是,如果查找的值是定义其中一个的对象   描述符方法,然后Python可以覆盖默认行为和   改为调用描述符方法。这种情况发生在哪里   优先级链取决于定义的描述符方法和   他们是如何被称为

希望您发现所有这些都有帮助。您正在进行的探索是了解Python的好方法: - )

P.S。您可能也喜欢阅读Python 2.2 entry for descriptors中的原始Whatsnew,或者查看Guido van Rossum最初提出这个想法的PEP 252

答案 1 :(得分:0)

object.__getattribute__(NothingSpecial, 'meth')

NothingSpecial.__dict__['meth'] 
在这种情况下,

返回相同的对象。您可以通过执行以下操作快速检查:

NothingSpecial.__dict__['meth'] is object.__getattribute__(NothingSpecial, 'meth')

$True

它们都指向相同的descriptor object

另一方面:

object.__getattribute__(NothingSpecial, 'meth') is getattr(NothingSpecial, 'meth')

$False

基本上,他们不是同一个对象和同一类型的对象:

type(object.__getattribute__(NothingSpecial, 'meth'))
$<class 'classmethod'>

type(getattr(NothingSpecial, 'meth'))
$<class 'method'>

答案 2 :(得分:0)

所以答案是getattr会自动调用对象的__get__方法(如果有),而object.__getattribute__和对象__dict__查找不会。以下功能证明:

class Nothing:

    @classmethod
    def a(cls): 
        return cls()

    @staticmethod
    def b(): 
        return 'b'

    def c(self): 
        return 'c'

def gitter(obj, name):
    value = object.__getattribute__(obj, name)
    if hasattr(value, '__get__'):
        if isclass(obj):
            instance, cls = None, obj
        else:
            instance, cls = obj, type(obj)
        return value.__get__(instance, cls)
    return value

>>> gitter(Nothing, 'a')()
<__main__.Nothing object at 0x03E97930>
>>> gitter(Nothing, 'b')()
'b'
>>> gitter(Nothing(), 'c')()
'c'

但是,gitter(Nothing(), 'b')目前无法正常工作,因为它未检测到objtype默认值为None,但这已足够。