2012-04-05 17 views
6

我創建了一個python-packages/MyLibPackage,我將在我的項目中導入它。如何爲所有調用覆蓋導入的python類

MyLibPackage.____init____.py包括mymodiciation.py。此外,MyLibPackage文件夾包含另一個文件:base_classes.py(=外部項目)

mymodiciation.py進口「from base_classes import *」。

目標: 我可以導入MyLibPackage,它具有base_classes(= external project)中的所有類。 如果我需要修改某些類或函數,我可以在mymodiciation.py中覆蓋它。它有效,但我遇到了問題。例如:

我mymodiciation.py改寫這個類:

class Bookcollection(Bookcollection): 
    new_member = "lalala" 


class user(user): 
    def get_books(self): 
     return Bookcollection() 

如果我這樣做:

from MyLibPackage import * 
x = user() 
books = x.get_books() 

然後Bookcollection有屬性 「new_member」 的對象。好! 但是,如果我將做到這一點:

from MyLibPackage import * 
x = shelf() #this class is not overwritten and used also the object "Bookcolelction" 
books = x.get_books() 

那麼對象Bookcollection沒有屬性「new_member」,因爲他與MyLibPackage.base_classes.Bookcollection而不是與我的覆蓋類實例化MyLibPackage.mymodiciation.Bookcollection

我該怎麼說:如果我在mymodiciation中覆蓋了一個類,那麼儘管MyLibPackage.base_classes.shelf(get_books)是來自MyLibPackage.base_classes.shelf,MyLibPackage也必須使用它。

+0

如果你在繼承鏈中糾纏不清,那麼也許你應該看看其他解決方案。 – jldupont 2012-04-05 11:52:48

+0

也許我可以嘗試使用鉤子?! – 2012-04-05 12:27:28

回答

11

你想要做的就是所謂的「猴子修補」,並且與對象取向沒有多大關係。

Python確實支持它,但是你可以控制所有的類,你應該認真檢查你的項目來檢查你是否真的需要它。

也許使用Zope Component Architecture這樣的框架,它允許你用接口標記類,並提供適配器對象,這樣你就可以乾淨地使用一個對象,因爲它有一些沒有設計在第一位的接口,一個更好的主意。

也就是說,你要求的是在另一個模塊中改變它的位置,以便所有其他模塊都可以看到這些改變。

你可以這樣做:改變它所屬模塊中的類。在Python這是可以做到簡單地歸咎於你的新類所需的名稱,產地的模塊:

import base_classes 

class Bookcollection(base_classes.Bookcollection): 
    new_member = "lalala" 

base_classes.Bookcollection = Bookcollection 

(強烈建議,以避免「從X導入*」比單一腳本更大的任何項目 - 在這種情況下,您有兩個具有相同名稱的變量,以及整個代碼中的不同含義:例如,基類和繼承類,Python名稱空間允許您避免這種情況)。

因此,這將更改base_class模塊中的Bookcollection類 - 但僅適用於將從此處開始並在您的執行鏈上引用它的代碼。如果示例中的「x」類在「base_classes」模塊中定義,或者在導入「MyModule」之前定義的類,它將獲得對舊的「Bookcollection」類的引用。正如你所看到的,它可能很快變成一團糟,如果你真的選擇這種方法,保持你的項目甚至可用的唯一方法是進行單元測試來驗證你想要修補的所有類,實際上是修補的。正如你所看到的,即使模塊的導入順序也會有所作爲。如果您有測試地點,如果您按照打破猴子補丁的順序進行導入,它們將會中斷。

如果您只需要添加和替換現有類中的東西,您可以猴子修補類本身來替換它的組件,而不是猴子修補它所在的模塊以替換類。通過這種方式,模塊的進口秩序將沒有多大關係 - 它會影響甚至現有該類的實例:

import base_classes 

base_classes.Bookcollection.new_member = "lalala" 

def new_bookcol_method(self): 
     pass 

# to replace or register a method in the other class: 
base_classes.Bookcollection.__dict__["old_bookcol_method"] = new_bookcol_method 

這會給你一個更一致的行爲比試圖分配一個新的類(這是一個對象本身)與原始模塊中的同名。總而言之,您應該按照@jamesj的建議在他的答案中使用,並使用不同的類,或者如果您需要動態行爲,請使用可維護的框架,如Zope組件體系結構。不管你採取什麼方法,寫單元測試。

+0

謝謝你。解決了我的問題! – ehambright 2016-12-29 22:43:55

1

由於您遇到名稱空間衝突,所以具有相同名稱的類通常是一個壞主意。我會建議將您的MyLibPackage.base_classes.Bookcollection更改爲MyLibPackage.base_classes.BaseBookcollection或類似的。這應該按預期工作。

+0

但是當我需要只更改一些基類的某些部分時,它應該如何工作?例如一個函數。 base_class.py的其餘部分與他的命名空間一起工作。其餘base_classes中的調用不知道我的新派生類 – 2012-04-05 12:06:59

+0

好了,然後將派生類重命名爲BookcollectionImproved或者執行類似'from base_classes import Bookcollection as BaseBookcollection'! – phobie 2012-09-25 18:48:44