55

私は__del__の詳細については、いつ、なぜそれを使うべきか、それを使用すべきではないことに興味があります。私は難問を学んだのですが、デストラクタから期待されるものが実際には好きではなく、__new__/__init__の反対ではありません。pythonオブジェクトの削除を強制するには?

class Foo(object): 

    def __init__(self): 
     self.bar = None 

    def open(self): 
     if self.bar != 'open': 
      print 'opening the bar' 
      self.bar = 'open' 

    def close(self): 
     if self.bar != 'closed': 
      print 'closing the bar' 
      self.bar = 'close' 

    def __del__(self): 
     self.close() 

if __name__ == '__main__': 
    foo = Foo() 
    foo.open() 
    del foo 
    import gc 
    gc.collect() 

私は__del__()メソッドはインタプリタが終了まだ存在するオブジェクトのために呼ばれている保証されていないことを文書で見ました。

  1. Fooインスタンスが存在する場合、インタープリタが終了してもバーが閉じられることをどのように保証できますか?
  2. 上記コードスニペットでは、バーはdel fooまたはgc.collect() ...またはどちらも閉じられませんか?それらの詳細を細かく制御したい場合(オブジェクトが参照されていないときにバーを閉じるなど)、それを実装する通常の方法は何ですか?
  3. __del__を呼び出すと、__init__が既に呼び出されていることが保証されますか? __init__が育ったらどうなりますか?

答えて

64

方法with声明別名、コンテキストマネージャです:

class Foo(object): 

    def __init__(self): 
    self.bar = None 

    def __enter__(self): 
    if self.bar != 'open': 
     print 'opening the bar' 
     self.bar = 'open' 
    return self # this is bound to the `as` part 

    def close(self): 
    if self.bar != 'closed': 
     print 'closing the bar' 
     self.bar = 'close' 

    def __exit__(self, *err): 
    self.close() 

if __name__ == '__main__': 
    with Foo() as foo: 
    print foo, foo.bar 

が出力:

opening the bar 
<__main__.Foo object at 0x17079d0> open 
closing the bar 

2)Pythonのオブジェクトをするとき、その参照カウント削除されますあなたの例ではdel fooは最後の参照を削除しますので__del__はすぐに呼び出されます。 GCにはこれには何も含まれていません。

class Foo(object): 

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

if __name__ == '__main__': 
    import gc 
    gc.disable() # no gc 
    f = Foo() 
    print "before" 
    del f # f gets deleted right away 
    print "after" 

出力:

before 
deling <__main__.Foo object at 0xc49690> 
after 

gcは、あなたや他のほとんどのオブジェクトの削除とは何の関係もありません。これは、理由は、自己参照や循環参照のため、単純な参照カウントが動作しないときにクリーンアップするためにあります:

class Foo(object): 
    def __init__(self, other=None): 
     # make a circular reference 
     self.link = other 
     if other is not None: 
      other.link = self 

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

if __name__ == '__main__': 
    import gc 
    gc.disable() 
    f = Foo(Foo()) 
    print "before" 
    del f # nothing gets deleted here 
    print "after" 
    gc.collect() 
    print gc.garbage # The GC knows the two Foos are garbage, but won't delete 
        # them because they have a __del__ method 
    print "after gc" 
    # break up the cycle and delete the reference from gc.garbage 
    del gc.garbage[0].link, gc.garbage[:] 
    print "done" 

出力:

class Foo(object): 
    def __init__(self): 

     raise Exception 

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

if __name__ == '__main__': 
    f = Foo() 

before 
after 
[<__main__.Foo object at 0x22ed8d0>, <__main__.Foo object at 0x22ed950>] 
after gc 
deling <__main__.Foo object at 0x22ed950> 
deling <__main__.Foo object at 0x22ed8d0> 
done 

3)を見てみましょう

は:

Traceback (most recent call last): 
    File "asd.py", line 10, in <module> 
    f = Foo() 
    File "asd.py", line 4, in __init__ 
    raise Exception 
Exception 
deling <__main__.Foo object at 0xa3a910> 

オブジェクトは__new__で作成され、__init__にはselfと渡されます。 __init__の例外の後では、オブジェクトは通常、名前がありません(つまり、f =部分は実行されません)ので、その参照カウントは0です。つまり、オブジェクトが正常に削除され、__del__が呼び出されます。

+0

>リソースを閉じる方法はコンテキストマネージャーです(別名:withステートメント)。 優れた先端。このようにオブジェクトのスコープを格納するために 'with 'を使うことができるのを知らなかった。 –

+3

この回答は非常に明るいです。明確な説明をありがとう! –

2
  1. すべてのバーを閉じexit handlerを追加します。
  2. __del__()は、VMの実行中にオブジェクトへの参照が0になると呼び出されます。これは、GCによって引き起こされる可能性があります。
  3. __init__()が例外を発生させた場合、オブジェクトは不完全であるとみなされ、__del__()は呼び出されません。一般的に
+2

注:os._exitは、すべてのクローズ処理機能を完全にバイパスします。 – cwallenpoole

8

、何かがどんな起こらないことを確認するために、あなたは

from exceptions import NameError 

try: 
    f = open(x) 
except ErrorType as e: 
    pass # handle the error 
finally: 
    try: 
     f.close() 
    except NameError: pass 

finallyを使用するブロックは、tryブロックにエラーがあるかどうかを実行し、そこにあるかどうかをされますexceptブロックで発生するエラー処理エラー。発生した例外を処理しない場合は、ブロックが実行された後も引き続き例外が発生します。

ファイルが閉じていることを確認する一般的な方法は、「コンテキストマネージャー」を使用することです。

http://docs.python.org/reference/datamodel.html#context-managers

with open(x) as f: 
    # do stuff 

これは自動的にfを閉じます。

あなたの質問#2の場合、barは参照カウントがゼロになるとすぐに閉じられますので、他の参照がない場合はdel fooになります。

オブジェクトは__init__で作成されず、__new__によって作成されます。

http://docs.python.org/reference/datamodel.html#object.new

あなたはfoo = Foo()二つのことを行うと、実際にそれは、__init__に初期化されている、__new__、最初に新しいオブジェクトが作成されて、起こっています。したがって、両方の手順を実行する前にdel fooに電話することはできません。ただし、__init__にエラーがある場合は、オブジェクトが実際にすでに__new__に作成されているため、__del__が呼び出されます。

編集:参照カウントが0に減少した場合に削除が発生した場合に訂正されます。

+1

'try..except..finally'の例が壊れています:' open() 'が例外をスローすると、' f'はセットされず、最後は動作しません。 – Duncan

+0

ありがとう、固定。しかし、エラーが開かれなかった場合にのみ発生するので、 'f'が閉じられたことを保証します。 – agf

+2

@afgあなたはgcについて間違っていて、オブジェクトが削除されたときは私の答えを見てください。 –

5

おそらくcontext managerをお探しですか?リソースを閉じ

>>> class Foo(object): 
... def __init__(self): 
...  self.bar = None 
... def __enter__(self): 
...  if self.bar != 'open': 
...  print 'opening the bar' 
...  self.bar = 'open' 
... def __exit__(self, type_, value, traceback): 
...  if self.bar != 'closed': 
...  print 'closing the bar', type_, value, traceback 
...  self.bar = 'close' 
... 
>>> 
>>> with Foo() as f: 
...  # oh no something crashes the program 
...  sys.exit(0) 
... 
opening the bar 
closing the bar <type 'exceptions.SystemExit'> 0 <traceback object at 0xb7720cfc> 
関連する問題