zoukankan      html  css  js  c++  java
  • 元类

    元类

    一切源自一句话:python中一切皆对象.

    class StanfordTeacher(object):
        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)
    

    所有对象都是实例化或者说调用类而得到的(调用类的过程为类的实例化),比如对象t1是调用类StanfordTeacher得到的

    t1=StanfordTeacher('lili',18)
    print(type(t1)) #查看对象t1的类是<class '__main__.StanfordTeacher'>
    

    如果一切皆为对象,那么类StanfordTeacher本质也是一个对象,既然所有的对象都是调用类得到的,那么StanfordTeacher必然也是调用了一个类得到的,这个类称为元类

    于是我们可以推导出====>产生StanfordTeacher的过程一定发生了:StanfordTeacher=元类(...)

    print(type(StanfordTeacher)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的StanfordTeacher,即默认的元类为type
    

    class关键字创建类的流程分析

    上文我们基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为Type

    class关键字在帮我们创建类是,必然帮我们调用了元类StanfordTeacher = type(...),那调用type时传入的参数是什么?

    必然是类的关键组成部分,一个类有三大组成部分,分别是

    1,类名class_name = 'StanfordTeacher '

    2,基类们class_bases = (object)

    3,类的名称空间class_bases = (object)

    4,调用type时会依次传入以上三个参数

    StanfordTeacher = type(class_name,class_bases,class_dic)

    # 1、先拿到一个类名
    class_name = "StanfordTeacher"
    
    # 2、然后拿到类的父类
    class_bases = (object,)
    
    # 3、再运行类体代码,将产生的名字放到名称空间中
    class_dic = {}
    class_body = """
    school = 'oldboy'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def say(self):
        print('%s says welcome to the oldboy to learn Python' % self.name)
    """
    exec(class_body,{},class_dic)
    # print(class_dic)
    
    # 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名StanfordTeacher ,StanfordTeacher 就是我们用class自定义的那个类
    StanfordTeacher  = type(class_name,class_bases,class_dic)
    

    exec的用法

    #exec:三个参数
    
    #参数一:包含一系列python代码的字符串
    
    #参数二:全局作用域(字典形式),如果不指定,默认为globals()
    
    #参数三:局部作用域(字典形式),如果不指定,默认为locals()
    
    #可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
    g={
        'x':1,
        'y':2
    }
    l={}
    
    exec('''
    global x,z
    x=100
    z=200
    
    m=300
    ''',g,l)
    
    print(g) #{'x': 100, 'y': 2,'z':200,......}
    print(l) #{'m': 300}
    

    自定义元类控制类的OldboyTeacher的创建

    一个类没有声明自己的元类,默认他的元类就是Type,除了使用内置元类Type,我们可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类

    class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
        pass
    
    
    class OldboyTeacher(object, metaclass=Mymeta):
        school = 'oldboy'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print('%s says welcome to the oldboy to learn Python' % self.name)
    
    
    # 1、先拿到一个类名:"OldboyTeacher"
    # 2、然后拿到类的父类:(object,)
    # 3、再运行类体代码,将产生的名字放到名称空间中{...}
    # 4、调用元类(传入类的三大要素:类名、基类、类的名称空间)得到一个元类的对象,然后将元类的对象赋值给变量名OldboyTeacher,oldboyTeacher就是我们用class自定义的那个类
    OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
    

    自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程

    调用Mymeta会先产生一个空对象,然后连同调用Mymeta括号内的参数一同传给Mymeta下的__init__方法,完成初始化.

    import re
    
    
    class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
        def __init__(self, class_name, class_bases, class_dic):
            # print(self)  # 类<class '__main__.OldboyTeacher'>
            # print(class_name)
            # print(class_bases)
            # print(class_dic)
    
            if not re.match("[A-Z]", class_name):
                raise BaseException("类名必须用驼峰体")
    
            if len(class_bases) == 0:
                raise BaseException("至少继承一个父类")
    
            # print("文档注释:",class_dic.get('__doc__'))
            doc=class_dic.get('__doc__')
    
            if not (doc and len(doc.strip()) > 0):
                raise BaseException("必须要有文件注释,并且注释内容不为空")
    
    # OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
    class OldboyTeacher(object,metaclass=Mymeta):
        """
        adsaf
        """
    
        school = 'oldboy'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print('%s says welcome
    

    自定义元类控制类的OldboyTeacher的调用

    储备知识:call

    import re
    
    
    class Mymeta(type):  # 只有继承了type类的类才是自定义的元类
        def __init__(self, class_name, class_bases, class_dic):
            # print(self)  # 类<class '__main__.OldboyTeacher'>
            # print(class_name)
            # print(class_bases)
            # print(class_dic)
    
            if not re.match("[A-Z]", class_name):
                raise BaseException("类名必须用驼峰体")
    
            if len(class_bases) == 0:
                raise BaseException("至少继承一个父类")
    
            # print("文档注释:",class_dic.get('__doc__'))
            doc = class_dic.get('__doc__')
    
            if not (doc and len(doc.strip()) > 0):
                raise BaseException("必须要有文件注释,并且注释内容不为空")
    
        # res = OldboyTeacher('egon',18)
        def __call__(self, *args, **kwargs):
            # 1、先创建一个老师的空对象
            tea_obj = object.__new__(self)
            # 2、调用老师类内的__init__函数,然后将老师的空对象连同括号内的参数的参数一同传给__init__
            self.__init__(tea_obj, *args, **kwargs)
            tea_obj.__dict__ = {"_%s__%s" %(self.__name__,k): v for k, v in tea_obj.__dict__.items()}
    
            # 3、将初始化好的老师对象赋值给变量名res
            return tea_obj
    
    
    # OldboyTeacher = Mymeta("OldboyTeacher",(object,),{...})
    class OldboyTeacher(object, metaclass=Mymeta):
        """
        adsaf
        """
    
        school = 'oldboy'
    
        #            tea_obj,'egon',18
        def __init__(self, name, age):
            self.name = name  # tea_obj.name='egon'
            self.age = age  # tea_obj.age=18
    
        def say(self):
            print('%s says welcome to the oldboy to learn Python' % self.name)
    
    
    res = OldboyTeacher('egon', 18)
    print(res.__dict__)
    # print(res.name)
    # print(res.age)
    # print(res.say)
    
    # 调用OldboyTeacher类做的事情:
    # 1、先创建一个老师的空对象
    # 2、调用老师类内的__init__方法,然后将老师的空对象连同括号内的参数的参数一同传给__init__
    # 3、将初始化好的老师对象赋值给变量名res
    

    单例模式

    单例模式是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.无论调用多少次产生的实例对象,都是指向同一个内存地址,仅仅只有一个实例(只有一个对象)

    1,使用模块

    python的模块就是单例模式,因为模块在第一次导入时,会产生.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码,因此,我们只需把相关的函数和数据定义在一个模块中,就可获得一个单例对象

    2,通过元类
    import settings
    
    
    class Mymeta(type):
        __instance = None
        def __init__(self,class_name,class_bases,class_dic):
            self.__instance=object.__new__(self)  # Mysql类的对象
            self.__init__(self.__instance,settings.IP,settings.PORT)
    
        def __call__(self, *args, **kwargs):
            if args or kwargs:
                obj = object.__new__(self)
                self.__init__(obj, *args, **kwargs)
                return obj
            else:
                return self.__instance
    
    # MySQL=Mymeta(...)
    class MySQL(metaclass=Mymeta):
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
            
            
    obj3 = MySQL()
    obj4 = MySQL()
    
    print(obj3 is obj4)
    # 原理:类定义时会调用元类下的__init__,类调用(实例化对象)时会触发元类下的__call__方法
    # 类定义时,给类新增一个空的数据属性,
    # 第一次实例化时,实例化之后就将这个对象赋值给类的数据属性;第二次再实例化时,直接返回类的这个数据属性
    
    
    通过绑定方法classmethod
    import settings
    
    
    class MySQL:
        __instance = None #记录实例化对象
    
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
        @classmethod
        def singleton(cls):
            if cls.__instance:
                return cls.__instance
            cls.__instance = cls(settings.IP, settings.PORT)
            return cls.__instance
    
    
    
    obj3 = MySQL.singleton()
    print(obj3)
    
    obj4 = MySQL.singleton()
    print(obj4)
    
    
    通过装饰器
    import settings
    
    def outter(func):  # func = MySQl类的内存地址
        _instance = func(settings.IP,settings.PORT)
        def wrapper(*args,**kwargs):
            if args or kwargs:
                res=func(*args,**kwargs)
                return res
            else:
                return _instance
        return wrapper
    
    @outter  # MySQL=outter(MySQl类的内存地址)  # MySQL=》wrapper
    class MySQL:
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
    
    # obj1 = MySQL("1.1.1.1", 3306)
    # obj2 = MySQL("1.1.1.2", 3306)
    # print(obj1)
    # print(obj2)
    
    obj3 = MySQL()
    obj4 = MySQL()
    print(obj3 is obj4)
    

    属性查找

    class Mymeta(type):
        n=444
    
        # def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
        #     obj=self.__new__(self)
        #     print(self.__new__ is object.__new__) #True
    
    
    class Bar(object):
        # n=333
    
        # def __new__(cls, *args, **kwargs):
        #     print('Bar.__new__')
        pass
    
    class Foo(Bar):
        # n=222
    
        # def __new__(cls, *args, **kwargs):
        #     print('Foo.__new__')
        pass
    
    class OldboyTeacher(Foo,metaclass=Mymeta):
        # n=111
    
        school='oldboy'
    
        def __init__(self,name,age):
            # self.n=0
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the oldboy to learn Python' %self.name)
    
    
        # def __new__(cls, *args, **kwargs):
        #     print('OldboyTeacher.__new__')
    
    
    # obj=OldboyTeacher('egon',18)
    # print(obj.n)
    
    print(OldboyTeacher.n)
    

    总结:

    Myneta下的__call__里的self.new__在OldboyTeacher,Foo,Bar里都没有找到__new__的情况下,会去找object里的__new,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会,也根本没必要再去找元类Mymeta=>type中查找__new__

  • 相关阅读:
    HDU 2639 Bone Collector II (01背包,第k解)
    POJ 2184 Cow Exhibition 奶牛展(01背包,变形)
    hihoCoder #1165 : 益智游戏 (挑战赛11 B题)
    UVA 562 Dividing coins 分硬币(01背包,简单变形)
    POJ Charm Bracelet 挑饰品 (常规01背包)
    hiho一下 第四十四周 博弈游戏·Nim游戏(直接公式解)
    UVA 624 CD(01背包,要记录路径)
    118 Pascal's Triangle 帕斯卡三角形 杨辉三角形
    117 Populating Next Right Pointers in Each Node II 每个节点的右向指针 II
    116 Populating Next Right Pointers in Each Node 每个节点的右向指针
  • 原文地址:https://www.cnblogs.com/lgh8023/p/13524515.html
Copyright © 2011-2022 走看看