Python __call __()这是一个隐式的类方法吗?

时间:2017-06-22 16:05:45

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

我想在python中实现单例模式,我喜欢http://www.python-course.eu/python3_metaclasses.php中描述的模式。

<div class="my-wrap">
  <div class="wrap-left">
    <div class="wrap-one">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum voluptates ipsa tempora qui voluptas, dicta corrupti dolorum, iure esse earum ut pariatur, ad possimus facilis consequatur impedit accusantium autem! Nesciunt?</p>
    </div>
    <div class="wrap-two">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat commodi aspernatur et! Voluptates officiis nemo corporis delectus pariatur. Cupiditate perspiciatis illum minima, porro voluptas velit nobis ad eveniet modi explicabo.</p>
    </div>
  </div>
  <div class="wrap-right">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perspiciatis reprehenderit, necessitatibus vel praesentium dolorum vitae sequi in magni voluptate alias fugit saepe eos sint dolore quae sapiente sunt itaque, cupiditate.</p>
  </div>
</div>

代码完美无缺。但是,class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class SingletonClass(metaclass=Singleton): pass class RegularClass(): pass x = SingletonClass() y = SingletonClass() print(x == y) x = RegularClass() y = RegularClass() print(x == y) 没有__call__(),也没有self@classmethod声明。

但是,在Python数据模型https://docs.python.org/3/reference/datamodel.html#object.__call__中,@staticmethod方法在参数中有一个self。

如果我通过__call__()或声明为self@staticmethod,则代码无效。

有人可以解释@classmethod方法背后语法的逻辑。

2 个答案:

答案 0 :(得分:7)

命名方法clsself的第一个参数只是一个约定__call__方法 参数,只有名为 cls。那是因为对于元类,该方法绑定到一个类对象,名称反映了这一点。

同样的约定适用于@classmethod方法;由于classmethod对象的绑定方式的性质,第一个参数总是一个类,因此将第一个参数命名为cls是有意义的。

但你可以自由地将第一个参数命名为其他任何东西。它不是使类方法或常规方法或方法在元类型上工作的名称。使用selfcls所做的就是记录这是什么类型的对象,使其他开发人员更容易在心理上跟踪正在发生的事情。

所以不,这不是一个隐式的类方法。第一个参数不绑定到Singleton元类对象,它绑定到被调用的类。这是有道理的,因为该类对象是Singleton元类型的实例。

如果您想深入了解绑定的工作原理(导致传递第一个参数的过程,无论名称如何),您可以阅读Descriptor HOWTO。 TLDR:函数,propertyclassmethodstaticmethod对象都是描述符,并且每当您将它们作为支持对象(如实例或属性)上的属性进行访问时一个类,它们被绑定,经常导致一个不同的对象作为结果被返回,当被调用时,它将绑定对象传递给实际的函数。

答案 1 :(得分:0)

马丁的回答说明了一切。我添加这个,所以也许不同的措辞可以为不同的人提供更多的亮点:

Python 调用()这是一个隐式的类方法吗?

没有。但所有&#34;元类&#34;方法是隐含的&#34;类方法&#34;对于使用该元类的类。

当我们考虑到类只是元类的实例时,这是隐含的。从语言的角度来看,类的行为几乎与任何其他实例完全相同 - 以及与类中的任何交互都会触发对象中的dunder(__magic__)方法 - 比如使用&#34; +, - , *,/&#34;运算符或使用[ ]进行索引检索,或使用( )调用它们会触发其类的相应方法。那通常是type

而且,正如另一个答案中所说,Python并不关心你在方法的第一个参数上使用什么名称。对于元类,在那里使用cls是有意义的,因为&#34;实例&#34;处理的方法是元类。因为对于元类的第一个参数是有意义的。 __new__方法名为metacls(如下例所示)。因为新的&#34; cls&#34;是调用type.__new__后得到的对象 - 纯Python中唯一可能实际创建类对象的调用。

class Meta(type):
    def __new__(metacls, name, bases, namespace):
         ...
         cls = super().__new__(metacls, name, bases, namespace)
         ...
         return cls

(现在,仍然是关于问题的主题:__new__ 是一个特例 一个隐式静态方法(即使对于不是元类的普通类) - Python专门添加第一个参数,使用与常规classmethod不同的机制。这就是为什么上面的super().__new__调用需要包含元数据作为第一个参数)