2011-12-13 66 views
2

我想動態添加類屬性,但不是在實例級別。例如。我可以做手工爲:python動態設置非實例類屬性

class Foo(object): 
    a = 1 
    b = 2 
    c = 3 

我希望能夠與做:

class Foo(object): 
    dct = {'a' : 1, 'b' : 2, 'c' : 3} 
    for key, val in dct.items(): 
     <update the Foo namespace here> 

我希望能夠從外面做這個沒有調用類該類(因此它是可移植的),或沒有額外的類/裝飾器。這可能嗎?

回答

4

從您的示例代碼判斷,您希望在創建類的同時執行此操作。在這種情況下,假設您使用CPython,則可以使用locals()

class Foo(object): 
    locals().update(a=1, b=2, c=3) 

這工作,因爲當正在定義的類,locals()指類的命名空間。它是特定於實現的行爲,可能不適用於更高版本的Python或其他實現。

下面顯示了使用類工廠的不太髒的hacky版本。基本的想法是,你的字典通過構造函數type()轉換爲類,然後將其用作新類的基類。爲了方便用最少的語法定義屬性,我使用了**約定來接受屬性。

def dicty(*bases, **attrs): 
    if not bases: 
     bases = (object,) 
    return type("<from dict>", bases, attrs) 

class Foo(dicty(a=1, b=2, c=3)): 
    pass 

# if you already have the dict, use unpacking 

dct = dict(a=1, b=2, c=3) 

class Foo(dicty(**dct)): 
    pass 

這實際上就是您自己調用type()的語法糖。這工作正常,例如:

class Foo(type("<none>", (object,), dict(a=1, b=2, c=3))): 
    pass 
+0

雖然這對當前的CPython有效,但[不保證可以在其他Python實現或CPython的未來版本上工作](http://docs.python.org/library/functions.html#locals) 。你不應該修改'locals()'返回的dicitonary。 (雖然修改'globals()'是好的,但至少可以保證工作。) –

+0

是的,很好的警告。 – kindall

+0

+1:這太棒了!比我提出的混亂的元類解決方案要好得多。如果可以的話,我會給出+10的推理(「指的是類名字空間」)。 TIL噸的東西。謝謝互聯網! –

2

你的意思是這樣的:

def update(obj, dct): 
    for key, val in dct.items(): 
     obj.setattr(key, val) 

然後只是去

update(Foo, {'a': 1, 'b': 2, 'c': 3}) 

這工作,因爲一類是隻是一個對象太;)

如果要移動一切進入課堂,然後試試這個:

class Foo(object): 
    __metaclass__ = lambda t, p, a: return type(t, p, a['dct']) 
    dct = {'a': 1, 'b': 2, 'c': 3} 

這將創建一個新類,成員在dct,但所有其他屬性將不存在 - 所以,您想要將最後一個參數更改爲type以包含所需的內容。我發現如何在這裏執行此操作:What is a metaclass in Python?

+0

這是一個味道問題,但我更喜歡使用類方法而不是函數。 – jcollado

+0

這兩者(使用setattr通過更新函數或classmethod)都很好,但他們需要在課程外部進行調用 - 爲了便於攜帶,如果可能,我寧願在課程的啓動階段完成所有操作 – gnr

+0

oh,ok,那麼我想我誤解了你。上面的解決方案工作得很好:代碼將在執行類定義時運行。這是設置類變量和方法的相同機制。 –

1

接受的答案是一個不錯的方法。然而,一個缺點是你最終的是不是真的有必要的MRO繼承鏈的另外父對象,甚至可能會造成混亂:

>>> Foo.__mro__ 
(<class '__main__.Foo'>, <class '__main__.<from dict>'>, <class 'object'>) 

另一種方法是使用一個裝飾。像這樣:

def dicty(**attrs): 
    def decorator(cls): 
     vars(cls).update(**attrs) 
     return cls 
    return decorator 

@dicty(**some_class_attr_namespace) 
class Foo(): 
    pass 

以這種方式,您可以避免繼承鏈中的其他對象。@decorator語法只是一個很好的說法:

Foo = dicty(a=1, b=2, c=3)(Foo) 
+0

可愛的方法。 – kindall