【问题标题】:Python __call__() is this an implicit classmethod?Python __call__() 这是一个隐式的类方法吗?
【发布时间】:2017-11-26 00:45:59
【问题描述】:

我想在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(函数中的所有位置,而不仅仅是参数),代码将继续工作。见鬼,把它重命名为gabbledygook 就可以了。

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


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    没有。但是对于使用该元类的类,所有“元类”方法都是隐含的“类方法”。

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

    而且,正如在另一个答案中所说,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__ 调用需要包含 metacls 作为第一个参数)

    【讨论】:

      猜你喜欢
      • 2023-01-12
      • 2017-10-25
      • 2012-07-20
      • 1970-01-01
      • 1970-01-01
      • 2012-09-02
      • 1970-01-01
      • 2011-10-21
      • 1970-01-01
      相关资源
      最近更新 更多