超级元类

时间:2017-12-08 11:22:03

标签: python metaclass

最近,我使用元类来实现单例。然后,我试着理解这个__metaclass__的东西是如何工作的。为此,我写了这段代码:

import numpy as np

class Meta(type):
    _instance = None
    def __init__(cls, *args, **kwargs):
        print('meta: init')
        #return super(Meta, cls).__init__(*args, **kwargs)

    def __new__(cls, *args, **kwargs):
        print('meta: new')
        return super(Meta, cls).__new__(cls, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('meta: call')
        if cls._instance is None:
            cls._instance = super(Meta, cls).__call__(*args, **kwargs)
        print(cls._instance.__class__)
        return cls._instance



class ClassA():

    __metaclass__ = Meta
    def __init__(self, *args, **kwargs):
        self.val = np.random.randint(1000)
        print('classA: init')


    def __new__(cls, *args, **kwargs):
        print('classA: new')
        return super(ClassA, cls).__new__(cls, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('classA: call')
        return super(ClassA, cls).__call__(*args, **kwargs)

class ClassB():

    __metaclass__ = Meta
    def __init__(self, *args, **kwargs):
        print('classB: init')
        self.val = np.random.randint(1000)

    def __new__(cls, *args, **kwargs):
        print('classB: new')
        return super(ClassB, cls).__new__(cls, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('classB: call')
        return super(ClassB, cls).__call__(*args, **kwargs)


class ClassC(ClassB):

    def __init__(self, *args, **kwargs):
        print('classC: init')

        super(ClassC, self).__init__(self, *args, **kwargs)
        self.test = 3

    def __new__(cls, *args, **kwargs):
        print('classC: new')
        return super(ClassC, cls).__new__(cls, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('classC: call')
        return super(ClassC, cls).__call__(*args, **kwargs)



if __name__ == '__main__':


    a1 = ClassA()
    b1 = ClassB()
    a2 = ClassA()
    b2 = ClassB()
    c1 = ClassC()

    print(a1.val)
    print(b1.val)
    print(a2.val)
    print(b2.val)

我有两个问题:

  • 为什么cls._instance__main__.ClassA object(或__main__.ClassA object)?它是不是父实例,因为它是使用super创建的?

  • 创建__call__实例时只调用__init__ ClassC内的ClassCClassB.__call__},但仅调用<asp:Label runat="server" ID="lbl_OrderDate_DateTime_M" Text="Order Date:"> </asp:Label> (因为它继承自它)?

1 个答案:

答案 0 :(得分:1)

首先,这里是您的代码的简化版本,其中包含一些&#34;噪音&#34;为了便于阅读而删除。

import random

class Meta(type):
    _instance = None

    def __call__(cls, *args, **kwargs):
        print('meta: call: %s' % cls)
        if cls._instance is None:
            cls._instance = super(Meta, cls).__call__(*args, **kwargs)
        print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
        return cls._instance


class ClassA(object):
    __metaclass__ = Meta

    def __new__(cls, *args, **kwargs):
        print('classA: new')
        return super(ClassA, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        self.val = random.randint(1, 1000)
        print('classA: init')


class ClassB(object):
    __metaclass__ = Meta

    def __new__(cls, *args, **kwargs):
        print('classB: new')
        return super(ClassB, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('classB: init')
        self.val = random.randint(1, 1000)

class ClassC(ClassB):
    def __new__(cls, *args, **kwargs):
        print('classC: new')
        return super(ClassC, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('classC: init')
        super(ClassC, self).__init__(self, *args, **kwargs)
        self.test = 3


def main():
    a1 = ClassA()
    b1 = ClassB()
    a2 = ClassA()
    b2 = ClassB()
    c1 = ClassC()

    print(a1.val)
    print(b1.val)
    print(a2.val)
    print(b2.val)

if __name__ == '__main__':
    main()

现在你的问题:

  

为什么cls._instance是 main .ClassA对象(或主要 .ClassA对象)?它应该不是父实例,因为它是用super创建的吗?

不,为什么?在您的情况下,在调用ClassA()时,您实际上会调用Meta.__call__(ClassA)super(Meta, cls)将选择Meta.__mro__中的下一个类type,然后调用type.__call__(Meta, ClassA)type.__call__(Meta, ClassA)将依次调用ClassA.__new__()ClassA.__init__()。当然(希望,否则你几乎不能使用继承),你会得到一个ClassA实例。

作为一般规则:super(CurrentClass, obj_or_cls).something()将在&#34; parent&#34;中选择something实施。 class(实际上在__mro__中的下一个类),但self(或cls或其他)第一个参数仍将指向self(或cls等等)。 IOW,您正在选择方法的父实现,但此方法仍然将当前对象/类作为第一个参数。

  

为什么在创建ClassC实例时只调用ClassC中的任何内容(__call____init__),而只调用ClassB.__call__(因为它继承了它)

ClassC.__call__()未被调用,因为代码中没有任何内容调用它。 ClassC.__metaclass__.__call__()负责ClassC实例化的ClassC。您需要调用ClassC.__call__()实例才能调用ClassC.__init__()

ClassC未被调用,因为实际上没有创建ClassB的实例。这里的原因是您首先创建ClassB的实例。此时(ClassB的第一次实例化),_instance没有属性object,因此在其父类object上查找该属性。由于_instance也没有属性ClassB.__class__,因此查询将继续Meta,即_instance。这个属性的None属性为ClassB,因此您创建了一个ClassB._instance实例并将其绑定到ClassB.__dict__(在ClassC中创建属性})。

然后,当您尝试实例化if cls._instance is None时,_instance测试将首先在ClassC上查找ClassC。此时,_instance 具有ClassC属性,因此它会在ClassB第一个父级(mro中的下一个类)上查找,ClassB。由于您已经实例化了ClassC._instance一次,因此查找在此处结束,将ClassB.__dict__["_instance"]解析为ClassB,因此您获取的内容已创建的Meta._instance实例

如果你想要一个有效的实现,你必须摆脱_instance并在None中的每个班级上设置Meta.__init__()class Meta(type): def __init__(cls, *args, **kw): cls._instance = None def __call__(cls, *args, **kwargs): print('meta: call: %s' % cls) if cls._instance is None: cls._instance = super(Meta, cls).__call__(*args, **kwargs) print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance))) return cls._instance

Meta.__call__()

或使用getattr()替换class Meta(type): def __call__(cls, *args, **kwargs): print('meta: call: %s' % cls) if not getattr(cls, "_instance", None): cls._instance = super(Meta, cls).__call__(*args, **kwargs) print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance))) return cls._instance 中的测试:

class Person:
def __init__(self, name, address):
    self.name = name
    self.address = address

def say_name(self):
    print('Hello, my name is', self.name)
def say_address(self):
    print('Hello, my name address', self.address)

p = Person('Swaroop')
q = Person('Duisburg')

p.say_name()
q.say_address()