外部ライブラリによって提供されるクラスがあります。このクラスのサブクラスを作成しました。元のクラスのインスタンスもあります。
インスタンスが既に持っているプロパティを変更せずに、このインスタンスをサブクラスのインスタンスに変換したいと考えています (サブクラスがオーバーライドするものを除く)。
次の解決策はうまくいくようです。
# This class comes from an external library. I don't (want) to control
# it, and I want to be open to changes that get made to the class
# by the library provider.
class Programmer(object):
def __init__(self,name):
self._name = name
def greet(self):
print "Hi, my name is %s." % self._name
def hard_work(self):
print "The garbage collector will take care of everything."
# This is my subclass.
class C_Programmer(Programmer):
def __init__(self, *args, **kwargs):
super(C_Programmer,self).__init__(*args, **kwargs)
self.learn_C()
def learn_C(self):
self._knowledge = ["malloc","free","pointer arithmetic","curly braces"]
def hard_work(self):
print "I'll have to remember " + " and ".join(self._knowledge) + "."
# The questionable thing: Reclassing a programmer.
@classmethod
def teach_C(cls, programmer):
programmer.__class__ = cls # <-- do I really want to do this?
programmer.learn_C()
joel = C_Programmer("Joel")
joel.greet()
joel.hard_work()
#>Hi, my name is Joel.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
jeff = Programmer("Jeff")
# We (or someone else) makes changes to the instance. The reclassing shouldn't
# overwrite these.
jeff._name = "Jeff A"
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>The garbage collector will take care of everything.
# Let magic happen.
C_Programmer.teach_C(jeff)
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
__class__ただし、特に魔法の再割り当てが正しく感じられないため、このソリューションに私が考えていなかった警告が含まれていないとは確信していません (トリプルネゲートで申し訳ありません) 。これが機能したとしても、これを行うにはもっとPython的な方法があるはずだと感じずにはいられません。
ある?
編集:回答ありがとうございます。ここに私が彼らから得たものがあります:
代入によってインスタンスを再分類するという考えは
__class__広く使用されているイディオムではありませんが、ほとんどの回答 (執筆時点で 6 人中 4 人) が有効なアプローチであると考えています。ある回答者 (ojrac による) は、「一見するとかなり奇妙だ」と述べていますが、これには同意します (それが質問をする理由でした)。1 つの回答 (Jason Baker によるもの、2 つの肯定的なコメントと投票) だけが、これを行うことを積極的に思いとどまらせましたが、一般的な手法よりもユースケースの例に基づいてそうしました。肯定的であろうとなかろうと、この方法で実際の技術的な問題を見つける答えはありません。小さな例外は、古いスタイルのクラスに注意するように言及している jls です。新しいスタイルのクラスを意識した C 拡張機能は、Python 自体と同じようにこのメソッドで問題ないと思います (後者が正しいと仮定します)。
これがどれほどPythonicであるかという質問に関しては、いくつかの肯定的な答えがありましたが、本当の理由は示されていません. Zen ( import this) を見ると、この場合の最も重要なルールは「明示的は暗黙的よりも優れている」ということだと思います。ただし、その規則がこのように再分類することに賛成か反対かはわかりません。
{has,get,set}attr魔法を使用する代わりにオブジェクトに明示的に変更を加えているため、使用はより明確に見えます。__class__ = newclass暗黙のうちに属性を変更するのではなく、「これは現在、クラス 'newclass' のオブジェクトです。別の動作を期待してください」と明示的に言うため、使用はより明確に見えますが、オブジェクトのユーザーは古いクラスの通常のオブジェクトを扱っていると信じています。
要約: 技術的な観点からは、この方法は問題ないようです。pythonicity の質問は、「はい」に偏っており、未回答のままです。
Mercurial プラグインの例は非常に強力なものであるため (また、私がまだ自分自身に尋ねたことのない質問にも回答しているため)、Martin Geisler の回答を受け入れました。ただし、pythonicity の質問について何らかの議論がある場合は、引き続き聞きたいと思います。今までありがとう。
PS 実際の使用例は、実行時に追加機能を拡張する必要がある UI データ コントロール オブジェクトです。ただし、質問は非常に一般的なものです。