web-dev-qa-db-ja.com

Python-すべてのメンバー変数を__init__で初期化する必要があります

たぶんこれは技術的な質問よりもスタイルの質問の方が多いかもしれませんが、いくつかのメンバー変数を持つpythonクラスがあり、ユーザーがいくつかのメンバー変数を初期化するように動作させたい最初にクラスのインスタンスを作成し(つまり、__init__関数で)、後で呼び出されるメンバー関数の引数から他のメンバー変数を定義したいので、すべてのメンバー変数を初期化する必要があります__init__関数で(および後で定義される値をダミー値に設定する)、または__init__関数でいくつかを初期化し、後の関数でいくつかを初期化します。いくつかの例です。

この例では、var3関数で__init__が最初に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

どちらの方法でも大きな違いはないと思います(メモリ使用量のわずかな違いかもしれません)。しかし、何らかの理由で、これらのいずれかが他よりも優先されるのではないかと考えていました。

39
user1893354

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

Pylint などのツールは、__init__の外部にインスタンス変数を設定しているときに警告を出します。 __init__内のすべてのインスタンス変数を設定することはよりクリーンであると言えますが、常に従わなければならないルールではありません。

27
Simeon Visser

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

OOこれが当てはまる場合、あなたの使用に疑問を投げかけますが、__init__がすべてを実行せず、クラスが望む有効で理解可能なケースがあると確信しています。他のメソッドで追加の属性を追加することにより、さらに自分自身を変更します。

私の意見では、使用したいメソッドの実行中に変数が設定されたかどうかをテストする適切な方法は、hasattrを使用することです。これは、これがメソッドを使用する有効な方法であり、テストが適切な方法で動作を切り替えるだけの場合です。

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

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

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

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

値がnot任意であり、単に変更可能なデフォルト値である場合、オーバーライド可能な__init__メソッドでデフォルト値を使用する必要があります。また、実際には有効な初期状態にすることもできます。これはnot任意であり、__init__メソッドで設定する必要があります。

したがって、本当の答えはそれは依存します、あなたはおそらくそれを避けて、OOこれを行うには、他のメソッドに属性を追加するか、属性を任意の値に初期化します。

Simeon Visserはオブジェクトを一貫した状態に保つように言っていますが、彼はあなたの抽象的な例に基づいてどのような一貫性があるのか​​についての根拠を持ちません。 Pylintはこの種のことを警告しますが、lintプログラムからの警告は、通常がコードのにおいを示すものについて、高レベルのレビュー担当者に警告できるようにするためのものです。本当のレビュアーはすべてのコードを読んで理解するべきであり、したがって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
6
Derek Litz

以下は、sololearn.com(Pythonを学ぶための無料サイト)からの抜粋です。

「プロパティは、インスタンス属性へのアクセスをカスタマイズする方法を提供します。メソッドの上にプロパティデコレータを配置することで作成されます。つまり、メソッドと同じ名前のインスタンス属性にアクセスすると、代わりにメソッドが呼び出されます。

プロパティの一般的な用途の1つは、属性を読み取り専用にすることです。」

例(sololearn.comからも):

class Pizza:
    def __init__(self, toppings):
    self.toppings = toppings

    @property
    def pineapple_allowed(self):
       return False

   pizza = Pizza(["cheese", "tomato"])
   print(pizza.pineapple_allowed)
   pizza.pineapple_allowed = True

結果:

  >>>
 False
 AttributeError: can't set attribute
 >>>

Var3がvar1とvar2に依存している場合

class myClass:
    def __init__(self,var1,var2):
        self.var1=var1
        self.var2=var2
    @property
    def var3(self):
        return(self.var1+self.var2)  #var3 depends on var1 and var2
 m1=myClass(1,2)
 print(m1.var3)   # var3 is 3

var3は、セッター関数を使用して任意に設定することもできます。 Noneを使用すると、var3を任意の値に設定することを回避できることに注意してください。

class myClass2(object):
    def __init__(self,var1,var2):
        self.var1=var1
        self.var2=var2
        self._var3=None     # None or an initial value that makes sense
        @property
        def var3(self):
            return(self._var3)
        @var3.setter
        def var3(self,value):
            self._var3=value
   m2=myClass(1,2)
   print(m2.var3)        # var3 is none
   print(m2.var3(10))    # var3 is set to 10 
0
Cody Aldaz