摘要:但一般情況下,我們使用類作為元類。那么,元類到底有什么用呢要你何用元類的主要目的是為了控制類的創(chuàng)建行為。當(dāng)然,有很多種做法,這里展示用元類的做法。當(dāng)你創(chuàng)建類時(shí),解釋器會(huì)調(diào)用元類來(lái)生成它,定義一個(gè)繼承自的普通類意味著調(diào)用來(lái)創(chuàng)建它。
元類
Python 中的元類(metaclass)是一個(gè)深度魔法,平時(shí)我們可能比較少接觸到元類,本文將通過(guò)一些簡(jiǎn)單的例子來(lái)理解這個(gè)魔法。
類也是對(duì)象在 Python 中,一切皆對(duì)象。字符串,列表,字典,函數(shù)是對(duì)象,類也是一個(gè)對(duì)象,因此你可以:
把類賦值給一個(gè)變量
把類作為函數(shù)參數(shù)進(jìn)行傳遞
把類作為函數(shù)的返回值
在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建類
看一個(gè)簡(jiǎn)單的例子:
class Foo(object): foo = True class Bar(object): bar = True def echo(cls): print cls def select(name): if name == "foo": return Foo # 返回值是一個(gè)類 if name == "bar": return Bar >>> echo(Foo) # 把類作為參數(shù)傳遞給函數(shù) echo熟悉又陌生的 type>>> cls = select("foo") # 函數(shù) select 的返回值是一個(gè)類,把它賦給變量 cls >>> cls __main__.Foo
在日常使用中,我們經(jīng)常使用 object 來(lái)派生一個(gè)類,事實(shí)上,在這種情況下,Python 解釋器會(huì)調(diào)用 type 來(lái)創(chuàng)建類。
這里,出現(xiàn)了 type,沒(méi)錯(cuò),是你知道的 type,我們經(jīng)常使用它來(lái)判斷一個(gè)對(duì)象的類型,比如:
class Foo(object): Foo = True >>> type(10)>>> type("hello") >>> type(Foo()) >>> type(Foo)
事實(shí)上,type 除了可以返回對(duì)象的類型,它還可以被用來(lái)動(dòng)態(tài)地創(chuàng)建類(對(duì)象)。下面,我們看幾個(gè)例子,來(lái)消化一下這句話。
使用 type 來(lái)創(chuàng)建類(對(duì)象)的方式如下:
最簡(jiǎn)單的情況type(類名, 父類的元組(針對(duì)繼承的情況,可以為空),包含屬性和方法的字典(名稱和值))
假設(shè)有下面的類:
class Foo(object): pass
現(xiàn)在,我們不使用 class 關(guān)鍵字來(lái)定義,而使用 type,如下:
Foo = type("Foo", (object, ), {}) # 使用 type 創(chuàng)建了一個(gè)類對(duì)象
上面兩種方式是等價(jià)的。我們看到,type 接收三個(gè)參數(shù):
第 1 個(gè)參數(shù)是字符串 "Foo",表示類名
第 2 個(gè)參數(shù)是元組 (object, ),表示所有的父類
第 3 個(gè)參數(shù)是字典,這里是一個(gè)空字典,表示沒(méi)有定義屬性和方法
在上面,我們使用 type() 創(chuàng)建了一個(gè)名為 Foo 的類,然后把它賦給了變量 Foo,我們當(dāng)然可以把它賦給其他變量,但是,此刻沒(méi)必要給自己找麻煩。
接著,我們看看使用:
>>> print Foo有屬性和方法的情況>>> print Foo() <__main__.Foo object at 0x10c34f250>
假設(shè)有下面的類:
class Foo(object): foo = True def greet(self): print "hello world" print self.foo
用 type 來(lái)創(chuàng)建這個(gè)類,如下:
def greet(self): print "hello world" print self.foo Foo = type("Foo", (object, ), {"foo": True, "greet": greet})
上面兩種方式的效果是一樣的,看下使用:
>>> f = Foo() >>> f.foo True >>> f.greet繼承的情況> >>> f.greet() hello world True
再來(lái)看看繼承的情況,假設(shè)有如下的父類:
class Base(object): pass
我們用 Base 派生一個(gè) Foo 類,如下:
class Foo(Base): foo = True
改用 type 來(lái)創(chuàng)建,如下:
Foo = type("Foo", (Base, ), {"foo": True})什么是元類(metaclass)
元類(metaclass)是用來(lái)創(chuàng)建類(對(duì)象)的可調(diào)用對(duì)象。這里的可調(diào)用對(duì)象可以是函數(shù)或者類等。但一般情況下,我們使用類作為元類。對(duì)于實(shí)例對(duì)象、類和元類,我們可以用下面的圖來(lái)描述:
類是實(shí)例對(duì)象的模板,元類是類的模板 +----------+ +----------+ +----------+ | | | | | | | | instance of | | instance of | | | instance +------------>+ class +------------>+ metaclass| | | | | | | | | | | | | +----------+ +----------+ +----------+
我們?cè)谇懊媸褂昧?type 來(lái)創(chuàng)建類(對(duì)象),事實(shí)上,type 就是一個(gè)元類。
那么,元類到底有什么用呢?要你何用...
元類的主要目的是為了控制類的創(chuàng)建行為。我們還是先來(lái)看看一些例子,以消化這句話。
元類的使用先從一個(gè)簡(jiǎn)單的例子開(kāi)始,假設(shè)有下面的類:
class Foo(object): name = "foo" def bar(self): print "bar"
現(xiàn)在我們想給這個(gè)類的方法和屬性名稱前面加上 my_ 前綴,即 name 變成 my_name,bar 變成 my_bar,另外,我們還想加一個(gè) echo 方法。當(dāng)然,有很多種做法,這里展示用元類的做法。
1.首先,定義一個(gè)元類,按照默認(rèn)習(xí)慣,類名以 Metaclass 結(jié)尾,代碼如下:
class PrefixMetaclass(type): def __new__(cls, name, bases, attrs): # 給所有屬性和方法前面加上前綴 my_ _attrs = (("my_" + name, value) for name, value in attrs.items()) _attrs = dict((name, value) for name, value in _attrs) # 轉(zhuǎn)化為字典 _attrs["echo"] = lambda self, phrase: phrase # 增加了一個(gè) echo 方法 return type.__new__(cls, name, bases, _attrs) # 返回創(chuàng)建后的類
上面的代碼有幾個(gè)需要注意的點(diǎn):
PrefixMetaClass 從 type 繼承,這是因?yàn)?PrefixMetaclass 是用來(lái)創(chuàng)建類的
__new__ 是在 __init__ 之前被調(diào)用的特殊方法,它用來(lái)創(chuàng)建對(duì)象并返回創(chuàng)建后的對(duì)象,對(duì)它的參數(shù)解釋如下:
cls:當(dāng)前準(zhǔn)備創(chuàng)建的類
name:類的名字
bases:類的父類集合
attrs:類的屬性和方法,是一個(gè)字典
2.接著,我們需要指示 Foo 使用 PrefixMetaclass 來(lái)定制類。
在 Python2 中,我們只需在 Foo 中加一個(gè) __metaclass__ 的屬性,如下:
class Foo(object): __metaclass__ = PrefixMetaclass name = "foo" def bar(self): print "bar"
在 Python3 中,這樣做:
class Foo(metaclass=PrefixMetaclass): name = "foo" def bar(self): print "bar"
現(xiàn)在,讓我們看看使用:
>>> f = Foo() >>> f.name # name 屬性已經(jīng)被改變 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last)in () ----> 1 f.name AttributeError: "Foo" object has no attribute "name" >>> >>> f.my_name "foo" >>> f.my_bar() bar >>> f.echo("hello") "hello"
可以看到,F(xiàn)oo 原來(lái)的屬性 name 已經(jīng)變成了 my_name,而方法 bar 也變成了 my_bar,這就是元類的魔法。
再來(lái)看一個(gè)繼承的例子,下面是完整的代碼:
class PrefixMetaclass(type): def __new__(cls, name, bases, attrs): # 給所有屬性和方法前面加上前綴 my_ _attrs = (("my_" + name, value) for name, value in attrs.items()) _attrs = dict((name, value) for name, value in _attrs) # 轉(zhuǎn)化為字典 _attrs["echo"] = lambda self, phrase: phrase # 增加了一個(gè) echo 方法 return type.__new__(cls, name, bases, _attrs) class Foo(object): __metaclass__ = PrefixMetaclass # 注意跟 Python3 的寫法有所區(qū)別 name = "foo" def bar(self): print "bar" class Bar(Foo): prop = "bar"
其中,PrefixMetaclass 和 Foo 跟前面的定義是一樣的,只是新增了 Bar,它繼承自 Foo。先讓我們看看使用:
>>> b = Bar() >>> b.prop # 發(fā)現(xiàn)沒(méi)這個(gè)屬性 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last)in () ----> 1 b.prop AttributeError: "Bar" object has no attribute "prop" >>> b.my_prop "bar" >>> b.my_name "foo" >>> b.my_bar() bar >>> b.echo("hello") "hello"
我們發(fā)現(xiàn),Bar 沒(méi)有 prop 這個(gè)屬性,但是有 my_prop 這個(gè)屬性,這是為什么呢?
原來(lái),當(dāng)我們定義 class Bar(Foo) 時(shí),Python 會(huì)首先在當(dāng)前類,即 Bar 中尋找 __metaclass__,如果沒(méi)有找到,就會(huì)在父類 Foo 中尋找 __metaclass__,如果找不到,就繼續(xù)在 Foo 的父類尋找,如此繼續(xù)下去,如果在任何父類都找不到 __metaclass__,就會(huì)到模塊層次中尋找,如果還是找不到,就會(huì)用 type 來(lái)創(chuàng)建這個(gè)類。
這里,我們?cè)?Foo 找到了 __metaclass__,Python 會(huì)使用 PrefixMetaclass 來(lái)創(chuàng)建 Bar,也就是說(shuō),元類會(huì)隱式地繼承到子類,雖然沒(méi)有顯示地在子類使用 __metaclass__,這也解釋了為什么 Bar 的 prop 屬性被動(dòng)態(tài)修改成了 my_prop。
寫到這里,不知道你理解元類了沒(méi)?希望理解了,如果沒(méi)理解,就多看幾遍吧~
小結(jié)在 Python 中,類也是一個(gè)對(duì)象。
類創(chuàng)建實(shí)例,元類創(chuàng)建類。
當(dāng)你創(chuàng)建類時(shí),解釋器會(huì)調(diào)用元類來(lái)生成它,定義一個(gè)繼承自 object 的普通類意味著調(diào)用 type 來(lái)創(chuàng)建它。
參考資料本文由 funhacks 發(fā)表于個(gè)人博客,采用 Creative Commons BY-NC-ND 4.0(自由轉(zhuǎn)載-保持署名-非商用-禁止演繹)協(xié)議發(fā)布。
非商業(yè)轉(zhuǎn)載請(qǐng)注明作者及出處。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者本人。
本文標(biāo)題為: Python: 陌生的 metaclass
本文鏈接為: https://funhacks.net/2016/11/...
oop - What is a metaclass in Python? - Stack Overflow
深刻理解Python中的元類(metaclass) - 伯樂(lè)在線
使用元類 - 廖雪峰的官方網(wǎng)站
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/44251.html
摘要:但我并不是一個(gè)翻譯者并不會(huì)嚴(yán)格遵守每行每句的翻譯有時(shí)候我會(huì)將表述換個(gè)順序省略一些我認(rèn)為無(wú)關(guān)緊要的話,以便讀者更好理解。類也是對(duì)象在理解之前,我們先要掌握中的類是什么。這個(gè)對(duì)象類自身?yè)碛挟a(chǎn)生對(duì)象實(shí)例的能力。不過(guò)這樣做是為了保持向后兼容性。 前言 這篇博客是我在stackoverflow上看了一個(gè)提問(wèn)回復(fù)后寫的,例子基本用的都是e-satis本人的例子,語(yǔ)言組織也基本按照翻譯來(lái)。 但我并不...
摘要:先簡(jiǎn)單介紹下中的元類。元類就是創(chuàng)建類的類,對(duì)于元類來(lái)說(shuō),類是它的實(shí)例,將返回。中的所有類,都是的實(shí)例,換句話說(shuō),是元類的基類。 我在看源代碼的時(shí)候,經(jīng)常蹦出這一句:How does it work!竟然有這種操作?本系列文章,試圖剖析代碼中發(fā)生的魔法。順便作為自己的閱讀筆記,以作提高。 先簡(jiǎn)單介紹下Python中的元類(metaclass)。元類就是創(chuàng)建類的類,對(duì)于元類來(lái)說(shuō),類是它的實(shí)...
摘要:原鏈接中的元類是什么類也是對(duì)象在理解元類之前,需要掌握中類概念。事實(shí)上,是中用于創(chuàng)建所有類的元類。類本身是元類的對(duì)象在中,除了,一切皆對(duì)象,一切都是類或者元類的對(duì)象。事實(shí)上是自己的元類, 學(xué)習(xí)契機(jī) 項(xiàng)目中使用Elasticsearch(ES)存儲(chǔ)海量業(yè)務(wù)數(shù)據(jù),基于ES向外提供的API進(jìn)一層封裝,按需處理原始數(shù)據(jù)提供更精確、更多樣化的結(jié)果。在研究這一層的代碼時(shí)接觸到@six.add_me...
摘要:什么是元類剛才說(shuō)了,元類就是創(chuàng)建類的類。類上面的屬性,相信愿意了解元類細(xì)節(jié)的盆友,都肯定見(jiàn)過(guò)這個(gè)東西,而且為之好奇。使用了這個(gè)魔法方法就意味著就會(huì)用指定的元類來(lái)創(chuàng)建類了。深刻理解中的元類 (一) python中的類 今天看到一篇好文,然后結(jié)合自己的情況總結(jié)一波。這里討論的python類,都基于python2.7x以及繼承于object的新式類進(jìn)行討論。 首先在python中,所有東西都...
摘要:最前面那個(gè),解釋器實(shí)際的流程是解析這段代碼,得知它需要?jiǎng)?chuàng)建一個(gè)類對(duì)象,這個(gè)類的名字叫做它的父類列表用表示是,它的屬性用一個(gè)來(lái)表示就是。解決方法很簡(jiǎn)單關(guān)鍵就是前面被特別標(biāo)記了的應(yīng)當(dāng)返回這個(gè)的父類的方法返回的對(duì)象。 (原發(fā)于我的blog:Python: metaclass小記 ) 友情提示:本文不一定適合閱讀,如果執(zhí)意要讀,請(qǐng)備好暈車藥。 題記 Metaclasses are deepe...
閱讀 2237·2021-09-07 10:24
閱讀 2199·2019-08-30 15:55
閱讀 2202·2019-08-30 15:43
閱讀 763·2019-08-29 15:25
閱讀 1189·2019-08-29 12:19
閱讀 2025·2019-08-23 18:32
閱讀 1632·2019-08-23 17:59
閱讀 1042·2019-08-23 12:22