2012-02-02 46 views
10

我有兩個類(讓我們稱它們爲Working和ReturnStatement),我不能修改,但我想用logging來擴展它們。技巧是Working的方法返回一個ReturnStatement對象,所以新的MutantWorking對象也會返回ReturnStatement,除非我可以將它轉換爲MutantReturnStatement。與代碼話說:如何在Python中投射物體

# these classes can't be changed 
class ReturnStatement(object): 
    def act(self): 
     print "I'm a ReturnStatement." 

class Working(object): 
    def do(self): 
     print "I am Working." 
     return ReturnStatement() 

# these classes should wrap the original ones 
class MutantReturnStatement(ReturnStatement): 
    def act(self): 
     print "I'm wrapping ReturnStatement." 
     return ReturnStatement().act() 

class MutantWorking(Working): 
    def do(self): 
     print "I am wrapping Working." 
     # !!! this is not working, I'd need that casting working !!! 
     return (MutantReturnStatement) Working().do() 

rs = MutantWorking().do() #I can use MutantWorking just like Working 
print "--" # just to separate output 
rs.act() #this must be MutantReturnState.act(), I need the overloaded method 

預期的結果:
我包裝工作。
我工作。
-
我在包裝ReturnStatement。
我是ReturnStatement。

是否可以解決問題?我也很好奇這個問題是否也可以在PHP中解決。除非我得到一個工作解決方案,否則我不能接受答案,所以請寫下工作代碼以便接受。

+6

「鑄造」在Python中不存在。 – 2012-02-02 12:38:26

+0

另外,你不應該這樣做''ReturnStatement()。act()'「 - 如果你想讓act方法在其他類上工作在當前對象上,可以執行'returnStatement.act(self)' - 或者標記它作爲類方法或靜態方法 - 如果它不需要當前對象的實例。 – jsbueno 2012-02-02 12:41:59

+0

Working.do()返回一個ReturnStatement。我想讓MutantWorking.do()返回一個MutantReturnStatement。我知道在Python中不存在轉換,但是問題確實存在。有解決方案嗎? – Visko 2012-02-02 12:43:44

回答

5

沒有鑄件作爲其他答案已經解釋。您可以使用修飾器來創建子類或創建具有額外功能的修改後的新類型。

下面是一個完整的示例(功勞How to make a chain of function decorators?)。你不需要修改你的原始類。在我的例子中,原來的類叫做Working。

# decorator for logging 
def logging(func): 
    def wrapper(*args, **kwargs): 
     print func.__name__, args, kwargs 
     res = func(*args, **kwargs) 
     return res 
    return wrapper 

# this is some example class you do not want to/can not modify 
class Working: 
    def Do(c): 
     print("I am working") 
    def pr(c,printit): # other example method 
     print(printit) 
    def bla(c):   # other example method 
     c.pr("saybla") 

# this is how to make a new class with some methods logged: 
class MutantWorking(Working): 
    pr=logging(Working.pr) 
    bla=logging(Working.bla) 
    Do=logging(Working.Do) 

h=MutantWorking() 
h.bla() 
h.pr("Working")             
h.Do() 

這將打印

h.bla() 
bla (<__main__.MutantWorking instance at 0xb776b78c>,) {} 
pr (<__main__.MutantWorking instance at 0xb776b78c>, 'saybla') {} 
saybla 

pr (<__main__.MutantWorking instance at 0xb776b78c>, 'Working') {} 
Working 

Do (<__main__.MutantWorking instance at 0xb776b78c>,) {} 
I am working 

另外,我想知道你爲什麼不能修改的類。你試過了嗎?因爲,作爲一個替代來製作一個子類,如果你覺得動態你可以幾乎總是修改老班到位:

Working.Do=logging(Working.Do) 
ReturnStatement.Act=logging(ReturnStatement.Act) 

更新:應用記錄到一個類的所有方法

正如你現在特意要求的那樣。你可以遍歷所有成員並將它們全部應用於日誌。但是您需要爲需要修改的成員定義規則。下面的例子排除了名稱中帶有__的任何方法。

import types 
def hasmethod(obj, name): 
    return hasattr(obj, name) and type(getattr(obj, name)) == types.MethodType 

def loggify(theclass): 
    for x in filter(lambda x:"__" not in x, dir(theclass)): 
    if hasmethod(theclass,x): 
     print(x) 
     setattr(theclass,x,logging(getattr(theclass,x))) 
    return theclass 

有了這一切,你必須做,使一類的新記錄的版本是:

@loggify 
class loggedWorker(Working): pass 

或修改現有類的地方:

loggify(Working) 
+1

我認爲你理解我的問題,至今這似乎是答案。謝謝! – Visko 2012-02-02 14:18:19

+0

在我最初的熱情之後,我很遺憾地意識到這不是我的問題的解決方案。我無法使用裝飾器修改Working類以返回MutantReturnStatement而不是ReturnStatement,因爲這是初始條件(Working無法修改)。 – Visko 2012-02-02 15:45:51

+0

@Visko,我想你誤解了我的答案。我的'loghello'正是您要求的'MutantWorking'。您不必修改'工作',就像我沒有修改'你好'一樣。如果你認爲這還不清楚,或者你不同意,我可以嘗試更新我的答案。 – 2012-02-02 16:03:54

2

Python中沒有「投射」。 類的任何子類都被認爲是其父項的一個實例。期望的行爲可以通過適當地調用超類方法並重寫類屬性來實現。

你可以在你的例子做的,是必須有接收超和副本及其相關屬性的子類初始化 - 所以,你MutantReturnstatement可以寫成這樣:

class MutantReturnStatement(ReturnStatement): 
    def __init__(self, previous_object=None): 
     if previous_object: 
      self.attribute = previous_object.attribute 
      # repeat for relevant attributes 
    def act(self): 
     print "I'm wrapping ReturnStatement." 
     return ReturnStatement().act() 

,然後改變你的MutantWorking類:

class MutantWorking(Working): 
    def do(self): 
     print "I am wrapping Working." 
     return MutantReturnStatement(Working().do()) 

有關於__init__方法不是有很多的self.attr = other.attr行,如果有大量的(例如,超過3 :-))Python的方式屬性要複製 - 其中最懶惰的只是複製其他實例的__dict__屬性。

另外,如果你知道你在做什麼,你也可以簡單地你的目標對象的__class__屬性更改爲所需的類 - 但這可能會產生誤導,並帶你到細微的錯誤(的的__init__方法子類不會被調用,不會在非python定義的類上工作,以及其他可能的問題),我不推薦這種方法 - 這不是「鑄造」,它是使用內省來強制對象更改,並且只是包括爲保持完整的答案:

class MutantWorking(Working): 
    def do(self): 
     print "I am wrapping Working." 
     result = Working.do(self) 
     result.__class__ = MutantReturnStatement 
     return result 

再次 - 這應該工作,但不這樣做 - 使用FO rmer方法。順便說一句,我對其他面向對象的語言不太熟悉,它允許投射 - 但是投射到任何語言都允許的子類中?是否有意義?我認爲只允許投票給家長。

+0

請問您可以在代碼中發佈答案嗎?否則,我不認爲你回答了我的問題。 – Visko 2012-02-02 12:50:17

+0

@jsbueno,你可以定義從任何對象到任何對象的轉換,例如C++。 – 2012-02-02 13:06:13

+0

我不一定知道非突變類的屬性。假設他們是導入到Python的C++庫。 – Visko 2012-02-02 14:02:41

1

沒有直接的辦法。

可以定義MutantReturnStatement的初始化這樣的:

def __init__(self, retStatement): 
    self.retStatement = retStatement 

,然後用它是這樣的:

class MutantWorking(Working): 
    def do(self): 
     print "I am wrapping Working." 
     # !!! this is not working, I'd need that casting working !!! 
     return MutantReturnStatement(Working().do()) 

,你應該得到從您的包裝繼承ReturnStatement去掉,這樣

class MutantReturnStatement(object): 
    def act(self): 
     print "I'm wrapping ReturnStatement." 
     return self.retStatement.act() 
+0

我是python新手,在代碼中可能會出現一些錯誤,但是它顯示了我的想法 – Jurlie 2012-02-02 12:49:06

+0

我喜歡你的答案!如果沒有人告訴另一種(更好的)技術,我會接受這個:)。 – Visko 2012-02-02 12:52:19

+0

+1。 @Visko,+1如果你喜歡什麼。 (你可以做多次)。另外,使用裝飾器,你可以做到這一點,而不需要爲每種方法創建一個這樣的代碼塊。 – 2012-02-02 13:04:26

0

你不」這裏需要投射。你只需要

class MutantWorking(Working): 
def do(self): 
    print "I am wrapping Working." 
    Working().do() 
    return MutantReturnStatement() 

這顯然會給出正確的回報和所需的打印輸出。

+0

這不是一個好的解決方案,因爲我必須返回一個相同的狀態對象(屬性不包括在我的示例中以保持簡單性)。這會返回一個新的實例,這不是我想要的。 – Visko 2012-02-02 13:49:27