2010-01-05 27 views
30

不能以任何其他方式使用元類可以做些什麼?什麼是Python元類有用嗎?

亞歷克斯馬爾泰利告訴說,有任務不能在這裏沒有元類實現Python metaclasses vs class decorators 我想知道哪些是?

+7

我記得我讀過類似這樣的關於元類的地方,鬆散地引用了這句話:「如果你要問什麼是它們有用的,那意味着你不需要它們」。 +1,有趣的問題。 – 2010-01-05 12:16:52

+3

我不確定「不能以其他方式完成」是一個有用的標準。幾乎所有事情都可以用許多不同的方式完成,這都是一個多麼簡單/自然的問題。 – 2010-01-05 12:23:47

+0

應該有一個 - 最好只有一個 - 明顯的方法來做到這一點。 :) – 2010-01-05 12:30:01

回答

18

元類是必不可少的,如果你想擁有類對象(而不是實例類對象的)配備了「特別定製的行爲」,因爲對象的行爲取決於對類型的對象特殊的方法,而類對象的類型就是元類的同義詞。例如,如果您想要一個類對象X,使得「print X」發出「Time is now 8:46 am」(上午8:46,或者更一般地說,當前時間),則這必定表示type(x) (AKA X的元類)有一個特殊的自定義方法 - 類似地(使用各種適用的特殊方法),如果要給諸如X + Y(其中X和Y都是類對象)或X[23](其中X,再次,是一個類對象),等等。

大多數其他自定義任務現在(在Python 2.6或更高版本中)更易於使用類裝飾器實現,該裝飾器可以在class語句結束後立即更改類對象。還有一些情況是不可行的,因爲如果它們有任何作用(例如,設定或改變__slots__),必須儘早進行改變。

在Python 3中,元類獲得了一點額外的實用性:元類現在可以選擇指定要在執行class語句正文期間填充的映射對象(默認情況下,它是正常的dict)。這允許保留和使用類體中名稱綁定的訂單(而正常的dict失去了訂單),當類必須具有特定順序的「字段」時(例如映射1: 1到C struct,CSV文件或數據庫表中的一行等) - 在Python 2中。*必須重複指定(通常帶有額外的類屬性,這是一個序列,因此保持順序),而Python 3元類的這一特性允許刪除冗餘。

10

添加額外的靈活性,以你的編程:

但根據本Metaclass programming in Python你可能並不需要它們(還)

元類魔法更深比99%的用戶應該永遠不用擔心。如果你想知道你是否需要他們,你就不會(實際需要他們的人確切地知道他們需要他們,並且不需要爲什麼解釋)。

- Python高手蒂姆·彼得斯

+15

我還想知道。 – 2010-01-05 12:24:42

+0

思考面向方面的編程。我發佈的鏈接包含一個全面的解釋和代碼示例。 – OscarRyz 2010-01-05 12:38:21

+0

我在發佈問題之前閱讀它,並且只有1個示例。 – 2010-01-05 13:30:01

1

他們很少用到,但派上用場當你想添加行爲對象的基本行爲的地方 - 比較面向方面編程,或者像Hibernate這樣的持久化框架中完成的工具。

例如,您可能需要一個持久存儲或記錄每個新對象的類。

+2

這不能用裝飾器完成嗎? – 2010-01-05 12:22:32

4

如果您正在尋找使用元類機制的示例,您可以閱讀django.forms的源代碼。

表單定義的聲明式樣式是通過元類實現的。

1

也許沒有什麼東西可以完全用元類來完成,但對於某些人(包括我的),這是一個您可以使用的有趣工具。只是要小心不要濫用,因爲它可能會很棘手。

例如,我在最近的一個項目中使用了元編程。這是一張OpenOffice計算表,它使用一些pyUNO宏來生成一些帶有信息的文件。有一張表格向用戶展示要填充的信息,其他表格可以用來描述元素的種類及其屬性。然後用戶可以選擇元素的數量和類型,並生成文件。 該宏將通過元編程創建一個類,在每個工作表上進行配置。然後,用戶可以實例化每個類並生成對象。

它可以在沒有元編程的情況下完成,但對我來說,使用元編程功能來做它似乎很自然。

1

看看Django的源代碼 - 例如元類用於生成模型。

http://code.djangoproject.com/wiki/DynamicModels

內部,Django使用元類基於一個類你 在你的源代碼提供 創建模型。如果沒有 進入太多細節,這 意味着,而不是你的類 是實際的模型,Django的 收到你的類, 它用來建立在其 代替模型的描述。

8

我使用具有某種頻率的元類,它們是工具箱中非常強大的工具。有時候,對於問題的解決方案可以更優雅,更少代碼,而不是沒有代碼。

我發現自己最經常使用元類的事情是在類創建期間後處理類屬性。例如,設置在對象name屬性在適當情況下(如Django的ORM是如何發揮作用):

class AutonamingType(type): 
    def __init__(cls, name, bases, attrs): 
     for k,v in attrs.iteritems(): 
      if getattr(v, '__autoname__', False): 
       v.name = k 

class Autonamer(object): 
    __metaclass__ = AutonamingType 

如果你有這個作爲一個工具,您使用的是類之前,必須知道它的name可以do_something()

class Foo(object): 
    __autoname__ = True 
    def __init__(self, name=None): 
     self.name = name 
    def do_something(self): 
     if self.name is None: 
      raise ValueError('name is None') 
     # now, do something 

它可以使這之間在你的代碼的其餘部分的區別:和

class Bar(object): 
    myfoo1 = Foo('myfoo1') 
    myfoo2 = Foo('myfoo2') 
    myfoo3 = Foo('myfoo3') 

此:

class Baaz(Autonamer): 
    myfoo1 = Foo() 
    myfoo2 = Foo() 
    myfoo3 = Foo() 

因此減少重複(和變量名和分配的名稱可能會走出不同步的機會)。

0

第一評論員指出,使用metaclasses here是'寶石',幫助他追蹤(多)'天'發生的意外錯誤。