2017-01-10 4 views
3

updateメソッドを実装したいと思います。クラスを再作成するが、コンストラクタ引数を1つだけ変更します。1つのコンストラクタ引数を変更するクラスオブジェクトを再作成

私の試み:

class Updateable: 
    def update(self, var, var_str, **kwargs): 
     kwargs.update(self._vars) 
     kwargs[var_str] = var 
     self.__init__(**kwargs) 

class Rectangle(Updateable): 
    def __init__(self, length, perimeter): 
     self._vars = locals() 
     self.length = length 
     self.width = 0.5*(perimeter - 2.*length)  

r = Rectangle(10, 20) 
r.update('perimeter', 16) 

問題は、全体locals()ことですが、私は、かなり怪しいと思い、それはUpdateableある任意のクラスがself._varsを割り当てる必要があることを意味します。

この機能を実現する正しい方法は何でしょうか?デコレータ、メタクラス?何か簡単ですか?

+0

オブジェクトがすでに存在している__init__'後に 'を呼び出すと、少し奇妙に思えます。 'recalculate_width'メソッドや何かを持っていないのはなぜですか? – BrenBarn

+0

私はそれが私が持っている他のクラスのために働きたいので、すべてが異なる引数名を持っています。ときには「幅」、時には「角度」、時には「波長」、何でもかまいません。 –

答えて

2

私はあなたの質問を誤解した場合、またはあなたに高レベルのアドバイスとあなたのちょっとした問題を解決したくない場合は、私を修正してください。

__init__が現在行っていることは、(おそらく関連する)変数が変更された場合に幾何学的図形のプロパティを再計算することです。ステップ1は、これを__init__から取り出し、initによって呼び出される別のdefにします。ここで重要なのは、この関数に変数を渡すのではなく、__init__に設定されたクラス変数またはスーパークラスの更新メソッドを使用することです。

手順2は、更新機能を変更することです。 Pythonには、propertiesと呼ばれるゲッターとセッターの形式があり、タスクを変数の更新に使うことができます。他の人が観察してきたように一方より一般的な方法は、あなた自身のアップデートに、より類似しており、オプションとしてリストされている2

以下の実施例の代替

class Updateable: 
    # Option 1 
    @property 
    def perimeter(self): 
     return self.__perimeter 

    @perimeter.setter 
    def perimeter(self, perimeter): 
     self.__perimeter = perimeter 
     self.recalculate_everything() # or self.calculate_width() or something 

    # Option 2 
    def update(self, **kwargs): 
     for key, value in kwargs.items(): 
      setattr(self, key, value) 
     self.recalculate_everything 

class Rectable(Updateable): 
    def __init__(self, length, perimeter): 
     self.__length = length 
     self.__perimeter = perimeter 
     recalculate_everything() 

    def recalculate_everything(): 
     self.calculate_width() 
     ...  

    def calculate_width(): 
     self.__width = 0.5*(self.__perimeter - 2.*self.__length)  
+0

1 /実装属性に二重のアンダースコアを使用しない - 継承を破るネームマングリング機能を起動します。 1つの先頭のアンダースコアのみを使用します。 2 /継承を使用する場合は、スーパークラス初期化子を呼び出します。 3/Python 2.xでは、古いスタイルのクラスではプロパティが正しく動作しません。あなたの "Updatable"クラスは、デフォルトの実装で "recalculate_everything()"を定義するか、抽象基本クラス(cfのstlibのABCモジュール)を定義する必要があります。 5 /あなたは 'Rectangle .__ init__'(...)にNameErrorを持っています –

+0

そして6.プロパティを持つポイント全体を守るためにプロパティの設定を使うのではなく、イニシャライザの実装属性を手動で設定します。 –

+0

ああ、はい:Updatableのポイントは一般的なものでした。特定の実装属性については何も知らないはずです。 –

1

widthのような計算は、プロパティに移動する必要がありますまたは方法。彼らはイニシャライザーに属していません。

あなたが本当に新しいインスタンスを返すようにしたい場合は、このインスタンスの属性は、文字列や整数などの不変オブジェクトである最も単純なケースのために働くだろう:あなたのオブジェクトは、ネストされた可変構造を含む場合が

import copy 

class Copyable: 

    """Mixin to create copies with a changed attribute.""" 

    def copy_and_modify(self, var, var_str, **kwargs): 
     new = copy.copy(self) 
     setattr(new, var, var_str) 
     return new 

class Rectangle(Copyable): 

    def __init__(self, length, perimeter): 
     self.perimeter = perimeter 
     self.length = length 

    @property 
    def width(self): 
     return 0.5 * (self.perimeter - 2.0 * self.length) 

辞書やリストなど、あなたが定義することができ

class Copyable: 

    def copy_and_modify(self, var, var_str, **kwargs): 
     new = copy.deepcopy(self) 
     setattr(new, var, var_str) 
     return new 

copy.deepcopyを使用するようにcopy_and_modifyを変更する(ただし、深いコピーが遅い注意)する必要があるだろう__copy____deepcopy__メソッドをサブクラスas described in the docsに追加して、コピー処理の制御を微調整します。

+0

新しいインスタンスを作成する場合、そのメソッドの名前を 'update()'にするべきではありません。 –

+0

@ brunodesthuilliers私はOPのオリジナルのコンベンションに忠実でしたが、あなたは正しいです。 OPは、新しいインスタンスを作成する代わりに、既存のインスタンスを再初期化していました。私は今日後で名前を修正します。おそらく 'create_modified_copy'ですか?また、私はミックスインの名前を、その目的にさらに近づけるように改名します。 – snakecharmerb

2

ローレンスKoppenolのでここで働くと神託たとえば、簡単です、良いアイデアですが、彼の例のコードは、両方の多くの方法で破壊され、それがために持っているよりも複雑されproperties(計算された属性のためのPythonの汎用的なサポート)を使用して提案しました(なしUpdatableクラスにも、他の余分なものは必要ありません):あなたがwidth値をキャッシュしたい場合は(無用な計算を避けるために)まだlengthまたはperimeter変更、あなたがする必要がありますときに更新されますことを確認してください

class Rectangle(object): 
    def __init__(self, length, perimeter): 
     self.length = length 
     self.perimeter = perimeter 

    @property 
    def width(self): 
     return 0.5*(self.perimeter - 2.*self.length)  

それらをすべてのプロパティにする:

class Rectangle(object): 
    def __init__(self, length, perimeter): 
     self.length = length 
     self.perimeter = perimeter 

    @property 
    def length(self): 
     return self._length 

    @length.setter 
    def length(self, value): 
     self._length = value 
     self._width = None 


    @property 
    def perimeter(self): 
     return self._perimeter 

    @length.setter 
    def perimiter(self, value): 
     self._perimeter = value 
     self._width = None 

    @property 
    def width(self): 
     if self._width is None: 
      self._width = 0.5*(self.perimeter - 2.*self.length)  
     return self._width 

か(あなたは、このような多くのものを持っている場合)は、この1のように、いくつかの「cached_property無効と」実装を使用します。Storing calculated values in an object

編集:WRT /あなたの質問に、localsへの呼び出しは確かに醜いです(と簡単に壊れる可能性があります - _varsの部分ではないと思われるローカル変数があります)、子クラスにself._varsを明示的に設定する必要があります。また、update() API自体がかなり醜いIMHOです。今、あなたは全体のことをもっとニシキヘビにするために派手な何かを必要としない - ここでは、その唯一の定型(位置のものでは動作しません)名前付き引数とUpdateable.__init__を呼び出す必要がある解決策です:として

class Updateable(object): 
    def __init__(self, **kwargs): 
     self._vars = kwargs 

    def update(self, **kwargs): 
     vars = self._vars.copy() 
     vars.update(**kwargs) 
     self.__init__(**vars) 


class Rectangle(Updateable): 
    def __init__(self, length, perimeter): 
     super(Rectangle, self).__init__(length=length, perimeter=perimeter) 
     self.length = length 
     self.width = 0.5*(perimeter - 2.*length) 

r = Rectangle(10, 20) 
r.update(perimeter=40) 

サイドノートでは、I personnalyあなたRectangleクラスがperimeter引数を取りますが、代わりにwidthを保存することは極めて憂慮見つける...たぶん、あなたはperimeterプロパティを考慮する必要がありますか? (読み取り専用などを再計算避けるためにも)

+0

2番目の例では、初期化メソッドで 'self._length/_perimeter'に代入することを意味しましたか? – snakecharmerb

+0

それでは、私は、さまざまなパラメータを持つ多くのクラスに更新を適用したいので、すべての単一のパラメータを 'propery'として書く必要を避けたかったのです。これが私がこれを行うことができるジェネリッククラスを作ることを試みた理由です。これが可能かどうかは不明です。 –

+0

@snakecharmerbいいえ、私が書いたことは、 'self.length'と' self.perimeter'に割り当てることです。なぜ私のプロパティの設定をバイパスし、 'width'ゲッターコードを破るのでしょうか? –

関連する問題