2017-11-26 8 views
1

私はインスタンス化できない基底クラスを持っています(私の簡略化した例ではBaseFruit)、同じメソッドを共有するいくつかの派生クラス(例えばApple) (printfuture)。しかしながら、それらは多くの可能な変形であり、どのような変形が使用されるべきかをユーザに選択させたい(例えば、sadfutureおよびsaddestfuture)。Pythonのメタベースクラスにユーザーが選択したメソッドを追加

私のprintfutureメソッドは私の派生クラスすべてに共通しているので、私は基本クラスの__new__メソッドを使ってバリアントのユーザー引数をキャッチし、そのメソッドを基本クラス自体に割り当てることに充当されると考えました。

# my custom methods 

def sadfuture(self): 
    """A sad future""" 
    print 'It looks {}!'.format(self.aspect) 
    print 'Mmm yummy !' 

def saddestfuture(self): 
    """A saddest future""" 
    print 'It looks {}'.format(self.aspect) 
    print 'Garbage !' 

# my base class 

class BaseFruit(object): 
    """Fruit base class""" 
    def __new__(cls, *args, **kwargs): 
     setattr(cls, 'printfuture', kwargs['usermethod']) 
     return object.__new__(cls) 

# My class 

class Apple(BaseFruit): 
    """An apple class""" 
    def __init__(self, aspect, usermethod=sadfuture): 
     self.aspect = aspect 


if __name__ == '__main__': 
    goodapple = Apple(aspect='delicious', usermethod=sadfuture) 
    goodapple.printfuture() # ==> ok 
    badapple = Apple(aspect='rotten', usermethod=saddestfuture) 
    goodapple.printfuture() # ==> not ok anymore 
    badapple.printfuture() # ==> ok 

出力します:

It looks delicious! 
Mmm yummy ! 
It looks delicious! 
Mmm yummy ! 
It looks rotten 
Garbage ! 

は私が私の基本クラスと私の最初のを上書きしていることを理解しない:代わりに期待される動作の

It looks delicious! 
Mmm yummy ! 
It looks delicious 
Garbage ! 
It looks rotten 
Garbage ! 

を以下の例に書かれたようオブジェクトがその動作を変更しました。だから、私の主な質問は:どのように私は期待された動作を達成することができますか?

このような問題のベストプラクティスと適切な設計についてのコメントも歓迎します。

+0

ご指定ください**予想される動作は何ですか?** – monamona

+0

@monamona done! – Delforge

+0

私は問題が何であるかについて強い疑念を持っていますが、答えを入力するのに少し時間がかかります – monamona

答えて

2

「期待される」振る舞いは、実際に実際に印刷されるものです。だから、行動は "あなたが期待していた"ものではなく、別のものです。理由を教えてください:

Appleの新しいインスタンスを作成するたびに、インスタンス化されたクラス(この場合はApple)に新しいメソッドを作成しています。行setattr(cls, 'printfuture', kwargs['usermethod'])は、新しいインスタンスBaseFruitまたはそのサブクラスを作成するたびに正確に実行されます。 (ちなみに、この行は単にcls.printfuture = kwargs['usermethod']とすることができ、属性名がハードコードされている場合はsetattrの必要はありません)。

それで、あなたはAppleのあなたの2番目のインスタンスを作成するときに、コールbadapple = Apple(aspect='rotten', usermethod=saddestfuture)だけsaddestfutureだけでなく、badappleための方法が、AppleのすべてのインスタンスのためにあることをAppleクラスのsaddestfutureprintfutureを作ります。

メタクラスを必要としない修正 - __new__のコードを使用して、代わりにインスタンスに接続された "擬似メソッド"を作成することができます。しかし、インスタンスの作成後、インスタンスへの参照があるとき、インスタンシエーション前ではなく、クラス自体への参照があるときにインスタンスで行う必要があります。

クラスを整理する前に実行する必要のある実際のコードはありませんので、メソッドのような機能を__init__にバインドして、実際に必要なときには__new__のままにしておいてください。その時ながら、それがスーパークラスの呼び出しをハードコーディングするのではなく、superを使用することを傷つけることはありません。

... 
# my base class 

class BaseFruit(object): 
    """Fruit base class""" 
    def __init__(self, *args, **kwargs): 
     printfuture = kwargs.pop('usermethod') 
     super(BaseFruit, self).__init__(*args, **kwargs) 
     # Wrap the call to the passed method in a function 
     # that captures "self" from here, since Python do not 
     # insert "self" in calls to functions 
     # attributed to instances. 
     self.printfuture = lambda: printfuture(self) 


# My class 

class Apple(BaseFruit): 
    """An apple class""" 
    def __init__(self, aspect, usermethod=sadfuture): 
     super(Apple, self).__init__(usermethod) 
     self.aspect = aspect 

そして、これは彼らのために必要がないメタクラスのためとして - 逆に、あなたは、各インスタンスをカスタマイズする必要が作成されます。クラス自体をカスタマイズする必要があるときは、通常、メタクラスを使用します。元のコードではそれが行われています(クラス自体のカスタマイズ)が、期待していなかった動作のために作成された各インスタンスが作成されると、そのコードが実行されます。 printfutureメソッドを作成するコードでは、メタクラス__new__メソッドではなく、スーパークラスにあるものと同じではなく、各サブクラスが宣言されたときに1回だけ発生します(そのサブクラスのすべてのインスタンスは同じprintifutureを共有します)。方法)。

これでどのように動作するのか把握したら、Python 3に移動してこのことを続けてください。 Python 2は、今から2年後には完全な終わりを迎え、いかなるプロジェクトでも役に立たなくなります。 1つはPython 2でレガシーコードを保持しなければならないこと、もう1つは新しいプロジェクトを学習すること、または新しいプロジェクトを開始することです.Python 3のみを使用してください。

0

問題は基本クラスから来ていると思います。

BaseFruitを使用してAppleクラスの基本クラスとして使用した場合、PythonはAppleクラスおよびBaseFruitクラスに存在する値をBaseFruitクラスに直接割り当てます。両方の 'りんご'は同じ基本クラスに基づいているので、このクラスから来た値を共有します。

saddestfutureを実行する関数に設定すると、BaseFruit-Classに基づいてすべてのオブジェクトに対して「グローバルに」種類が設定されます。

あなたはapple__init__

self.userm = usermethod 

ラインを必要としています。次にself.usermkwargとし、文字列"usermethod"ではなくBaseClassに渡します。

私はPythonの継承ルールで長年働いていなかったので、この操作の文法をexcatly知らないのですが、私は構文を忘れてしまったことは認めています。たぶん、誰かがコメント内にコードを提案したり、あなた自身がそれを見つけることができます:-)。

関連する問題