python入門學習記錄之元類type和metaclass的使用

引言:衆所周知 python是解釋型語言,亦是動態語言,解釋型語言和編譯型語言的不同在於函數和類的定義是在運行時創建的。

例如有一個user.py和class user(object):,python解釋器就會在載入該user.py模塊時依次執行,動態創建user類對象

一:python有一個type()函數:該函數的作用可以查看一個類型或變量的類型

type(user):type class類型

type(user):class-user類型

type()函數既可以返回一個數的類型。還可以運行時動態創建類,其實user實例在創建時也是由type()創建的,解析器掃描到user需創建實例便調用type函數進行創建,同樣的,我們不需要提前寫好類的定義也能在運行時動態創建,這就是動態語言的魅力。

type(name,tuple,fn)創建類有三個參數:

1:要創建的類名

2:要創建的類需繼承的元祖,單繼承需加,

3:需綁定的函數:例def fun():hello = fun(爲類綁定一個hello函數)

現在我們便可使用該類了

例:

User = type('User',(object,),hello=fn)

user=User()

user.hello()

正常情況下我們都是用class xx():定義,type()函數可以創建類便說明動態語言本身支持運行時創建類

動態語言和靜態語言的不同在於,靜態語言需定義好要運行的類,再由編譯器編譯成字節碼文件再交由機器執行

動態語言則省去了編譯這部分,直接在運行時由解釋器來解析數據交由機器執行

二:元類metaclass的使用

除了使用type()創建類時,我們還可以使用metaclass控制類的創建行爲:

metaclass直譯爲元類,也稱爲python的魔術代碼

當我們定義好類之後,可以通過metaclass創建類的實例,先定義類,再定義metaclass,再創建實例

如果想先創建類,就先定義metaclass,再創建class,再創建實例

metaclass允許你創建類和修改類,所以可以把類看成是metaclass創建出來的實例

創建metaclass,類名結尾一般爲Metaclass,Metaclass是類的模板,所以需繼承type

例:class ListMetaclass(type):

其中__now__有4個重要參數:

cls:準備創建的類的對象,

name:類名

base:類繼承的父類(list)

attrs:類的函數集合

    class ListMetaclass(type):
    def  __now__(cls,name,base,attrs):

    attrs['hello'] = lambda self,value:self.append(value)

    return type.__now__(cls,name,base,attrs)

    class Mylist(List,metaclass=ListMetaclass)

        pass

加上metaclass=,其中Mylist 擁有hello這個函數,需傳一個value ,等價於定義了def hello(self,value):

而繼承的List卻是沒有hello函數的,因爲是創建在metaclass的實例Mylist上的

metaclass就相當於java的aop,aop的原理是java代理技術,在運行時由代理子類來替換父類執行

metaclass也是在類運行時增加函數方法的行爲,還可以修改類的函數方法,aop不可以

metaclass的應用場景不是非常多,可以簡單時儘量也不要使用metaclass

例如ORM就是一個典型的例子:
orm全稱爲對象關係-映射,將類的對象在運行時映射爲數據庫的表字段,也就是一個類的字段對應一個表的字段

要編寫orm框架,所有的類只能動態定義,在運行時由使用者來構造類:

#!/usr/bin/env python
# -*- encoding:utf-8 -*-
"""

# !/user/bin/env python
# ! -*- coding:utf-8 -*-
'''
我們想要的結果是
user = User('1','zf','100.0')
user.save()#保存到mysql
or:
user.update_user()#修改id爲1的數據
'''
if __name__ == '__main__':
    # type create hello_class
    # args1:要動態創建的class的名稱
    # args2:要動態創建的class的父類繼承,使用tuple表示,單繼承加,
    # args3:要動態創建的class的函數綁定,dict(函數名=已創建函數名,fn=fn)
    # h1 = type(args1,args2,args3)

    # 寫一個ORM映射框架,一行sql對應着對象的每個字段
    # 定義一個字段基類
    class Field(object):
        def __init__(self, field_name):
            self.field_name = field_name

        def __str__(self):
            print('nam=%s,type=%s' % (self.field_name))


    # 定義一個數據類型表,以下示例簡短爲只有三種Str,Int,Float
    # str
    class str_Field(Field):
        def __init__(self, field_name):
            super(str_Field, self).__init__(field_name)


    # int
    class int_Field(Field):
        def __init__(self, field_name):
            super(int_Field, self).__init__(field_name)


    # float
    class float_Field(Field):
        def __init__(self, field_name):
            super(float_Field, self).__init__(field_name)


    # 定義metaclass
    class ModelMetaclass(type):
        # 類對象,類名,繼承類的父類集合,方法集合
        def __new__(cls, name, base_cls, attrs):
            if name == 'Model':
                return type.__new__(cls, name, base_cls, attrs)
            mappings = dict()  # 空字典

            for func_name, func_type in attrs.items():
                if isinstance(func_type, Field):
                    mappings[func_name] = func_type

            for key in mappings.keys():
                attrs.pop(key)

            attrs['mappings'] = mappings
            attrs['table_name'] = name
            return type.__new__(cls, name, base_cls, attrs)


    class Model(dict, metaclass=ModelMetaclass):
        def __init__(self, **kwargs):
            # 調用dict的父類init
            super(Model, self).__init__(kwargs)

        def __getattr__(self, item):
            return self[item]

        def __setattr__(self, key, value):
            self[key] = value

        # 數據庫增加操作
        def save(self):
            fields = []
            args = []
            values = []
            table_name = self.table_name
            for k, v in self.mappings.items():
                fields.append(v.field_name)
                args.append(getattr(self, v.field_name, None))
                values.append('?')
            SQL = 'insert into %s(%s) values (%s)' % (table_name, ','.join(fields), ','.join(values))
            print('SQL: %s 運行' % SQL)
            print('參數列表args:%s' % args)

        def update_user(self, **kwargs):
            if kwargs: super(Model, self).__init__(kwargs)
            fields = []
            args = []
            values = []
            table_name = self.table_name

            for k, v in self.mappings.items():
                fields.append(v.field_name)
                args.append(getattr(self, v.field_name, None))
                values.append('?')
            #合併field值

            dicts_user = dict(zip(fields, values))
            SQL ='update %s set name=%s ,salary=%s where id = %s' %(table_name,dicts_user['name'],dicts_user['salary'],dicts_user['id'])
            print('SQL:%s 運行' %SQL)
            print('參數列表args:%s ' %args)
    # user entity
    class User(Model):

        id = int_Field('id')  # 編號
        name = str_Field('name')  # 姓名
        salary = float_Field('salary')  # 薪水

        def __init__(self, **kwargs):
            if kwargs:
                try:

                    kwargs1 = {'id': kwargs['id'], 'name': kwargs['name'], 'salary': kwargs['salary']}
                    super(User, self).__init__(**kwargs1)
                except:
                    raise

    #修改,可以不傳參數,可以在初始化時傳完
    user = User()
    user.update_user(id=123,name=234,salary=345)
    user.save()


















增加和修改的運行結果:

結尾:這樣一個精簡的ORM實例就完成了,metaclass還是很強大的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章