2016-11-14 5 views
7

時にはそれはすなわち、既存のオブジェクトの初期化方法として__init__を使用するのが合理的になります。コンストラクタとしてではなく、初期化のための通常のメソッドとして__init__を使用できますか?

class A(): 
    def __init__(self, x): 
     self.x = x 

    def set_state_from_file(self, file): 
     x = parse_file(file) 
     self.__init__(x) 

この実装する代わりに、私は以下を参照してください。

class A(): 
    def __init__(self, x): 
     self.init(x)   

    def init(self, x): 
     self.x = x 

    def set_state_from_file(self, file): 
     x = parse_file(file) 
     self.init(x) 

それはオーバーと私には思えますコードの複雑さ。このような状況についてのガイドラインはありますか?

+1

ファイルを解析するために、非メンバ関数を持っている、そして新しいインスタンスを作成しないのはなぜ? [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single_responsibility_principle)に違反する別のソースからクラスを初期化するたびに、クラスを書き直す必要があるようです。 –

+0

@Peter Wood、 '__setstate__'の' __init__'はどうですか? – Sklavit

答えて

11

__init__はコンストラクタではありません。これは、と呼ばれる初期化メソッドで、のインスタンスが既に構築されています(実際のコンストラクタメソッドは__new__()と呼ばれています)。

再初期化が必要な場合は、コードから再度呼び出すことができます。これはスタイル違反ではありません。実際には、Pythonの標準ライブラリで使用されています。例えばmultiprocessing.heap.Heap() implementation参照:

def malloc(self, size): 
    # return a block of right size (possibly rounded up) 
    assert 0 <= size < sys.maxsize 
    if os.getpid() != self._lastpid: 
     self.__init__()      # reinitialize after fork 

又はthreading.local implementation、初期化を延期するコンテキストマネージャを使用します。

__init__メソッド自体に特別なことはありません。単にtype.__call__によって自動的に呼び出されます(instance = cls.__new__(cls, *args, **kwargs)でインスタンスを作成した後は、利用可能な場合はcls.__init__(instance, *args, **kwargs)が呼び出されます)。 Martjinの答えに加えて

4

:Pythonで一般的なパターンは、ファクトリメソッドとしてクラスメソッドを使用することで、すなわち:

1:

class A(): 
    def __init__(self, x): 
     self.x = x 

    @classmethod 
    def from_file(cls, file): 
     x = parse_file(file) 
     return cls(x) 


a1 = A(42) 
a2 = A.from_file(open("/path/to/file")) 
+0

----------------------------------- – Sklavit

+0

@Sklavit明らかに、「代替のコンストラクタとしてのクラスメソッド」パターンは必要なものではありません

+0

これは、既存のインスタンスで '__init__'メソッドを再利用することと何が関係していますか? –

0

私は__init__と「通常の」方法に若干の違いを発見しました。 __init__は何も返されません。TypeErrorが発生します。 __init__にエラーが発生した場合

2.、__del__が呼び出されます。マルタインピータースによって UPDATE:これが唯一のコンストラクタ呼び出しのためではなく、一般的な使用のためであるが、以下のコメントを参照してください。

class A(object): 
    def __init__(self): 
      print('__init__') 
      raise ValueError('__init__ error') 
      pass 

    def method(self): 
     raise ValueError('method error') 

    def __del__(self): 
     print("__del__") 

def main(): 
    try: 
     a = A() 
     a.method() 
    except ValueError as e: 
     print(e) 
    print('exit main') 

if __name__ == '__main__': 
    main() 
    print('end of file') 

意志出力:

__init__ 
__init__ error 
__del__ 
exit main 
end of file 
+0

もちろん、新しいインスタンスがクリアされます。構築中の例外( '__init__'初期化子の呼び出しを含む)は、新しいオブジェクトが返されないことを意味します。例外は通常のフローを破り、割り当ては行われず、参照は残されません。あなたが 'inst = A .__ new __(A)'と 'inst .__ init __()'を使っていれば、オブジェクトへの参照を持ち、 '__del__'は呼び出されません。これは '__init__'とは何の関係もなく、通常の方法です。 –

+0

しかし、これは、既存のインスタンスで '__init__'メソッドを再利用することと何が関係しているのかよくわかりません。 –

+0

また、 'TypeError'は' __init__'を呼び出すコードによってスローされます。メソッドは特別なものではなく、呼び出し側だけが(例えば、合意した契約を破る特別なメソッドを使用する他のコードではなく、 '__str__'などの文字列以外のものを返そうとする) '__init__'メソッドを直接呼び出すと、' None'以外のものを返すと何も起こりません。 –

関連する問題