2013-04-06 8 views
5

関連:Python:インスタンスのクラスを変更するためのユースケースはありますか?

class Robe: 
    pass 

class Dress: 
    pass 

r = Robe() 
r.__class__ = Dress 

私はケースがどこ「核変換」のようなオブジェクトが存在するかどうかを把握しようとしている:Python object conversion

私は最近、Pythonはあなたがそうのようなインスタンスのクラスを変更することを可能にすることを学びましたこれは便利です。私はIDLEでこれを混乱させました。私が気づいたことは、別のクラスを割り当てることで、新しいクラスの__init__メソッドが呼び出されないということです。ただし、必要に応じて明示的に行うこともできます。

私が考えることができるほぼすべての使用事例は、構成によってうまくいくと思いますが、私はコードの新作ですので、私は何を知っていますか? ;)

答えて

6

あなたの例ではRobeDressのような無関係なクラスに対してこれを行うのは正当な理由はほとんどありません。ちょっとした作業がなければ、最終的に得られるオブジェクトが正常な状態にあることを保証することは難しいです。

ただし、非標準のファクトリ関数またはコンストラクタを使用してベースオブジェクトを構築する場合は、ベースクラスから継承すると便利です。ここでは例を示します。この例では

class Base(object): 
    pass 

def base_factory(): 
    return Base() # in real code, this would probably be something opaque 

def Derived(Base): 
    def __new__(cls): 
     self = base_factory()  # get an instance of Base 
     self.__class__ = Derived # and turn it into an instance of Derived 
     return self 

、派生クラスの__new__方法はBaseクラスのインスタンスを返すbase_factory方法を使用して、そのオブジェクトを構築したいと考えています。しばしば、この種の工場はどこかの図書館にあり、オブジェクトをどのように作っているのかは分かりません(同じ結果を得るには、Base()またはと呼んでください)。 Derived.__new__を呼び出した結果は、(そのような方法が存在する場合)、それはそれで呼び出さDerived.__init__メソッドを持っていることが保証さDerivedクラスのインスタンスになるよう

インスタンスの__class__属性が書き換えられています。

+0

この回答は非常に徹底しているようですが、私はプログラミングに新しいので、理解するのに苦労しています。 '__new__' /コンストラクタのアスペクトを無視して、どこかのライブラリに定義されたクラスを持ち、それを継承する独自のクラスを作成した場合、後者のインスタンスを作成することができます。前者は「変質する」。しかし、単に継承されたクラスをインスタンス化するだけではどうですか?私は、コンストラクタのコンセプトや '__new__'メソッドの機能を理解していないことを認めます。 – henrebotha

+1

@henrebotha:[Pythonデータモデル](http://docs.python.org/3/reference/datamodel.html#basic-customization)についてお読みください。クラスの '__new__'メソッドがインスタンスを構築するために呼び出されます。 'Derived()'を呼び出すと 'Derived .__ new__'が存在する場合はそれが呼び出されます。通常、 '__new__'は(' super'呼び出しを介して) 'object .__ new__'によって作成された新しいオブジェクトを返しますが、それを行う必要はありません。上記の例では、ファクトリ関数からインスタンスを取得してカスタマイズしました。 [この前の質問](http://stackoverflow.com/questions/15330755/construct-class-from-superclass-instance)も参照してください。 – Blckknght

2

私は、既存のオブジェクトをどのような種類のデータが保持されているかを認識した後に、そのオブジェクトを「アップグレード」するためにこれまでずっとこの手法を使用していたことを覚えています。それは実験的なXMPPクライアントの一部でした。 XMPPは、通信に多くの短いXMLメッセージ(「スタンザ」)を使用します。

アプリケーションがスタンザを受信すると、DOMツリーに解析されました。次に、アプリケーションは、それがどのようなスタンザ(プレゼンス・スタンザ、メッセージ、自動照会など)であるかを認識する必要がありました。たとえば、メッセージ・スタンザとして認識された場合、DOMオブジェクトは「get_author」、「get_body」などのメソッドを提供するサブクラスに「アップグレード」されました。

私はもちろん新しいクラスを作成することができます解析されたメッセージを表し、そのクラスの新しいオブジェクトを作成し、元のXML DOMオブジェクトから関連するデータをコピーします。しかし、オブジェクトのクラスをインプレースで変更することには2つの利点がありました。第1に、XMPPは非常に拡張性の高い標準であり、コードの他の部分がそこで有用なものを見つけた場合、またはデバッグ中に元のDOMオブジェクトに簡単にアクセスできるようにすることは有用でした。第2に、コードをプロファイリングすると、新しいオブジェクトを作成して明示的にデータをコピーすると、すぐに破棄されるオブジェクトを再利用するよりもはるかに遅いことがわかりました。その違いは多くのショートメッセージを使用するXMPPで問題になりました。

私はこれらの理由のいずれも、実際にCPythonで(それほど大きくない)高速化が必要な場合を除いて、このテクニックの使用を正当化するものではないと思います。これはちょうど実験的なアプリケーションでコードを少し短く、速くするのに役立つハックです。このテクニックは、非CPython実装のJITエンジンを簡単に壊して、コードを非常に遅くすることにも注意してください。

+0

JITコンパイラについて興味深い点があります。 – henrebotha

関連する問題