• python的元类


    本文示例代码在python3.7下

    一.元类(metaclass)

    1.python中一切皆对象.class也是一个对象.

    class A:
        pass
    
    a = A()
    print(type(a))
    print(type(A))
    

      

    输出

    <class '__main__.A'>
    <class 'type'>
    

      

    a是A类的实例,类A(是一个class)是type的实例(注意:类A是type的实例,是object的子类,所有新式类的根类都是object)

    2.A类是如何创建的?

    (1).解释器检查是否有类A的元类属性,如果有则按指定元类来创建

    (2).如果没有则默认使用type元类来创建

    (3).对于创建类,解释器调用元类,需要使用三个参数

    name:这将是需要创建的类的名称。对于我们的情况,它将是字符串'A'。
    bases:该类基类的元组。
    attrs:它是一个字典,包含为该类定义的所有属性

    因此动态创建上述A类可以写作:

    A = type('A', (), {})
    

      

    3.元类:

    (1).type是创建类的默认元类,指示了类如何被创建.
    (2).元类只是一个可以创建类的类,就像普通类具有创建该类实例的能力一样,元类也具有创建类的能力。创建的类是元类的一个实例
    (3).类是元类的一个实例
    (4).由于type是默认元类,因此如果我们要编写元类,我们必须从type继承。

    二.实例

    1.自定义元类:

    class TestMetaClass(type):
        def __new__(cls, name, bases, attrs):
            return super().__new__(cls, name, bases, attrs)
    

      

    (1).继承自元类type
    (2).在元类中覆写了__new__
    (3).解释器将使用三个参数调用我们的元类,因此元类的__new__将接收四个参数。所以我们要注意__new__元类需要四个参数
    (4).在__new__中我们使用超类中的__new__,如果TestMetaClass不是从type继承,实际的类创建将发生在type的__new__中
    (5).任何类的__new__收到的第一个参数是类本身(上文代码中的cls)

    如果我们编写元类,必须从type继承,必须覆写__new__,并且调用超类的__new__来创建类

    2.使用TestMetaClass

    class B(metaclass=TestMetaClass):
        pass
    
    b = B()
    print(type(b))
    print(type(B))
    

      

    输出
    <class '__main__.B'>
    <class '__main__.TestMetaClass'>

    b是类B的一个实例,类B是TestMetaClass的实例

    (1).解释器知道默认的元类类型不能用于创建B.而是必须使用TestMetaClass来创建B.

    (2).当调用MyMeta时,调用MyMeta的__new__

    (3).以上代码等同于:

    B = TestMetaClass('B', (), {})
    b = B()
    print(type(b))
    print(type(B))

    (4).因为TestMetaClass继承自type,所以TestMetaClass的__new__也可以定义成

    class TestMetaClass(type):
        def __new__(cls, name, bases, attrs):
            return type.__new__(cls, name, bases, attrs)
    

      

    三.type

    1.type(),是一个内置函数,可以检查类型

    方法为:
    type(some_object)

    2.type是一个内置函数,也可以动态创建类:

    方法为:
    type(cls类名, bases(继承的元组), attrs(属性字典))

    3.type也是一个类,是创建类的默认元类

    四.何时使用

    1.元类使用的比较少,99%的情况下用不到
    2.一个简单的例子,限制类的属性:

    allowed_attributes = ['first', 'second']
    
    class Meta(type):
        def __new__(cls, name, bases, attrs):
            attrs_list = list(attrs)
            for each_attr in attrs_list:
                if not each_attr.startswith('_') and each_attr not in allowed_attributes:
                    del attrs[each_attr]
                    print("Attributes after deleting non allowed attributes", attrs)
            return type.__new__(cls, name, bases, attrs)
    
    
    class B(metaclass=Meta):
        first = 1
        second = 2
        third = 3
    
    b = B()
    

      

    注意:
    上文代码中,您或许可能认为直接使用__new__就可以实现,因为__new__就是负责实例的创建.但类B中first,second等属性是静态属性,隶属于类,而不是实例,所以此处使用了元类.元类是负责类的创建.

    我们使用__new__来写一个限制实例属性的(不是很恰当)

    class My:
        def __new__(cls, *args, **kwargs):
            print(kwargs)
            if not isinstance(kwargs, dict):
                raise RuntimeError('参数错误')
            if 'c' in kwargs:
                raise RuntimeError('不能包含key为c的参数')
            return super().__new__(cls)
    
        def __init__(self, **kwargs):
            self.args = kwargs
    
    
    test = My(a=2, b=3, c=100)
    print(test.args)
    

      

    3.ORM的例子

    class Field(object):
        def __init__(self, name, column_type):
            self.__name = name
            self.__column_type = column_type
    
        def __str__(self):
            return '<%s,%s>' % (self.__name, self.__column_type)
    
        def __getattr__(self, item):
            return {'name': self.__name, 'column': self.__column_type}[item]
    
    class IntegerField(Field):
        def __init__(self, name):
            super(IntegerField, self).__init__(name, 'bigint')
    
    class ModelMetaClass(type):
        def __new__(cls, name, bases, attrs):
            if name == 'Model':
                return type.__new__(cls, name, bases, attrs)
    
            mappings = dict()
            for k, v in attrs.items():
                if isinstance(v, Field):
                    mappings[k] = v
    
            for k in mappings.keys():
                attrs.pop(k)
    
            attrs['__mappings__'] = mappings
            attrs['__table__'] = name
            return type.__new__(cls, name, bases, attrs)
    
    class Model(dict, metaclass=ModelMetaClass):
        def __init__(self, **kwargs):
            super(Model, self).__init__(**kwargs)
    
        def __getattr__(self, key):
            try:
                return self[key]
            except BaseException:
                raise AttributeError(r"'Model' object has no attribute '%s'" % key)
    
        def __setattr__(self, key, value):
            self[key] = value
    
    
        def save(self):
            fields = []
            params = []
            args = []
    
            for k, v in self.__mappings__.items():
                fields.append(v.name)
                params.append('?')
                args.append(getattr(self, k, None))
    
            sql = 'insert into %s(%s) values(%s)' % 
                  (self.__table__, ','.join(fields), 
                   ','.join([str(x) for x in args]))
            print('SQL: %s' % sql)
    
    
    class User(Model):
        id = IntegerField('id')
    
    # create user instance
    user = User(id=100)
    user.save()
    

      

  • 相关阅读:
    day 66 crm(3) 自创组件stark界面展示数据
    day 65 crm(2) admin源码解析,以及简单的仿造admin组件
    用 Python+nginx+django 打造在线家庭影院
    django -admin 源码解析
    day 64 crm项目(1) admin组件的初识别以及应用
    云链接 接口不允许 情况 解决方法 mysql Host is not allowed to connect to this MySQL server解决方法
    day 56 linux的安装python3 ,虚拟环境,mysql ,redis
    day55 linux 基础以及系统优化
    Codeforces 989 P循环节01构造 ABCD连通块构造 思维对云遮月参考系坐标轴转换
    Codeforces 990 调和级数路灯贪心暴力 DFS生成树两子树差调水 GCD树连通块暴力
  • 原文地址:https://www.cnblogs.com/itfenqing/p/10230191.html
Copyright © 2020-2023  润新知