Python\u call\uuu()这是一个隐式类方法吗?

2024-09-29 17:13:28 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用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或{}声明。在

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

如果我传递self,或者声明为@staticmethod或{},则代码不起作用。在

有人能解释一下__call__()方法背后的语法逻辑吗。在


Tags: instances方法self声明模式argspasscall
2条回答

马丁的回答说明了一切。我加上这一点,也许不同的措辞可以给不同的人带来更多的启示:

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

不是。但是所有的“元类”方法都是使用该元类的类的隐式“类方法”。在

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

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

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

(现在,仍然是关于这个问题的主题:__new__是一个特例吗 对于一个隐式静态方法(即使对于不打算成为元类的普通类也是如此),Python特别向其添加第一个参数,使用的机制与对正则的classmethods所做的不同。这就是为什么上面的super().__new__调用需要将metals作为第一个参数包含在内)

将方法的第一个参数命名为clsself只是一个约定。__call__方法没有自变量,只是在这里它的名称是cls。这是因为对于元类,方法是绑定到类对象的,其名称反映了这一点。在

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

但你可以随意给第一个论点起其他名字。它不是使类方法、常规方法或元类型上的方法工作的名称。使用selfcls所做的就是记录这是什么类型的对象,这样其他开发人员就更容易在心里跟踪正在发生的事情。在

所以不,这不是一个隐式类方法。第一个参数没有绑定到Singleton元类对象,而是绑定到被调用的类。这是有意义的,因为类对象是Singleton元类型的一个实例。在

如果您想深入研究绑定的工作原理(不管名称是什么,都可以传入第一个参数的过程),您可以阅读Descriptor HOWTO。TLDR:functions、propertyclassmethodstaticmethod对象都是描述符,每当您将它们作为支持对象(如实例或类)的属性访问时,它们都是绑定的,通常会导致返回一个不同的对象,当调用时,这些对象会将绑定对象传递给实际函数。在

相关问题 更多 >

    热门问题