2015-05-13 43 views
2

讓我們有一個元類FieldModelMetaclass稱爲fields.py外部模塊,改變FieldModel類:是否可以從外部模塊修改元類?

# module fields.py 
class FieldModelMetaclass(type): 
    def __new__(cls, name, bases, attrs): 
     # code to alter class creation 

class FieldModel(object): 
    __metaclass__ = FieldModelMetaclass 
    print 'run at parse time BEFORE metaclass applies' 

# module consumer.py 
import fields 

def adjust_metaclass_new(cls, name, bases, attrs): 
    # do an extra work on class `cls' and attributes `attrs' 

# Is somehow possible from here alter `FieldModelMetaclass.__new__` 
# by injection of adjust_metaclass_new to modify FieldModel 
# before it gets instantiated ? 
mymodel = fields.FieldModel() 

據我所知,一類元類需要在效果編譯運行這樣的,當我做的那一刻導入定義的模塊,類已經受到影響。

如何從外部模塊創建類時攔截元類的效果?

+2

元類在運行時*生效,而不是編譯時。但是,由於它在同一個模塊中被使用*,因此在加載類語句之前,不能攔截元類。 –

+0

@MartijnPieters哦,是的,我現在看到:)當然:) –

+0

@MartijnPieters所以我留下來注入'Class .__ new__'方法,所以它會在每個類實例化? –

回答

3

在這種情況下,您不能輕易攔截MetaClass的使用。在導入模塊時(運行時,而不是編譯時),在class FieldModel正文完成後立即調用FieldModelMetaclass.__new__方法。

理論上,您可以使用sys.setrace() hook在創建元類之後但執行class語句之前注入額外的Python代碼,但這種方式的瘋狂在於。

另一個理論選擇是解析模塊源代碼,重寫它(使用ast module),但這又是一個更瘋狂的謊言。

您可以只需添加一個__call__方法來元類或__new__方法與元類創建的每個類,如果你想一切都攔截創建新實例:

import fields 

def metaclass__call__(cls, *args, **kw): 
    instance = super(FieldModelMetaclass, cls).__call__(*args, **kw) 
    # do something to the instance 
    return instance 

FieldModelMetaclass.__call__ = metaclass__call__ 

如果你的目標是改變FieldModelMetaclass生成類對象的方式,那麼最好的選擇是在導入後再次更改類對象。您始終可以爲該類添加更多屬性,或者使用您自己的元類將其替換爲不同的類。

+0

哦,謝謝你的回答。我無法想象在生產代碼中使用'settrace',因爲它會顯着降低運行時速度,因爲tracefunc在每個表達式被調用。用AST解析源代碼看起來更加瘋狂;)使用'.__ call__'方法的好主意,類似於'__new__'的用法,但是分離了一些東西。 –

+1

@DavidUnric:並具有額外的優勢,您可以在元類上設置它,並從它派生* *所有*類來使用它。 –

+0

作爲一個側面說明,在這裏,Ruby會佔上風,因爲重新打開和修改任何**類(不像Python的內置/擴展)是無限且簡單的。 –

相關問題