変更できない2つのクラス(WorkingとReturnStatementと呼びます)がありますが、両方をログで拡張したいと思います。トリックは、WorkingのメソッドがReturnStatementオブジェクトを返すため、新しいMutantWorkingオブジェクトもMutantReturnStatementにキャストできない限りReturnStatementを返すということです。コードで言う:
# 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で問題を解決できるかどうかも知りたいです。機能するソリューションが得られない限り、答えを受け入れることができないので、受け入れられるように機能するコードを書いてください。
他の回答がすでに説明したように、キャストはありません。 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)
更新:ログをクラスのすべてのメソッドに適用します
あなたが今、具体的にこれを求めたように。あなたはcanすべてのメンバーをループし、それらすべてにロギングを適用します。ただし、変更するメンバーの種類に関するルールを定義する必要があります。次の例では、名前に__が含まれるメソッドを除外しています。
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)
Pythonには「キャスティング」はありません。クラスのサブクラスは、その親のインスタンスと見なされます。スーパークラスメソッドを適切に呼び出し、クラス属性をオーバーライドすることにより、望ましい動作を実現できます。
この例でできることは、スーパークラスを受け取り、その関連属性をコピーするサブクラス初期化子を用意する必要があることです。つまり、MutantReturnステートメントは次のように書くことができます。
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つ以上ある場合など、self.attr = other.attr
メソッドに__init__
行を多く含まないようにするPythonicの方法があります。他のインスタンスの__dict__
属性をコピーします。
または、何をしているのかわかっている場合の場合、ターゲットオブジェクトの__class__
属性を目的のクラスに変更することもできますが、これは誤解を招き、微妙なエラーにつながる可能性があります(サブクラスの__init__
メソッドが呼び出されない、Pythonで定義されていないクラスで機能しない、その他の考えられる問題)、私はこのアプローチを推奨しません-これは「キャスティング」ではなく、オブジェクトの変更をブルートフォースでイントロスペクションし、答えを完全に保つためにのみ含まれています:
class MutantWorking(Working):
def do(self):
print "I am wrapping Working."
result = Working.do(self)
result.__class__ = MutantReturnStatement
return result
繰り返しますが、これは機能するはずですが、機能しません。前者の方法を使用してください。
ちなみに、キャストを許可する他のOO=言語についてはあまり経験がありませんが、どの言語でもサブクラスにキャストできますか?理にかなっていますか?キャストのみ許可されていると思います親クラスに。
直接的な方法はありません。
次のように、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()
あなたがすることはキャストではなく、型変換です。それでも、次のようなものを書くことができます
_def cast_to(mytype: Type[any], obj: any):
if isinstance(obj, mytype):
return obj
else:
return mytype(obj)
class MutantReturnStatement(ReturnStatement):
def __init__(self, *args, **kwargs):
if isinstance(args[0], Working):
pass
# your custom logic here
# for the type conversion.
_
使用法:
_cast_to(MutantReturnStatement, Working()).act()
# or simply
MutantReturnStatement(Working()).act()
_
(例では、MutantReturnStatement
には.do()
メンバー関数がないことに注意してください。)
ここでキャストする必要はありません。あなたはただ必要です
class MutantWorking(Working):
def do(self):
print "I am wrapping Working."
Working().do()
return MutantReturnStatement()
これは明らかに正しいリターンと望ましいプリントアウトを与えます。