zoukankan      html  css  js  c++  java
  • Python的元类

    一、元类的介绍

    1、引入:一切都源自一句话:一切皆对象

    class People:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say(self):
            print('%s:%s'%(self.name,self.age))
    
    # 如何得到对象?  答:变量名obj=调用类()
    obj = People('lsj',18)
      类也是对象,变量 = 调用类(),其实是在使用python关键字class定义类的时候,底层代码帮我们实现了这个功能
      People = 调用类(....)
    2、什么是元类:
      把类本身当作对象,这种类叫元类:就是用来实例化产生类的类

      关系:元类--->实例化--->类(People)--->实例化-->对象(obj)
    # 如何查看内置的元类:
    # 1、type是内置的元类
    # 2、我们用class关键字定义的所有的类以及内置的类都是由元类type()实例化产生的,所以我们自定义的所有的类祖师爷都是type
    print(type(obj))    # <class '__main__.People'>
    print(type(People)) # <class 'type'>
    # 查看一下自定义类型和内置类型都是type类
    print(type(int))  # <class 'type'>
    print(type(People)) # <class 'type'>

     二、class机制

    # 分析:type调用了元类People= type(...)
    
    # class People():
    #     def __init__(self,name,age):
    #         self.name = name
    #         self.age = age
    #     def say(self):
    #         print('%s:%s'%(self.name,self.age))
    # 查看名称空间
    print(People.__dict__)
    """
    {'__module__': '__main__', '__init__': <function People.__init__ at 0x00000241001214C0>, 'say': <function People.say at 0x0000024100121550>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
    """
    # 刨析class机制创造类的工作原理有四步。
    # 类有三大特征:
    # 1、类名
    class_name = 'People'
    # 2、类的基类(class People():),没有继承类就放一个object类,以元组形式保存
    class_bases = (object,)
    # 3、类体,类体本身就是字符串,但是执行类体代码拿到的最终的结果是类的名称空间
    class_body = """
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say(self):
        print('%s:%s'%(self.name,self.age))
    """
    class_dic = {}
    exec(class_body,{},class_dic) # exec()的功能是:将class_body代码运行,将产生的名字放到class_dic里,{}表示全局名称空间
    print(class_dic)  # {'__init__': <function __init__ at 0x0000016A0FE01430>, 'say': <function say at 0x0000016A0FE014C0>}
    # 4、调用元类:class,使用type(类名,类的基类,类的名称空间)
    print(type(class_name,class_bases,class_dic))  # <class '__main__.People'>
    x = type(class_name,class_bases,class_dic)
    print(x)  # <class '__main__.People'>
    People = type(class_name,class_bases,class_dic)
    print(People) # <class '__main__.People'>
    # 所以上面的People只是个变量名,但是这个变量名的值才是类

     

     三、如何自定义元类来控制类的产生

    1、定制元类控制类的产生

    # class People(A,B,metaclass=type):  # 相当于继承了A,B两个类,metaclass=type调用了内置的元类
    # class People(metaclass=type):  # 相当于继承了object类
    # 自定义一个名叫Mymeta的元类
    class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
        # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
        def __init__(self,):print('run...') # run...
    
    # People = Mymeta(class_name,class_bases,class_dic)
    # 调用Mymeta发生三件事
    # 1、先造一个空对象==>People
    # 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作。
    # 3、返回初始化好的对象
    class People(metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say(self):
            print('%s:%s'%(self.name,self.age))
    # People = type(class_name,class_bases,class_dic)
    # People = Mymeta(class_name,class_bases,class_dic)
    
    # class People(metaclass=Mymeta):报错位置
    # TypeError: __init__() takes 1 positional argument but 4 were given
    # 针对报错:修改自定义的元类
    class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
        # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
        # def __init__(self,):
        def __init__(self,x,y,z):
            print('run...') # run...
            print(self) # <class '__main__.People'>
            print(x)    # 类名:People
            print(y)    # 类的基类:()
            print(z)    # 类的名称空间:{'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x0000019F99581550>, 'say': <function People.say at 0x0000019F995815E0>}
    # run...
    # Mymeta对象是People
    print(y)  # 类的基类:() 我们看到基类是空的,是因为class People(metaclass=Mymeta):中没有继承任何的类
    如果class People(object,metaclass=Mymeta):
    print(y)  # 运行结果就是 (<class 'object'>,)

     2、自定义类时类名必须大写的提示:

    # 自定义元类
    class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
        # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
        # def __init__(self,):
        def __init__(self,x,y,z):
            if not x.istitle():
                raise NameError('类名的首字母必须大写')
    
    # 自定义的类
    # class people(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
    class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say(self):
            print('%s:%s'%(self.name,self.age))
    
    # NameError: 类名的首字母必须大写
    # 所以把自定义的类名改成大写就可以了

     3、__new__方法:调用一个类时发生的三件事,在第一步中先造空对象,我们要用到__new__方法

    class Mymeta(type):
        # 空对象,class_name,class_bases,class_dic=("People",(object,),{...})
        def __init__(self,x,y,z):
            print('run222')
            print(y)  # 类的基类:()
            print(self.__bases__)  # (<class 'object'>,)
            if not x.istitle():
                raise NameError('类名的首字母必须大写')
        # 我们重写new方法,来造对象。
        def __new__(cls, *args, **kwargs): # 当前所在的类,调用类时所传入的参数
            # 造Mymeta对象
            print('run111111....')
            print(cls,args,kwargs)
            # 自己这里没有我们跟谁要?1、跟父类要。2、指名道姓的要
            # return super().__new__(cls,*args,**kwargs) # 1、跟父类要
            return type.__new__(cls,*args,**kwargs) # 2、指名道姓的要
    
    # 调用一个类(Mymeta)会发生的三件事。调用Mymeta就是type.__call__ 
    # 1、先造一个空对象==>People,调用类内的__new__方法来造空对象
    # 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作。
    # 3、返回初始化好的对象
    
    class People(metaclass=Mymeta):
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say(self):
            print('%s:%s'%(self.name,self.age))
    
    # 只要是调用类,那么会依次调用
    # 1、第一步调用类内的__new__
    # 2、第二步调用类内的__init__

     4、__call__方法

    # 返回初始化好的对象,到底返回的是谁?
    # call方法
    class Foo:
        def __init__(self,x,y):
            self.x = x
            self.y = y
    # Foo调用,Foo()    
    obj = Foo(111,222)
    # obj可以调用么?如下所示 obj()
    # 当obj调用时会报错:TypeError: 'Foo' object is not callable 针对这个报错,我们要在Foo类中加入call方法
    class Foo:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def __call__(self, *args, **kwargs):
         print('==》')
    obj = Foo(111,222) print(obj) # 触发的是obj.__str__ 运行结果:<__main__.Foo object at 0x0000021C1CFB44F0>
    obj() # 触发的是obj.__call__ 运行结果:==》
    class Foo:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def __call__(self, *args, **kwargs):
            print('==》')
            return 123
    
    obj = Foo(111,222)
    
    res = obj()   # 获取返回值
    print(res)    # 如果call方法下没有return得到结果是:None。有return得到的是return后面的结果123
    总结应用:如果想让一个对象可以加()括号调用,需要在该对象的类中添加一个方法__call__
    class Foo:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def __call__(self,*args,**kwargs):
            print('==》',args,kwargs)
            return 123
    
    obj = Foo(111,222)
    res = obj(1,2,3,4,a=5,b=6)   # obj传入参数,结果获取返回值
    print(res) 
    # ==》 (1, 2, 3, 4) {'a': 5, 'b': 6}
    # 123

     5、总结

    对象()-->类内的__call__
    类()-->元类内的__call__
    自定义元类() -->内置元类__call__
    # 定制元类控制类的产生
    # 自定义元类控制类的调用-->类的对象的产生
    class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
        def __init__(self,x,y,z):
            print('run2222222....')
        def __new__(cls, *args, **kwargs):
            print('run1111111....')
            return type.__new__(cls,*args,**kwargs)
    # 类的产生
    # People = Mymeta()  # 内置的元类type.__call__==》干了三件事:
    # 1、type.__call__函数内会先调用Mymeta内的__new__
    # 2、type.__call__函数内会先调用Mymeta内的__init__
    # 3、type.__call__函数内会返回一个初始化好的对象
    
    class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say(self):
            print('%s:%s'%(self.name,self.age))# 类的调用
    obj = People('lsj',18)
    
    # 我们研究一下People的调用,是在调用谁?
    # obj = People('lsj',18)  -->Mymeta.__call__-->干了三件事:
    # 1、Mymeta.__call__函数内会先调用People内的__new__
    # 2、Mymeta.__call__函数内会先调用People内的__init__
    # 3、Mymeta.__call__函数内会返回一个初始化好的对象
    
    print(obj)  
    对Mymeta进行修改
    class
    Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类 def __call__(self, *args, **kwargs): # 1、Mymeta.__call__函数内会先调用People内的__new__ people_obj = self.__new__(self) # 2、Mymeta.__call__函数内会先调用People内的__init__ self.__init__(people_obj,*args,**kwargs) # 3、Mymeta.__call__函数内会返回一个初始化好的对象 # print(self) # <class '__main__.People'> # print(args) # ('lsj', 18) # print(kwargs) # {} # return 11111 return people_obj # <__main__.People object at 0x0000023483307700>
    class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
    def __init__(self,name,age):
    self.name = name
    self.age = age
    def say(self):
    print('%s:%s'%(self.name,self.age))
    def __new__(cls, *args, **kwargs):
    # 产生真正的对象
    return object.__new__(cls)
    obj = People('lsj',18)
    print(obj)  # <__main__.People object at 0x0000029B61717700>
    print(obj.__dict__) # {'name': 'lsj', 'age': 18}

     

    # 我要把dict里的属性变成隐藏的属性
    class Mymeta(type): # 注意:这里必须继承type,只有继承了type的类才叫元类
        def __call__(self, *args, **kwargs):
            # 1、Mymeta.__call__函数内会先调用People内的__new__
            people_obj = self.__new__(self)
            # 2、Mymeta.__call__函数内会先调用People内的__init__
            self.__init__(people_obj,*args,**kwargs)
            # 3、Mymeta.__call__函数内会返回一个初始化好的对象
            print('people对象的属性:',people_obj.__dict__)
            people_obj.__dict__['xxxx'] = 11
            return people_obj  # <__main__.People object at 0x0000023483307700>
    class People(object,metaclass=Mymeta):  # metaclass=Mymeta调用了我自定义的元类
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say(self):
            print('%s:%s'%(self.name,self.age))
        def __new__(cls, *args, **kwargs):
            # 产生真正的对象
            return object.__new__(cls)
    
    # 类的调用
    obj = People('lsj',18)
    
    print(obj)  # <__main__.People object at 0x0000029B61717700>
    print(obj.__dict__) # {'name': 'lsj', 'age': 18, 'xxxx': 11}

     

     

    6、元类的属性查找

    属性查找的原则:对象->类->父类
    切记:父类不是元类
    在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),
    我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象StanfordTeacher继承对象Foo,对象Foo继承对象Bar,
    对象Bar继承对象object

     

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        n=444
    
        def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
            obj=self.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj
    
    class Bar(object):
        n=333
    
    class Foo(Bar):
        n=222
    
    class StanfordTeacher(Foo,metaclass=Mymeta):
        n=111
    
        school='Stanford'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the Stanford to learn Python' %self.name)
    
    
    print(StanfordTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为StanfordTeacher->Foo->Bar->object->Mymeta->type

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    Redis简单实践-分布式锁
    Redis基础数据结构
    Redis介绍
    MakeGenericType方法,运行时传入泛型T参数,动态生成泛型类
    Visual Studio 2017 Ctrl+单击键会跳转到定义的设置
    10 分钟 创建分布式微服务
    nodejs 中自定义事件
    我是这么给娃娃取名的(使用 node.js )
    使用 Fiddler 上传微信公众账号 自定义菜单
    drf_yasg 简单使用
  • 原文地址:https://www.cnblogs.com/liunaixu/p/12889441.html
Copyright © 2011-2022 走看看