python的元類

本文示例代碼在python3.7下python

一.元類(metaclass)

1.python中一切皆對象.class也是一個對象.

class A:
    pass

a = A()
print(type(a))
print(type(A))

  

輸出sql

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

  

a是A類的實例,類A(是一個class)是type的實例(注意:類A是type的實例,是object的子類,全部新式類的根類都是object)app

2.A類是如何建立的?

(1).解釋器檢查是否有類A的元類屬性,若是有則按指定元類來建立

(2).若是沒有則默認使用type元類來建立

(3).對於建立類,解釋器調用元類,須要使用三個參數

name:這將是須要建立的類的名稱。對於咱們的狀況,它將是字符串'A'。
bases:該類基類的元組。
attrs:它是一個字典,包含爲該類定義的全部屬性函數

所以動態建立上述A類能夠寫做:spa

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__來建立類blog

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

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()
相關文章
相關標籤/搜索