• Python之元类


    type生元类,元类生类,类生对象

    换句话就是

    道系元类解读,生就完了

    话不多说上代码来理解:

    def fn1(self,name='world'):
        print('Hello,%s'%name)
    def fn2(self,name='world'):
        print('Hi,%s'%name)
    
    Hello = type('Hello',(object,),dict(say_hi=fn1,hi='hello'))
    Hi = type('Hello',(object,),dict(say_hi=fn2,hi='hi'))
    # 生成Hello类的对象
    hello1 = Hello()
    hello1.say_hi()
    print(hello1.hi)
    print(hello1)
    
    print('-'*50) # 华丽分割线
    # 生成Hi类的对象 hello2 = Hi() print(hello2.hi) hello2.say_hi() print(hello2)

    结果:

    Hello,world
    hello
    <__main__.Hello object at 0x0000022EBA06EBA8>
    --------------------------------------------------
    hi
    Hi,world
    <__main__.Hello object at 0x0000022EBA06EC50>

    type()可以产生类,那么结果就证明了,他可以定制类的名称属性方法等。可以用于创造万物。

    django中的ORM,大致思路如下:

    class Field(object):
        def __init__(self, column_type, primary_key, default):
            self.column_type = column_type
            self.primary_key = primary_key
            self.default = default
    
    
    class StringField(Field):
        def __init__(self, column_type='varchar(32)', primary_key=False, default=None):
            super().__init__(column_type, primary_key, default)
    
    
    class IntegerField(Field):
        def __init__(self,column_type='int', primary_key=False, default=0):
            super().__init__(column_type, primary_key, default)
    
    
    class MyMetaClass(type):
        def __new__(cls, class_name,class_bases,class_attrs):
            if class_name == 'Models':
                return type.__new__(cls,class_name,class_bases,class_attrs)
            table_name = class_attrs.get('table_name',class_name)
            primary_key = None
            mappings = {}
    
            for k,v in class_attrs.items():
                if isinstance(v,Field):
                    mappings[k]=v
                    if v.primary_key:
                        if primary_key:
                            raise TypeError('一张表只能有一个主键')
                        primary_key = k
    
            print(class_attrs)
    
            for k in mappings.keys():
                class_attrs.pop(k)
            if not primary_key:
                raise TypeError('一张表必须有主键')
            class_attrs['table_name'] = table_name
            class_attrs['primary_key'] = primary_key
            class_attrs['mappings'] = mappings
    
            print(class_attrs)
    
            return type.__new__(cls, class_name,class_bases,class_attrs)
    
    class Models(dict,metaclass=MyMetaClass):
    
        def __init__(self,**kwargs):
            super().__init__(**kwargs)
    
        def __getattr__(self, item):
            return self.get(item,'没有该键')
    
        def __setattr__(self, key, value):
            self[key] = value
    
        @classmethod
        def select(cls,**kwargs):
            pass
    
    if __name__ == '__main__':
        class Teacher(Models):
            table_name = 'author'
            id = IntegerField(primary_key=True)
            name = StringField()
    
        a = Teacher(id=12,name='xxx')
        print(a)
        print(a.name)

    结果:

    {'__module__': '__main__', '__qualname__': 'Teacher', 'table_name': 'author', 'id': <__main__.IntegerField object at 0x0000021E7680ECF8>, 'name': <__main__.StringField object at 0x0000021E76814438>}
    {'__module__': '__main__', '__qualname__': 'Teacher', 'table_name': 'author', 'primary_key': 'id', 'mappings': {'id': <__main__.IntegerField object at 0x0000021E7680ECF8>, 'name': <__main__.StringField object at 0x0000021E76814438>}}
    {'id': 12, 'name': 'xxx'}
    xxx

    可以理解为:元类metaclass,是继承了type来控制其他类的产生的工具

    然后用MyMetaClass来控制继承Model类的Teacher类,在生成类的实例的过程中,将类的同名属性id、name打包进mappings属性中,使得可以通过getattr来获取他自身字典中的值。可能还是不太明白那就继续看:

    class Models(dict):
    
        def __init__(self,**kwargs):
            super().__init__(**kwargs)
    
        def __getattr__(self, item):
            return self.get(item,'没有该键')
    
        def __setattr__(self, key, value):
            self[key] = value
    
        @classmethod
        def select(cls,**kwargs):
            pass
    
    if __name__ == '__main__':
        class Teacher(Models):
            table_name = 'author'
            id = IntegerField(primary_key=True)
            name = StringField()
    
        a = Teacher(id=12,name='xxx')
        print(a)
        print(a.name)

    结果:

    {'id': 12, 'name': 'xxx'}
    <__main__.StringField object at 0x000001BD9825EDD8>

    此时没有元类MyMetaClass的控制,a.name,获取到的是Teacher类的属性,是一个StringField的对象,而不是他本身字典的键对应值,问题就是这里,元类的引入就是为了解决这种问题,因为a.name会首先找本类的属性,再找父类的属性,如果在这过程中找到了,就不会继续查询,也根本不会调用重写的__getattr__方法。所以在类的创建时,通过把原先的属性隐藏起来,可以让对象去触发getattr得到自身包含的值。

    type产生类

    # 类是type的对象,可以通过type(参数)来创建类
    
    # type(name, bases, namespace)
    
    s = '''
    my_a = 10
    my_b = 20
    def __init__(self):
        pass
    @classmethod
    def print_msg(cls, msg):
        print(msg)
    '''
    namespace = {}
    exec(s, {}, namespace)
    
    Student = type('Student', (object, ), namespace)
    
    stu = Student()

     自定义元类

    # 元类:所有自定义的类本身也是对象,是元类的对象,所有自定义的类本质上是由元类实例化出来了
    Student = type('Student', (object, ), namespace)
    
    class MyMeta(type):
        # 在class Student时调用:Student类的创建 => 来控制类的创建
        
        # 自定义元类,重写init方法的目的:
        # 1.该方法是从type中继承来的,所以参数同type的init
        # 2.最终的工作(如果开辟空间,如果操作内存)还是要借助type
        # 3.在交给type最终完成工作之前,可以对类的创建加以限制 *****
        def __init__(cls, class_name, bases, namespace):
            # 目的:对class_name | bases | namespace加以限制 **********************
            super().__init__(class_name, bases, namespace)
        
        # 在Student()时调用:Student类的对象的创建 => 来控制对象的创建
        
        # 自定义元类,重写call方法的目的:
        # 1.被该元类控制的类生成对象,会调用元类的call方法
        # 2.在call中的返回值就是创建的对象
        # 3.在call中
        #       -- 通过object开辟空间产生对象
        #       -- 用被控制的类回调到自己的init方法完成名称空间的赋值
        #       -- 将修饰好的对象反馈给外界
        def __call__(cls, *args, **kwargs):
            # 目的:创建对象,就可以对对象加以限制 **********************
            obj = object.__new__(cls)  # 通过object为哪个类开辟空间
            cls.__init__(obj, *args, **kwargs)  # 调回当前被控制的类自身的init方法,完成名称空间的赋值
            return obj
    
    # 问题:
    # 1.继承是想获得父级的属性和方法,元类是要将类的创建于对象的创建加以控制
    # 2.类的创建由元类的__init__方法控制
    #        -- 元类(class_name, bases, namespase) => 元类.__init__来完成实例化
    # 3.类的对象的创建由元类的__call__方法控制
    #         -- 对象产生是需要开辟空间,在__call__中用object.__new__()来完成的
    class Student(object, metaclass=MyMeta):
        pass
    
    # class Student:  <=>  type(class_name, bases, namespace)

    单例

    # 单例:一个类只能产生一个实例
    # 为什么要有单例:
    # 1.该类需要对象的产生
    # 2.对象一旦产生,在任何位置再实例化对象,只能得到第一次实例化出来的对象
    # 3.在对象唯一创建后,可以通过属性修改或方法间接修改属性,来完成数据的更新,不能通过实例化方式更新数据
  • 相关阅读:
    node
    前端工程师的思考
    前端工程师需要具备的条件
    产品经理必须掌握的名词
    金融人必须掌握的词汇
    央企降两金、降杠杆的 “兵器谱”
    抽屉协议
    清分、清算、结算的关系
    如何有效规避风险
    浅谈在项目管理过程中风险管理
  • 原文地址:https://www.cnblogs.com/xufengfan/p/10951356.html
Copyright © 2020-2023  润新知