66

__del__Pythonの詳細、いつ、なぜ使用する必要があるのか​​、何に使用しないのかについて興味があります。__new__/の反対ではないという点で、デストラクタに素朴に期待されるものとは実際には似ていないという難しい方法を学びました__init__

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__
4

4 に答える 4

74

リソースを閉じる方法は、コンテキストマネージャー、別名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のオブジェクトは、参照カウントが0のときに削除されます。この例で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"

出力:

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)見てみましょう:

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

        raise Exception

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

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

与える:

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__呼び出されることを意味します。

于 2011-07-21T07:55:19.540 に答える
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ます。発生した例外を処理しない場合でも、finallyブロックが実行された後も例外が発生します。

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

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

with open(x) as f:
    # do stuff

これにより、自動的に閉じfます。

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

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

http://docs.python.org/reference/datamodel.html#object。新着

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

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

于 2011-07-21T07:21:25.067 に答える
5

おそらくあなたはコンテキストマネージャーを探していますか?

>>> 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>
于 2011-07-21T07:21:53.513 に答える
2
  1. すべてのバーを閉じる出口ハンドラーを追加します。
  2. __del__()VMの実行中に、オブジェクトへの参照の数が0に達したときに呼び出されます。これはGCが原因である可能性があります。
  3. __init__()例外が発生した場合、オブジェクトは不完全であると見なされ、呼び出さ__del__()れません。
于 2011-07-21T07:17:38.690 に答える