2012-02-02 13 views
10

私は変更できない2つのクラス(WorkingとReturnStatementと呼ぶ)を持っていますが、両方をロギングで拡張したいと思います。トリックは、WorkingのメソッドがReturnStatementオブジェクトを返すので、新しいMutantWorkingオブジェクトはMutantReturnStatementにキャストできない限り、ReturnStatementも返します。コードと言う: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では存在しません。 –

+0

また、 "ReturnStatement()。act()' "を実行すべきではありません。 - 他のクラスが現在のオブジェクトで動作する必要がある場合は、' returnStatement.act(self) 'を実行するか、現在のオブジェクトのインスタンスを必要としない場合は、classmethodまたはstaticmethodとして宣言します。 – jsbueno

+0

Working.do()は、ReturnStatementを返します。私はMutantWorking.do()がMutantReturnStatementを返すようにします。私はキャストがPythonでは存在しないことを知っていますが、問題はあります。解決策はありますか? – Visko

答えて

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

+0

私の最初の熱意の後、私は悲しいことに、これは私の問題の解決策ではないことに気付きました。 ReturnStatementではなくMutantReturnStatementを返すためにデコレータを使用してWorkingクラスを変更することはできません。これは初期状態(Working can not be modified)だったからです。 – Visko

+0

@ヴィスコ、私はあなたが私の答えを誤解したと思う。私の 'loghello'はまさにあなたが求めている 'MutantWorking'です。私は 'こんにちは'を修正しなかったのと同じように、あなたは「働く」を修正する必要はありません。これがまだ不明であると思われる場合、または同意しない場合は私の回答を更新することができます。 –

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()) 

(3 :-)以上のように、)たくさんある場合__init__方法にself.attr = other.attrラインの多くを持っていないためにPython的な方法がありますが、コピーしたい属性 - そのうちの1つは、他のインスタンスの__dict__属性を単にコピーするだけです。また

あなたはをやっていることを知っていれば、あなたは、単に目的のクラスにあなたのターゲットオブジェクトの__class__属性を変更することができます - しかし、それは誤解を招くことと微妙なエラーにあなたを運ぶことができる(の__init__方法サブクラスは呼び出されず、非Pythonで定義されたクラスでは動作しません)、私はこのアプローチを推奨しません - これは "キャスティング"ではなく、イントロスペクションを使用してオブジェクトの変更をブルートフォース答えを保持するために含まれています:

もう一度 - これはうまくいくはずですが、 rmerメソッド。

ところで、私は他のOO言語でもキャストを許していませんが、どの言語でも許可されているサブクラスにキャストしています。それは理にかなっていますか?キャストは親クラスだけに許されていると思う。

+0

コードで回答を投稿してください。それ以外の場合、私はあなたが私の質問に答えたとは思わない。 – Visko

+0

@jsbueno、はいあなたcan *任意のオブジェクトから任意のオブジェクトへのキャストを定義することができます例えばC++。 –

+0

私は、非突然変異クラスの属性を必ずしも知っているとは限りません。 PythonにインポートされたC++ライブラリであるとしましょう。 – Visko

1

直接的な方法はありません。

あなたはこのようなMutantReturnStatementののinit定義することができます。

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

+0

私はあなたの答えが好きです!誰も他の(より良い)テクニックを知らないなら、私はこれを受け入れるでしょう:)。 – Visko

+0

+1。 @Visko、あなたが何か好きなら+1。 (複数回行うこともできます)また、デコレータを使用すると、メソッドごとにそのようなコードブロックを1つ作成する必要なく、これを行うことができます。 –

0

」ここに鋳造する必要があります。必要なのは

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

これは明らかに適切な戻り値と望ましい出力値を与えます。

+0

私は同じ状態オブジェクト(プロパティは私の例では単純さを保つためには含まれていません)で返さなければならないので、良い解決策ではありません。これは私が望んでいない新しいインスタンスを返します。 – Visko

関連する問題