类型类的__call__方法

5

在我所了解的Python面向对象编程中,如果一个类定义了__call__方法,则当我们像调用函数一样使用该类的实例时,该方法将被调用。例如:

class Employee:
    def __init__(self,name,sal):
        self.name = name
        self.salary = sal
    def __call__(self,value):
        return self.salary * value

e = Employee("Subhayan",20000)
print (e(10))

__call__方法以对象实例作为第一个参数。

我只是在尝试理解Python中的元类,并且我读到type是所有用户定义类的默认元类。

如果我在Python中定义一个基本的自定义元类:

class Meta(type):
    def __new__(meta, classname, supers, classdict):
        # Run by inherited type.__call__
        return type.__new__(meta, classname, supers, classdict)

根据书中提供的文档,元类 __new__ 方法会被 type 类从 __call__ 方法中调用。

现在我的问题是,要使用任何类的 __call__ 方法,我们必须拥有该类的对象并将其作为函数调用。

在这里,我们没有 type 类的任何对象来使用它的 __call__ 方法。

有人可以解释一下 type 类的 __call__ 方法是如何被调用的吗?

2个回答

6
任何类本身都是类型“type”的实例 - 因此“调用”类就相当于在其类上调用方法__call__,而该类恰好是类型的__call__type.__call__的效果确切地是: 对于像下面这样的代码:
class A:
    pass
b = A()
  1. type.__call__ 接收类 A 本身作为它的第一个参数。
  2. 它调用 A.__new__ - 我们可以把 instace = A.__new__(cls) 想象成伪代码来理解。
  3. 这将返回一个 "A" 类的实例
  4. 然后它在实例上调用 __init__ (instance.__init__())
  5. 然后返回该实例 return instance

然而,如果类本身派生自 "type" - 即,它是一个 "new" 类的实例,则会采取额外的步骤:任何一个类方法中使用 super 调用的魔法变量 __class__ 的特殊值都会被填充。并且在 Python 3.6 中,会执行这两个进一步的步骤:调用任何定义了 __set_name__ 方法的类属性,并调用类的 __init_subclass__ 方法。


我将打开一个新的问题,因为我仍然不清楚当类本身从类型派生时会发生什么。我会在那里粘贴新代码,请帮我解决这个问题。 - Subhayan Bhattacharya
直到创建一个类,它才成为类型的实例。因此,在上面的代码中调用A()时,我可以理解调用类型的__call__方法。但是,应该在运行类语句时立即调用__new__。但正如您所提到的,__new__是从__call__中调用的。不确定我是否理解正确。 - Subhayan Bhattacharya

2

请记住,type 是元类,因此它的实例是类,而不是对象。这意味着(除其他外),当您这样做:

e= Employee(*args,**kwargs)

你正在调用Employee.__init__和一些来自你的元类的方法,包括Meta.__call__,就像你执行.时一样。
print (e(10))

你正在调用自定义的__call__方法。

Singleton 元类为例:

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 MyClass(BaseClass):
    __metaclass__ = Singleton

现在每次创建一个新的MyClass实例时,元类中的__call__方法会在实际实例被创建之前被调用,从而允许您检查先前是否存在实例并返回该实例。希望这可以帮助您。

这里有一个问题。在 Singleton 的 call 函数内,cls 参数将具有 MyClass 的值。而 _instances 属于 Singleton 类。那么 MyClass 如何拥有 _instances 字典呢? - Subhayan Bhattacharya
因为MyClassSingleton的一个实例。 - yorodm
Subhayan:……并且所有的 “Singleton” 实例都共享相同的 _instances 字典,因为它是一个类,而不是一个实例属性。 - martineau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接