zoukankan      html  css  js  c++  java
  • metaclass,__call__,__new__,__init__

    重点分清楚对象是由A类实例化的,A类是由B类实例化的,

    调用B类的__call__(self,*args,**kwargs),self=A

    __call__()函数中执行

    A_obj=self.__new__(self),

    self.__init__(A_obj,*args,**kwargs)

    return A_obj

    
    

    示例一: 

    示例一:
    class Mymeta(type):
        def __init__(self,name,bases,dic):
            print('===>Mymeta.__init__')
    
    
        def __new__(cls, *args, **kwargs):
            print('===>Mymeta.__new__')
            return type.__new__(cls,*args,**kwargs)
    
        def __call__(self, *args, **kwargs):          #若Foo('eagon'),self 为Foo,*args='egon'
            print('aaa')
            obj=self.__new__(self)
            self.__init__(self,*args,**kwargs)
            return obj
    
    class Foo(object,metaclass=Mymeta):
        def __init__(self,name):
            self.name=name
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    
    '''
    需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__  
    而爹.__call__一般做两件事:
    1.调用name.__new__方法并返回一个对象
    2.进而调用name.__init__方法对儿子name进行初始化
    '''
    
    '''
    class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行
    Foo=Mymeta('foo',(...),{...})
    因此我们可以看到,只定义class就会有如下执行效果
    ===>Mymeta.__new__
    ===>Mymeta.__init__
    实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作,
    遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法
    于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化
    '''
    
    '''
    obj=Foo('egon')
    的原理同上
    '''
    
    '''
    总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了
    1.谁后面跟括号,就从谁的爹中找__call__方法执行
    type->Mymeta->Foo->obj
    Mymeta()触发type.__call__
    Foo()触发Mymeta.__call__
    obj()触发Foo.__call__
    2.__call__内按先后顺序依次调用儿子的__new__和__init__方法
    '''
    View Code

    示例二:

    '''
    class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
        pass
    
    class Teacher(object): #Teacher=Mymeta('Teacher',(object,),{...})
        school = 'Qinghua'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def score(self):
            print('%s is scoring' %self.name)
    
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    tea1=Teacher('egon',18,'male')
    
    tea1(1,2,a=1,b=2) #__call__(tea1,(1,2).{'a':1,'b':2})
    '''
    
    总结:对象之所以可以调用,是因为对象的类中有一个函数__call__
    
    推导:如果一切皆对象,那么Teacher也是一个对象,该对象之所可以调用,肯定是这个对象的类中也定义了一个函数__call__
    
    class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
        def __call__(self, *args, **kwargs): #self=Teacher这个类,args=('egon',18,'male'),kwargs={}
            # 1. 先产生一个空对象
            tea_obj=self.__new__(self) #tea_obj是Teacher这个类的对象   self--->Teacher
            # 2. 执行__init__方法,完成对象的初始属性操作
            self.__init__(tea_obj,*args,**kwargs)
            # 3. 返回初始化好的那个对象
            return tea_obj
    
    class Teacher(object,metaclass=Mymeta): #Teacher=Mymeta('Teacher',(object,),{...})
        school = 'Qinghua'
        
        #tea_obj,'egon',18,'male'
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def score(self):
            print('%s is scoring' %self.name)
    
    tea1=Teacher('egon',18,'male') # 会触发Teacher的类(即元类)中的__call__函数
    print(tea1)
    print(tea1.__dict__)
    View Code
  • 相关阅读:
    【转】多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)
    Mono初接触
    计算机颜色格式( 8位 16位 24位 32位色)
    我爱源代码
    Linux小白教程: tar的几种常用格式
    Linux小白教程:查看当前Linux的发行版本、内核(kernel)版本
    10大糟糕预测:
    一日编程小悟
    Linux小白教程:vi(shell文本编辑器)保存、退出命令
    C结构体中的函数指针与函数
  • 原文地址:https://www.cnblogs.com/wddxx/p/13663975.html
Copyright © 2011-2022 走看看