【问题标题】:Why is cls.__dict__[meth] different than getattr(cls, meth) for classmethods/staticmethods?对于类方法/静态方法,为什么 cls.__dict__[meth] 与 getattr(cls, meth) 不同?
【发布时间】:2017-06-16 18:42:29
【问题描述】:

我以前从未见过其他任何东西像这样工作。

还有什么可以做到这一点的吗?

>>> 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'>>

【问题讨论】:

  • 因为类方法是描述符。阅读描述符。
  • 所以我看到位于描述符 how-to 上的类方法的纯 python 实现做同样的事情。所以我想这意味着如果type.__getattribute__ 得到一个描述符,它正在做一些特别的事情。但我仍然不知道如何模仿这种行为。
  • type 是一个元类,所有类的默认元类。它可能正在做一些特别的事情。
  • @bup:如果你想模仿它,你应该问一个单独的问题(也许解释你为什么要这样做)。

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


【解决方案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 有一个 以 a.__dict__['x'] 开头的查找链,然后 type(a).__dict__['x'],并继续遍历基类 type(a) 不包括元类。

但是,如果查找的值是一个对象,它定义了 描述符方法,那么 Python 可能会覆盖默认行为和 而是调用描述符方法。这种情况发生在 优先链取决于定义了哪些描述符方法以及 他们是怎么称呼的

希望所有这些对您有所帮助。你正在做的那种探索是了解 Python 的好方法 :-)

附:您可能还喜欢阅读 Python 2.2 中的原始 Whatsnew entry for descriptors 或查看 Guido van Rossum 最初提出该想法的 PEP 252

【讨论】:

    【解决方案2】:
    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'>
    

    【讨论】:

      【解决方案3】:

      所以答案是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,但这已经足够了。

      【讨论】:

      • object.__getattribute__ 将执行描述符处理,但描述符处理将是常规对象描述符处理,而不是类对象描述符处理;对于常规对象,对象__dict__ 中的描述符不会调用描述符方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-05
      • 2019-11-12
      • 1970-01-01
      • 2012-11-12
      • 2010-09-05
      相关资源
      最近更新 更多