zoukankan      html  css  js  c++  java
  • 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()
    

      

  • 相关阅读:
    python常见的错误异常
    vim命令c编程
    递归函数初步理解---python实现(汉诺塔问题)
    python报错:AttributeError: module 'pdb' has no attribute 'set_trace'
    python程序控制结构
    python函数
    结构体的四种定义方法
    基于一维数组的桶排序入门
    双链表与单链表的比较
    在用free()函数释放指针内存时为何要将其指针置空
  • 原文地址:https://www.cnblogs.com/itfenqing/p/10230191.html
Copyright © 2011-2022 走看看