【问题标题】:__call__ method of type class__call__ 类型类的方法
【发布时间】:2017-10-25 23:10:34
【问题描述】:

根据我对 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__ 方法将由继承自类型的__call__ 方法运行。

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

这里我们没有任何类型的对象可以使用其__call__ 函数。

有人可以向我解释一下类型类的__call__ 函数是如何出现的吗?

【问题讨论】:

    标签: python metaclass


    【解决方案1】:

    任何类本身都是“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

    但是,如果类本身是从“类型”派生的——即,它是“新”类的实例化,则需要采取额外的步骤:魔术__class__ 变量的特殊值填入任何类方法 - 如果这些方法中的任何一个使用对 super 的调用。在 Python 3.6 上,这两个进一步的步骤:调用任何定义 __set_name__ 方法的类属性,并调用该类的 __init_subclass__ 方法。

    【讨论】:

    • 我将提出一个新问题,因为我仍然不清楚该类本身何时派生自类型。我将在那里粘贴一个新代码,所以也请帮助我。
    • 另外,在创建类之前,它还不是类型的实例。因此,当在上面的代码中调用 A() 时,我可以理解被调用的类型的 call 方法。但是 new 应该在 class 语句运行后立即被调用。但正如您提到的,new 是从 call 调用的。不知道我是否理解正确。
    【解决方案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字典呢?
    • 因为MyClassSingleton 的一个实例。
    • Subhayan: ...和所有Singleton 实例共享同一个_instances 字典,因为它是一个类,不是一个实例、属性。
    猜你喜欢
    • 2012-09-02
    • 2011-12-10
    • 2015-08-31
    • 2012-07-04
    • 1970-01-01
    • 2023-01-12
    • 2011-10-21
    • 2017-11-26
    • 1970-01-01
    相关资源
    最近更新 更多