Python __call__() 这是隐式类方法吗?
Python __call__() is this an implicit classmethod?
我想在 python 中实现单例模式,我喜欢 http://www.python-course.eu/python3_metaclasses.php 中描述的模式。
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
或 @staticmethod
声明。
但是,在 Python 数据模型 https://docs.python.org/3/reference/datamodel.html#object.__call__ 中,__call__()
方法的参数中有一个 self。
如果我通过 self
或声明为 @staticmethod
或 @classmethod
,代码将不起作用。
谁能解释一下 __call__()
方法背后的语法逻辑。
将方法的第一个参数命名为 cls
或 self
只是一种约定。 __call__
方法确实有一个self参数,只是这里是named cls
。那是因为对于 metaclass,该方法绑定到一个 class 对象,名称反映了这一点。
相同的约定适用于 @classmethod
方法;第一个参数总是 class,由于 classmethod
对象的绑定方式的性质,因此将第一个参数命名为 cls
.
是有意义的
但是您可以随意为第一个参数命名。使 class 方法或常规方法或元类型上的方法起作用的不是名称。使用 self
或 cls
所做的只是 记录这是什么类型的对象,使其他开发人员更容易在脑海中跟踪正在发生的事情。
所以不,这不是隐式的 class 方法。第一个参数没有绑定到 Singleton
metaclass 对象,它绑定到被调用的 class 对象。这是有道理的,因为 class 对象是 Singleton
元类型的一个实例。
如果您想深入了解 binding 的工作原理(导致传入第一个参数的过程,无论名称如何),您可以阅读 Descriptor HOWTO。 TLDR:函数、property
、classmethod
和 staticmethod
对象都是 描述符 ,并且每当您将它们作为支持对象的属性访问时,例如一个实例或一个 class,它们是绑定的,通常会导致 不同的 对象作为结果返回,在调用时将绑定对象传递给实际函数。
马丁的回答说明了一切。我正在添加这个,所以也许不同的措辞可以为不同的人带来更多的启发:
Python 调用() 这是一个隐含的class方法吗?
没有。但是所有 "metaclass" 方法对于使用该元 class 的 class 都是隐式的 "class methods"。
当我们考虑到 classes 只是元 class 的实例时,这是隐含的。从语言的角度来看,class 的行为几乎与任何其他实例完全一样 - 与 class 的任何交互都会触发对象中的 dunder (__magic__
) 方法 - 比如使用“+、-、*、/”运算符,或用[ ]
检索索引,或用( )
调用它们,触发其class上的相应方法。那通常是type
。
并且,正如在另一个答案中所说的那样,Python 不关心您在方法的第一个参数上使用的名称。对于 metaclasses,在那里使用 cls
是有意义的,因为 "instances" 方法处理的是 metaclasses。因为将 metaclass' __new__
方法的第一个参数命名为 metacls
是有道理的(就像下面的例子一样)。因为新的 "cls" 是我们在调用 type.__new__
之后得到的对象——纯 Python 中唯一可能的调用实际上会创建一个 class 对象。
class Meta(type):
def __new__(metacls, name, bases, namespace):
...
cls = super().__new__(metacls, name, bases, namespace)
...
return cls
(现在,还是正题:__new__
是的特例
隐式静态方法的(即使是普通的 classes 不打算成为 metaclasses) - Python 专门添加第一个参数,使用与它不同的机制完成常规 classmethod
s。这就是为什么上面的 super().__new__
调用需要将 metacls 作为第一个参数)
我想在 python 中实现单例模式,我喜欢 http://www.python-course.eu/python3_metaclasses.php 中描述的模式。
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
或 @staticmethod
声明。
但是,在 Python 数据模型 https://docs.python.org/3/reference/datamodel.html#object.__call__ 中,__call__()
方法的参数中有一个 self。
如果我通过 self
或声明为 @staticmethod
或 @classmethod
,代码将不起作用。
谁能解释一下 __call__()
方法背后的语法逻辑。
将方法的第一个参数命名为 cls
或 self
只是一种约定。 __call__
方法确实有一个self参数,只是这里是named cls
。那是因为对于 metaclass,该方法绑定到一个 class 对象,名称反映了这一点。
相同的约定适用于 @classmethod
方法;第一个参数总是 class,由于 classmethod
对象的绑定方式的性质,因此将第一个参数命名为 cls
.
但是您可以随意为第一个参数命名。使 class 方法或常规方法或元类型上的方法起作用的不是名称。使用 self
或 cls
所做的只是 记录这是什么类型的对象,使其他开发人员更容易在脑海中跟踪正在发生的事情。
所以不,这不是隐式的 class 方法。第一个参数没有绑定到 Singleton
metaclass 对象,它绑定到被调用的 class 对象。这是有道理的,因为 class 对象是 Singleton
元类型的一个实例。
如果您想深入了解 binding 的工作原理(导致传入第一个参数的过程,无论名称如何),您可以阅读 Descriptor HOWTO。 TLDR:函数、property
、classmethod
和 staticmethod
对象都是 描述符 ,并且每当您将它们作为支持对象的属性访问时,例如一个实例或一个 class,它们是绑定的,通常会导致 不同的 对象作为结果返回,在调用时将绑定对象传递给实际函数。
马丁的回答说明了一切。我正在添加这个,所以也许不同的措辞可以为不同的人带来更多的启发:
Python 调用() 这是一个隐含的class方法吗?
没有。但是所有 "metaclass" 方法对于使用该元 class 的 class 都是隐式的 "class methods"。
当我们考虑到 classes 只是元 class 的实例时,这是隐含的。从语言的角度来看,class 的行为几乎与任何其他实例完全一样 - 与 class 的任何交互都会触发对象中的 dunder (__magic__
) 方法 - 比如使用“+、-、*、/”运算符,或用[ ]
检索索引,或用( )
调用它们,触发其class上的相应方法。那通常是type
。
并且,正如在另一个答案中所说的那样,Python 不关心您在方法的第一个参数上使用的名称。对于 metaclasses,在那里使用 cls
是有意义的,因为 "instances" 方法处理的是 metaclasses。因为将 metaclass' __new__
方法的第一个参数命名为 metacls
是有道理的(就像下面的例子一样)。因为新的 "cls" 是我们在调用 type.__new__
之后得到的对象——纯 Python 中唯一可能的调用实际上会创建一个 class 对象。
class Meta(type):
def __new__(metacls, name, bases, namespace):
...
cls = super().__new__(metacls, name, bases, namespace)
...
return cls
(现在,还是正题:__new__
是的特例
隐式静态方法的(即使是普通的 classes 不打算成为 metaclasses) - Python 专门添加第一个参数,使用与它不同的机制完成常规 classmethod
s。这就是为什么上面的 super().__new__
调用需要将 metacls 作为第一个参数)