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