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()

  

 

posted @ 2019-01-06 20:20  rorshach  阅读(225)  评论(0编辑  收藏  举报