2013-12-18 1 views
28

多分これは技術的なものよりもスタイルに関する質問ですが、私はいくつかのメンバー変数を持つPythonクラスを持っています。ユーザが最初にクラスのインスタンスを作成したとき(つまり__init__の関数で)初期化され、後で呼び出されるメンバ関数の引数から他のメンバ変数を定義したいとします。だから私の質問は、__init__関数内のすべてのメンバ変数を初期化し、後で定義されるものをダミー値に設定するか、または__init__関数の一部とそれ以降の関数を初期化する必要があります。これは理解しにくいかもしれないことに気がついたので、ここにいくつかの例があります。Python - すべてのメンバー変数を__init__で初期化する必要があります

この例では、__init__ファンクションでは最初にvar3が0に設定され、その後、my_functファンクションで目的の値に設定されています。

class myClass(object): 
    def __init__(self,var1,var2): 
     self.var1=var1 
     self.var2=var2 
     self.var3=0 

    def my_funct(self,var3): 
     self.var3=var3 

この例では、var3__init__機能

class myClass(object): 
    def __init__(self,var1,var2): 
     self.var1=var1 
     self.var2=var2 

    def my_funct(self,var3): 
     self.var3=var3 

に全く定義されていない、私はどちらかの方法は、大きな違い(メモリ使用量のかもしれないわずかな差)になるだろうとは思いません。しかし、私はこれらのうちの1つが何らかの理由で他のものより優先されるかどうか疑問に思っていました。

答えて

19

オブジェクト指向プログラミングでは、インスタンス化後およびメソッドの終了後にオブジェクトが常に一貫性のある状態になるようにすることは開発者の責任です。それ以外は、あなたが望むようにクラスを自由に開発することができます(サブクラス化/オーバーライドなどの特定の原則を念頭に置いてください)。

Pylintなどのツールでは、インスタンス変数を__init__以外に設定すると警告が表示されます。 __init__内のすべてのインスタンス変数を設定することはよりクリーンであると主張できますが、それはいつも守らなければならないルールではありません。

+1

合意しました。一貫性が重要です。外部の人があなたのAPIやクラスを無効な状態で使用することは望ましくありません。 –

+0

「一貫性のある状態」の意味を簡単に定義できますか?インスタンス化後に新しいメンバ変数を追加する必要はありませんか? – user1893354

+1

@ user1893354インスタンス化の後に変数を追加することはできますが、オブジェクトを作成したり、1つ以上のメソッドを呼び出したり、面倒なオブジェクトを作成することはできません。メソッドとその戻り値の動作は、常に一貫していなければなりません。例えば、「壊れた」と「正しく機能する」と報告する 'Car'クラスを持つことはできません。 –

3

__init__には必ずしも必要ではない変数の初期化は、実際には任意のデフォルト値にすることをお勧めします。

この場合、OOの使用について質問しますが、__init__がすべてを実行しない有効かつ分かりやすいケースがあると確信しています。クラスはさらに属性を追加することでそれ自体を変更する必要があります他の方法。

私の意見では、変数を設定しているかどうかを調べるには、hasattrを使用することをお勧めします。これは、これがメソッドを使用する有効な方法であり、テストが賢明な方法で動作を切り替えるだけの場合です。

もう1つの方法は、それを使用して例外を処理し、クラスのユーザーが間違っていることに関するユーザーフレンドリーな情報を提供することです。これは、メソッドが実行前に属性を設定する必要がある場合です。

つまり、あなたはクラスを初期化しましたが、z_runメソッドを実行する前にz_initメソッドを呼び出して、z属性が存在することを確認する必要があります。

もう1つ、おそらくもっとpythonicな方法は、ドキュメントストリングでメソッドを使用する方法を文書化し、不適切に使用されたときに例外を飛ばすことです。これは何かの最初の実装には十分であり、次のタスクに集中することができます。これは上記と同じ状況にあり、メソッドは属性を設定する必要があります。

変数を任意のデフォルト値に初期化するという考えが嫌いな理由は、これは混乱する可能性があります(任意であるため)、ラインノイズです。

値がではなく、であり、変更可能な既定値の場合は、オーバーライドできる__init__メソッドの既定値を使用する必要があります。実際には有効な初期状態でもあり、ではなく、であり、__init__メソッドで設定する必要があります。

に依存しているため、他の方法で属性を追加したり、属性を任意の値に初期化することで、OOを使用することをお勧めします。

Simeon Visserはオブジェクトを一貫性のある状態に保つと言っていますが、抽象的な例に基づいてどのような一貫性があるかについての根拠はありません。 Pylintはこのようなことについて警告していますが、糸くずのプログラムからの警告は単に高いレベルの査読者には、通常はがコードの匂いを示していることを警告できます。本当の批評家があなたのコードをすべて読んで理解し、パイリントを本当に必要としないので、高レベルの批評家と言います。

親指のルールを破っ例:

class Mutant(object): 
    """A mutant!""" 

    def __init__(self): 
     """A mutant is born with only 1 eye and 1 mouth""" 

     self.eyes = 1 
     self.mouth = 1 
     self.location = 'Montana' 

    def roll_to(self, location): 
     """If they have limbs, running is less dangerous""" 

     if hasattr(self, 'limbs'): 
      print 'Your mutant broke its limbs off!!' 
      del self.limbs 

     self.location = location 

    def run_to(self, location): 
     """If they don't have limbs, running is not effective""" 

     if not hasattr(self, 'limbs'): 
      print 'Your mutant tries to run but he has no limbs.' 
     else: 
      self.location = location 

    def grow_limbs(self, number_of_limbs): 
     """Ah, evolution!""" 

     assert number_of_limbs > 0, 'Cannot grow 0 or less limbs...' 

     if hasattr(self, 'limbs'): 
      self.limbs += number_of_limbs 
     else: 
      self.limbs = number_of_limbs 
+0

良い点。私は、メンバ変数の1つが、オブジェクト内に保存したいメンバ関数の出力である場合を考えていました。この場合、変数は、メンバー関数が呼び出された後にのみ実際の値でインスタンス化することができます。 – user1893354

+0

それで、私があなたを正しく理解できるようにしましょう。たとえば、 'obj'は' output_complex_calculation'を持っていて、 'obj.set_result_of_complex_calculation(obj.output_of_complex_calculation())' –

+0

にしたいとします。クラスは、ユーザがインスタンス化時にモデルのパラメータを入力する予測モデルです。次に、ユーザーは予測のリストを作成する.predict()関数を呼び出します。しかし、このオブジェクトのこれらの予測をメンバー変数として保存したいと考えています。次に、他のメンバー関数がこの新しい予測メンバー変数で何かをするでしょう。この場合、予測メンバー変数の実際の値は、.predict()が呼び出された後でのみインスタンス化できます。 – user1893354

関連する問題