• 元类


    什么是元类

    一切源自于一句话,python中一切皆为对象。
    所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化)
    Teacher是通过type实例化得到的,既然如此,是不是可以自己调用type来实例化一个class呢
    >>>>>>
    创建类的流程分析
    class关键字子在帮我们创建类时,必然帮我们调用了元类Teacher=type(...)
    那调用type时传入的参数是什么呢?必然是类的关键字组成部分,一个类有三个组成部分
    1.类名class_name='Teacher'
    2.基类class_bases=(object,)
    3.类的名称空间class_dic,类的名称空间是执行类体代码而得到的
    调用type时会依次传入以上三个参数
    class Teacher(object):
        school = 'shanghai'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print('%s says welcome to shanghai' % self.name)
    
    
    t1 = Teacher('xxx', 18)
    print(type(t1))  # 查看对象t1的类是<class '__main__.Teacher'>
    t_class = Teacher
    
    li = [t_class]
    
    
    def func(cls):
        print(cls)
    
    
    func(li[0])
    # 完全没问题把他当做对象来使用 和其他对象没有任何区别
    什么是元类
        1.需求规范类名必须大写
    2.类中必须包含文档注释
    class MyMate(type):
        def __init__(self, name, bases, dic):
            print('run')
            if not dic.get('__doc__'):
                raise TypeError('类必须有文档注释!')
            if not name.istitle():
                raise TypeError('类名必须大写开头!')
            super().__init__(name, bases, dic)
    
    
    class Foo(object, metaclass=MyMate):
        ''''
        aaa
        '''
        pass
    代码
    自定义元类控制类的调用
    __call__函数的执行时机
    该函数会在调用对象是自动触发执行(对象加括号)
    class Foo:
    def __call__(self, *args, **kwargs):
    print('run')

    f = Foo() # 调用 Foo得到f对象
    f() # 调用对象时触发__call__的执行

    必须明确创建对象的过程:先创建空对象,执行初始化将属性存储到对象的名称空间中
    所以在__call__函数中必须完成这两步操作,同时将初始化完成的对象返回给调用者
    一旦覆盖了__call__函数,就必须自己完成上述的几个步骤
    class MyMate(type):
        def __call__(self, *args, **kwargs):
            # 创建空对象
            # 调用init
            # 返回初始化后的对象
    
            obj = object.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
    
    
    class Foo(metaclass=MyMate):
        def __init__(self):
            print('初始化对象')
    
    
    f = Foo()
    print(f)
    代码
    通过元类来控制一个类实例化对象的过程
    只需覆盖__call__函数我们就能完成对实例化过程的控制
    需求:
    要求实例化时传参必须为关键字形式,否则抛出异常
    TypeError: must use keyword argument
    key作为用户自定义类产生对象的属性,且所有属性变成大写
    class MyMetaclass(type):
        def __call__(self, *args, **kwargs):
            if args:
                raise TypeError('TypeError: must use keyword argument')
            obj = object.__new__(self)  # 创建对象,self为类的Chinese
            for k, v in kwargs.items():
                obj.__dict__[k.upper()] = v
            return obj
    
    
    class Chinese(metaclass=MyMetaclass):
        county = 'China'
        tag = 'Chinese'
    
        def walk(self):
            print('%s is walk ' % self.name)
    
    
    p = Chinese(name='xxx', age=18, sex='man')
    print(p.__dict__)
    案例

    补充:

        产生类Teacher的过程时在调用MyMeta,而MyMeta也是type的类的一个对象,那么MyMeta之所以可以调用
    一定是在元类type中有一个__call__方法

    元类实现单例

    什么是单例

     单例指的是单个实例,指一个类只能有一个实例对象

    为什么要用单例

        当一个类的实例中的数据不会变化时使用单例,数据是不变的
    例如开发一个音乐播放器程序,音乐播放器可以封装为一个对象
    当你切歌的时候,是否重新创建一个播放器
    播放器中的数据和业务逻辑都是相同的没有必要创建新的,所以最好使用单例模式,以节省资源
    当两个对象的数据完全相同时,则没有必要占用两份资源
    class MyMeta(type):
        __instance = None
    
        def __init__(self, name, bases, dic):
            if not self.__instance:
                self.__instance = object.__new__(self)
                self.__init__(self.__instance)
                super().__init__(name, bases, dic)
    
        def __call__(cls):
            return cls.__instance
    
    
    class Play(metaclass=MyMeta):
        def __init__(self):
            print('创建播放器了')
    
    
    Play()
    Play()
    单例

    元类之属性查找

    class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        n = 444
    
        def __new__(cls, *args, **kwargs):
            pass
    
    
    class Bar(object):
        n = 333
    
        def __new__(cls, *args, **kwargs):
            pass
    
    
    class Foo(Bar):
        n = 222
    
        def __new__(cls, *args, **kwargs):
            pass
    
    
    class Teacher(Foo, metaclass=Mymeta):
        n = 111
    
        def __new__(cls, *args, **kwargs):
            pass
    
        school = 'Tsinghua'
    
    
    print(Teacher.__new__)
    print(Teacher.n)
    查找顺序

    属性查找的顺序依然时遵循MRO列表顺序,

    当顶级类object中不存在时会查找元类,
    元类没有时查找元类的父类也就是type类
    令人迷糊的__new__函数与__init__函数
    class M(type):
        def __init__(self, classname, bases, namespace):
            print('init')
    
        def __call__(self, *args, **kwargs):
            pass
    
        pass
    
    
    class A(metaclass=M):
        n = 1
        pass
    
    
    print(A.__name__)
    print(A.__bases__)
    print(A.__dict__)
    __init__
    __init__可以控制类的创建过程,但是现在我们看到的是,init中没有任何代码
    但是类的三个基本信息已经都有了,这说明类的创建其实已经完成了
    class M(type):
        def __new__(cls, *args, **kwargs):
            print('new')
            # return type.__new__(cls,*args,**kwargs)
    
        def __init__(self, classname, bases, namespace):
            print('init')
    
    
    class A(metaclass=M):
        n = 1
    
    
    print(A.__name__)
    print(A.__bases__)
    print(A.__dict__)
    __new__
    执行了__new__函数但是并没有执行__init__,因为__new__函数是真正用于创建类的
    方法,只有创建类成功了才会执行init函数,new必须要返回值且返回类型为__type__
    时才会执行__init__函数
    将__new__中被注释的代码代开一切正常
    总结:元类中__new__是用于创建类对象的__init__是用于初始化类的其他信息的
  • 相关阅读:
    SQL SET NOCOUNT (Transact-SQL)
    Delphi WinAPI DragAcceptFiles、DragQueryFile、DragFinish、DragQueryPoint
    Delphi Thread线程错误:Canvas doesn't allow drawing
    Delphi ADOQuery错误:ADOQuery1:commandtext does not return a result set
    医学-药物-未分类-胃舒平(复方氢氧化铝)
    SQL 查询时间超时已过期(SQL 2000、SQL2005、SQL2008、SQL2012等)
    SQL 数据库引擎语句 sp_executesql 的使用介绍(Transact-SQL)
    android 定位代码
    Centos7开放及查看端口
    非常详细的sklearn介绍
  • 原文地址:https://www.cnblogs.com/ShenJunHui6/p/10580754.html
Copyright © 2020-2023  润新知