Python之難點元類|一句話給你安排的明明白白

Xufengfan發表於2019-05-30

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得到自身包含的值。

 

相關文章