python meataclass详解
寫(xiě)在篇前
??這篇文章主要介紹python3中metaclass(元類(lèi))的概念及其應(yīng)用。簡(jiǎn)而言之,元類(lèi)就是創(chuàng)建類(lèi)的東西,而type就是Python的內(nèi)建元類(lèi)。如此一來(lái),在python中,先要定義元類(lèi),然后利用元類(lèi)創(chuàng)建類(lèi),最后根據(jù)類(lèi)創(chuàng)建實(shí)例。
一切皆為對(duì)象
??相信任何一個(gè)python愛(ài)好者,從業(yè)者都聽(tīng)過(guò)"在python中,一切皆為對(duì)象"這句話,但為什么探討元類(lèi)前要先拋出這個(gè)話題呢?因?yàn)槲矣X(jué)得只有當(dāng)你能夠理解元類(lèi)不僅是類(lèi)也是對(duì)象,那么這才算是真正理解了"一切皆為對(duì)象",基本數(shù)據(jù)類(lèi)型、集合、類(lèi)等在python中都是對(duì)象。
?
-
基本數(shù)據(jù)類(lèi)型、集合是對(duì)象
??基礎(chǔ)數(shù)據(jù)類(lèi)型如數(shù)值類(lèi)型int、float、bool、complex等都是對(duì)象,舉例:
>>> a = 1 >>> b = 1.0 >>> c = False >>> d = 1+2j>>> type(a) <class 'int'> >>> type(b) <class 'float'> >>> type(c) <class 'bool'> >>> type(d) <class 'complex'>??集合映射類(lèi)型如list、tuple、dict、set等也同樣是對(duì)象,舉例:
>>> a = [1,2] >>> b = (1,2) >>> c = {1:2} >>> d = {1,2,2} # 試著自己用type試試吧 # 注意使用type(a)和a.__class__是等效的;??而其他數(shù)據(jù)類(lèi)型如str、None毫無(wú)疑問(wèn),同樣也是對(duì)象,大家可以自己試試,嘗試他們的用法。
-
函數(shù)、類(lèi)是對(duì)象
??函數(shù)和類(lèi)在python中又被稱(chēng)為第一類(lèi)對(duì)象,第一類(lèi)對(duì)象具有以下特性:
- 可以賦值給一個(gè)變量
- 可以作為元素添加到集合對(duì)象中
- 可以作為參數(shù)值傳遞給函數(shù)
- 可以作為函數(shù)的返回值
舉個(gè)栗子,如函數(shù)可以賦值給一個(gè)變量,當(dāng)函數(shù)賦值給另外一個(gè)變量時(shí),函數(shù)并不會(huì)被調(diào)用,僅僅是在函數(shù)對(duì)象上綁定一個(gè)新的名字而已。
>>> def hello_world(): ... print('hello world!') ... >>> hello = hello_world >>> hello() hello world!??同理,你還可以把該函數(shù)賦值給更多的變量,唯一變化的是該函數(shù)對(duì)象的引用計(jì)數(shù)不斷地增加,本質(zhì)上這些變量最終指向的都是同一個(gè)函數(shù)對(duì)象。
>>> id(hello) 4440775400 >>> id(hello_world) 4440775400# 查看一個(gè)對(duì)象的引用計(jì)數(shù) >>>import sys >>> sys.getrefcount(hello_world) 3 # 可以思考一下為什么是3??容器對(duì)象(list、dict、set等)中可以存放任何對(duì)象,包括整數(shù)、字符串,當(dāng)然函數(shù)、類(lèi)也可以作存放到容器對(duì)象中,例如:
>>> class A(object): ... pass ... >>> class B(A): ... pass ... >>> list_cls = [A,B] >>> for cls in list_cls: ... print(cls) ... <class '__main__.A'> <class '__main__.B'>??而將函數(shù)、類(lèi)作為參數(shù)或返回函數(shù)(類(lèi))最經(jīng)典的栗子當(dāng)然是裝飾器啦~所以這里就不展開(kāi)舉例了,如果你有興趣,可以閱讀我之前的博客python裝飾器詳細(xì)剖析。
-
type、object也是對(duì)象
??python面向?qū)ο笾?#xff0c;type和object的關(guān)系可能是最難理解的部分,既然比較難理解,不如就從難以理解的地方開(kāi)始,請(qǐng)看以下代碼:
>>> isinstance(type,object) True >>> isinstance(object,type) True??我想此時(shí)此刻,基本上的人都蒙圈了,what f**k?! type是object類(lèi)的實(shí)例,object是type類(lèi)的實(shí)例,這好像有點(diǎn)反人類(lèi)啊!Anyway,我們來(lái)慢慢理清它們之間的關(guān)系:
-
type和object是類(lèi)實(shí)例也是類(lèi)
首先看object,以下代碼說(shuō)明object對(duì)象既是object類(lèi)的實(shí)例,也是type類(lèi)的實(shí)例,并且在繼承關(guān)系上,object沒(méi)有父類(lèi)。
# object是作為一個(gè)對(duì)象 >>> object <class 'object'> >>> object.__class__ # 同type(object)<class 'type'> # 這個(gè)object類(lèi)又是 type類(lèi)的實(shí)例object是作為一個(gè)類(lèi)
>>> object.__bases__所以可知有以下關(guān)系
isinstance(object,type)True isinstance(object,object)True再看type,首先type也是一個(gè)類(lèi),同時(shí):
# type是作為一個(gè)對(duì)象>>> type<class 'type'>>>> type.__class__<class 'type'># type是作為一個(gè)類(lèi)>>> type.__bases__(<class 'object'>,)# 所以可知有以下關(guān)系>>> isinstance(type,object)True>>> isinstance(type,type)True綜上所述,object和type是實(shí)例對(duì)象也是類(lèi),其中object是type類(lèi)、object類(lèi)的實(shí)例,type是type類(lèi)、object類(lèi)的實(shí)例,即:
>>> isinstance(type,object)True>>> isinstance(type,type)True>>> isinstance(object,type)True>>> isinstance(object,object)True -
type繼承自object
issubclass(object,type)False issubclass(type,object)True可以看出,type作為一個(gè)類(lèi)時(shí),繼承自object類(lèi),這一點(diǎn)可以從python的源碼中得以窺見(jiàn):
class type(object):"""type(object_or_name, bases, dict)type(object) -> the object's typetype(name, bases, dict) -> a new type"""pass??在這個(gè)類(lèi)的注釋中也可以看到,type有三種用法,其中第二種是我們最為熟悉的,其返回對(duì)象的類(lèi)型,第一種動(dòng)態(tài)創(chuàng)建類(lèi)的方法和第三種元類(lèi)的使用方法將會(huì)在后面探討。
-
type和object都是python oop的頂級(jí)
??object和type就好比"雞生蛋、蛋生雞"的關(guān)系,type繼承自object,object是type的實(shí)例。怎么理解呢?在Python的世界中,在面向?qū)ο篌w系里面,存在兩種關(guān)系:
- 父子關(guān)系,即繼承關(guān)系,表現(xiàn)為子類(lèi)繼承于父類(lèi)。在python里要查看一個(gè)實(shí)例的父類(lèi),可以使用__bases__查看;
- 類(lèi)型實(shí)例關(guān)系,表現(xiàn)為某個(gè)類(lèi)型的實(shí)例化,如小青是一條蛇,在python里要查看一個(gè)實(shí)例的類(lèi)型,使用它的__class__屬性可以查看,或者使用type()函數(shù)查看。
??而object是父子關(guān)系的頂端,所有的數(shù)據(jù)類(lèi)型的父類(lèi)都是它;type是實(shí)例類(lèi)型關(guān)系的頂端,所有對(duì)象都是它的實(shí)例的。所以,type和object的關(guān)系如下圖(請(qǐng)?jiān)徫抑赡鄣氖指?#xff09;:
元類(lèi)初探
??上面我們知道了type和object分別是python OOP兩種不同關(guān)系的頂級(jí)。接下來(lái)我們繼續(xù)看看,在類(lèi)和對(duì)象的創(chuàng)建過(guò)程中,type和object分別扮演什么角色。我們知道,類(lèi)的定義通過(guò)class關(guān)鍵字申明,一個(gè)類(lèi)中可以有若干屬性、方法:
class A(object):def say_hello(self):print("Hello World!")??在上邊的例子中,類(lèi)Foo的創(chuàng)建過(guò)程中會(huì)執(zhí)行class語(yǔ)句,此時(shí):
首先確定元類(lèi),創(chuàng)建類(lèi)對(duì)象:
- 確定類(lèi)Foo的父類(lèi)是否有參數(shù)metaclass,如果沒(méi)有則下一步;
- 確定類(lèi)Foo的父類(lèi)的父類(lèi)是否有參數(shù)metaclass,如果沒(méi)有則下一步;
- 使用默認(rèn)元類(lèi)type(type的用法會(huì)在3中講解)
因?yàn)檫@里我們似乎并沒(méi)有定義什么元類(lèi),所以毫無(wú)疑問(wèn),會(huì)使用python默認(rèn)元類(lèi)type(type(object_or_name, bases, attrs))來(lái)創(chuàng)建類(lèi),其中object_or_name表示類(lèi)名、bases表示父類(lèi)、attrs表示屬性和方法,用字典形式傳入,所以以上類(lèi)的定義等效于以下過(guò)程:
>>> def say_hello(): ... print('Hello world!') ... >>> type('A', (object,),{'say_hello':say_hello})??通過(guò)type()函數(shù)創(chuàng)建的類(lèi)和直接寫(xiě)class是完全一樣的,因?yàn)镻ython解釋器遇到class定義時(shí),僅僅是掃描一下class定義的語(yǔ)法,然后調(diào)用type()函數(shù)創(chuàng)建出class。動(dòng)態(tài)語(yǔ)言本身支持運(yùn)行期動(dòng)態(tài)創(chuàng)建類(lèi),這和靜態(tài)語(yǔ)言有非常大的不同,要在靜態(tài)語(yǔ)言運(yùn)行期創(chuàng)建類(lèi),必須構(gòu)造源代碼字符串再調(diào)用編譯器,或者借助一些工具生成字節(jié)碼實(shí)現(xiàn),本質(zhì)上都是動(dòng)態(tài)編譯,會(huì)非常復(fù)雜。
2、根據(jù)創(chuàng)建的類(lèi)、創(chuàng)建對(duì)象
??我們知道,創(chuàng)建類(lèi)的時(shí)候已經(jīng)指明了類(lèi)的元類(lèi)是type、類(lèi)的父類(lèi)是object,因此此時(shí),創(chuàng)建對(duì)象時(shí)會(huì)先根據(jù)type創(chuàng)建一個(gè)對(duì)象,然后再繼承父類(lèi)object的屬性和方法。
元類(lèi)進(jìn)階
??再一次說(shuō)明實(shí)例、類(lèi)和元類(lèi)之間的關(guān)系(代碼如下),在python中,先要定義元類(lèi),然后利用元類(lèi)創(chuàng)建類(lèi),最后根據(jù)類(lèi)創(chuàng)建實(shí)例。
>>> foo.__class__ # <class 'Foo'> >>> Foo.__class__ # <class 'type'> >>> type.__class__ # <class 'type'>??前面提到,type是python默認(rèn)是元類(lèi),那么可不可以自定義元類(lèi)呢?答案當(dāng)然是肯定的。自定義元類(lèi),可以改變對(duì)象的創(chuàng)建方式,滿(mǎn)足一些特殊的需求。自定義的元類(lèi)創(chuàng)建方式如下:
class ListMetaclass(type):"""元類(lèi)就是重寫(xiě)新式類(lèi)的實(shí)際構(gòu)造方法__new__"""def __new__(cls, name, bases, attrs):attrs['add'] = lambda self, value: self.append(value)return super().__new__(cls, name, bases, attrs)# return type.__new__(cls, name, bases, attrs)# 注意理解以下輸出 >>> ListMetaclass.__class__ <class 'type'> >>> ListMetaclass.__bases__ (<class 'type'>,) >>> ListMetaclass.__mro__ (<class '__main__.ListMetaclass'>, <class 'type'>, <class 'object'>)??自定義元類(lèi)需要繼承type類(lèi)或則type類(lèi)的子類(lèi),然后再通過(guò)重寫(xiě)__new__方法,改變對(duì)象創(chuàng)建的方式。要使用自定義,只需要使用關(guān)鍵字metaclass指定元類(lèi)即可。
class MyList(list, metaclass=ListMetaclass):passl = MyList() l.add(1) print(l)注意:Python3中不再有__metaclass__屬性以及模塊級(jí)別的__metaclass__屬性
??這樣,Mylist作為list類(lèi)的子類(lèi),既繼承了list原有的方法append、insert等,又獲得了由元類(lèi)的創(chuàng)建的add方法。
元類(lèi)應(yīng)用
??元類(lèi)是一個(gè)很高深的東西,大多數(shù)場(chǎng)景我們并不需要它,這里必須要引用e-satis的一段話:
Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).
??關(guān)于元類(lèi)的應(yīng)用,最經(jīng)典的應(yīng)該屬于ORM,以下一個(gè)關(guān)于ORM的例子來(lái)自廖雪峰python教程,略有改變,首先我們來(lái)看看ORM的調(diào)用形式,知道結(jié)果再來(lái)一步步實(shí)現(xiàn)ORM。
# _orm.py from _orm_dataType import IntegerField, StringField from _orm_model import Modelclass User(Model):# 定義類(lèi)的屬性到列的映射:id = IntegerField('id')name = StringField('username')email = StringField('email')password = StringField('password')# 創(chuàng)建一個(gè)實(shí)例 u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 保存到數(shù)據(jù)庫(kù) u.save()# print(User.__bases__, # 父類(lèi) # User.__class__, # 元類(lèi) # User.__dict__, # 字典 # u.__class__ # 所屬類(lèi) # )??要實(shí)現(xiàn)該API調(diào)用,我們需要定義數(shù)據(jù)類(lèi)型IntergerField、StringField等等以及所有數(shù)據(jù)表的通用父類(lèi)Model類(lèi)。數(shù)據(jù)類(lèi)型的定義很簡(jiǎn)單,我們只指明數(shù)據(jù)類(lèi)型以及字段名就好:
#_orm_dataType.py class Field(object):def __init__(self, name, column_type):self.name = nameself.column_type = column_typedef __str__(self):return '<%s:%s>' % (self.__class__.__name__, self.name)class StringField(Field):def __init__(self, name):super().__init__(name, 'varchar(100)')class IntegerField(Field):def __init__(self, name):super().__init__(name, 'bigint')??接下來(lái)就是定義Model類(lèi),同時(shí)改變Model類(lèi)的元類(lèi),使得Model子類(lèi)的創(chuàng)建動(dòng)態(tài)改變,這里需要注意的是,Model類(lèi)繼承自dict類(lèi),并且通過(guò)__getattr__、__setattr__改變了屬性的默認(rèn)讀寫(xiě)方式。
# _orm_model.py from _orm_dataType import Fieldclass ModelMetaclass(type):def __new__(cls, name, bases, attrs):print('*******__new__*******')# print(name)# print(bases)# print(attrs)# print('*******__new__*******')if name == 'Model':return super().__new__(cls, name, bases, attrs)# 這里是創(chuàng)建類(lèi),不是創(chuàng)建對(duì)象mappings = dict()for k, v in attrs.items():if isinstance(v, Field):mappings[k] = vfor k in mappings.keys():attrs.pop(k)attrs['__mappings__'] = mappings # 保存屬性和列的映射關(guān)系attrs['__table__'] = name # 假設(shè)表名和類(lèi)名一致return super().__new__(cls, name, bases, attrs)# 這里是創(chuàng)建類(lèi),不是創(chuàng)建對(duì)象# 指定新的meataclass class Model(dict, metaclass=ModelMetaclass):def __init__(self, **kw):super().__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Model' object has no attribute '%s'" % key)def __setattr__(self, key, value):self[key] = valuedef 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(params))print('SQL: %s' % sql)print('ARGS: %s' % str(args))if __name__ == '__main__':m = Model()總結(jié)
以上是生活随笔為你收集整理的python meataclass详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: R语言入门3---R语言六大基本数据结构
- 下一篇: websocket python爬虫_p