2011-02-16 296 views
3

我想構建一些繼承父類的類,它包含從其他父類繼承的子類。但是,當我更改任何子項中的子類中的屬性時,該更改會影響所有子類。我期望避免創建實例,因爲我稍後使用該功能。Python子類繼承

下面的代碼歸結了這個問題。最後一行顯示了意想不到的結果。

class SubclsParent(object): 
    a = "Hello" 

class Parent(object): 
    class Subcls(SubclsParent): 
     pass 

class Child1(Parent): 
    pass 

class Child2(Parent): 
    pass 

Child1.Subcls.a # Returns "Hello" 
Child2.Subcls.a # Returns "Hello" 
Child1.Subcls.a = "Goodbye" 
Child1.Subcls.a # Returns "Goodbye" 
Child2.Subcls.a # Returns "Goodbye"/Should still return "Hello"! 
+1

你正在處理類,而不是類的實例。那是你要的嗎? – robert

+5

「我期望避免創建實例,因爲我稍後使用該功能。」嚴重的錯誤觀念。請找一個關於面向對象設計的更好的教程。 –

回答

8

你所看到的行爲正是你應該期待的。當你定義一個類

>>> class Foo(object): pass 
... 

可以修改類 - 而不是它的實例,類本身 - 因爲類是另一個對象,存儲在變量foo中。所以,舉例來說,你可以獲取和設置類的屬性:

>>> Foo.a = 1 
>>> Foo.a 
1 

換句話說,在class關鍵字創建一個新類型的對象和綁定指定名稱到該對象。現在


,如果你定義一個類裏面另一個類(這是做的,順便說一個奇怪的事情),即相當於定義的類體中的局部變量。而且你知道在一個類的內部定義局部變量是什麼:它將它們設置爲類屬性。換句話說,本地定義的變量存儲在類對象中,而不是存儲在單個實例中。因此,

>>> class Foo(object): 
...  class Bar(object): pass 
... 

定義了一類Foo與一種類屬性,Bar,這恰好本身是一類。儘管如此,這裏沒有任何子類化 - 類別FooBar是完全獨立的。(你已經實現可複製的行爲如下:

>>> class Foo(object): 
...  class Bar(object): pass 
... 
>>> class Foo(object): pass 
... 
>>> class Bar(object): pass 
... 
>>> Foo.Bar = Bar 

。)

所以你總是修改同一個變量!當然,你會改變你看到的價值;你已經改變了他們自己!


你的問題似乎是你在實例和類屬性之間有些混淆,它們根本不是一回事。

A 屬性是一個定義在整個類別上的變量。也就是說,該類的任何實例都將共享相同的變量。例如,大多數方法都是類屬性,因爲您希望在每個實例(通常)上調用相同的方法。您還可以使用類屬性來處理全局計數器(您已實例化此類的次數?)以及應該在實例之間共享的其他屬性。

一個實例屬性是一個類的實例特有的變量。也就是說,每個實例都有一個不同的變量副本,可能有不同的內容。這是您將數據存儲在類中的位置 - 如果您有Page類,那麼您希望contents屬性按每個實例存儲,因爲不同的Page s當然需要不同的contents

在你的例子中,你想要Child1.Subcls.aChild2.Subcls.a不同的變量。自然,那麼,他們應該依賴於實例!


這可能有點信仰的飛躍,但是你想在Python中實現Java風格的接口?換句話說,你是否試圖指定所具有的屬性和方法,而不實際定義這些屬性?

這被認爲是非pythonic的事情,因爲普遍的共識是你應該允許類做任何他們想做的事,並且捕獲當它們沒有定義需要的屬性時產生的異常或方法。然而,最近人們已經意識到接口實際上有時是一件好事,並且新的功能被添加到Python以允許:abstract base classes

3

試試這個

class SubclsParent(object): 
    def __init__(self): 
     self.a = "Hello" 

當你直接在類中定義SubclsParent.a,你將其定義爲靜態的。

+0

+1簡潔明瞭的答案。一般信息:Python調用「靜態字段」「類屬性」,一個方法也是類屬性,它恰好是可調用的。 – Skurmedel

+0

問題在於它需要我創建一個Subcls實例才能訪問'a' – Conor

+2

問題是,當python解釋器遍歷代碼並創建類時,變量a是一個引用到一個對象,一個「你好」字符串。當你創建子類時,它們都只是繼承了相同的靜態引用。內存中只有一個「hello」對象。你將不得不在每個子類上定義一個新的字符串對象或重寫這個對象。或者,如果你想成爲邪惡的方式,你真的,真的,不應該這樣做,導入複製模塊,並且當你定義子類時,定義a = copy.deepcopy(SubclsParent.a) –

1

當您使用Child1.Subcls時,python會看到沒有Child1.Subcls,因此會檢查Parent並找到它並將其返回。 Child2.Subcls發生同樣的事情。因此,這兩個表達式都指向同一個類。 Child1和Child2不會獲得它們自己的子類,而是可以訪問原始類。

* 我正在尋找避免必須創建實例,因爲我稍後使用該功能。 *

我不明白你在這裏的意思。

0

這可能是你真正想要的是metaclasses

+0

這不是元類的候選人。 OP甚至不理解類和類實例。 – Keith

+0

@Keith:我對這個問題的閱讀表明,他們確實從「稍後使用該功能」位理解了類實例。也許我錯了。 – nmichaels

+0

不幸的是,孩子的子類的元類將在孩子完全定義之前運行。我在大型方案中試圖做的是在我開始創建這些子類的實例之前,使用子類的元類來定義其子類的屬性(例如'a')。 (對不起,如果我的問題的措辭是低於標準) – Conor

1

你的問題是,當你訪問屬性時,你正在訪問父類中創建的所有引用相同變量的繼承例程。您可以創建這些實例變量,也可以在子類中創建屬性以獲取獨立屬性。