class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
上記の__del__(self)
はAttributeError例外で失敗します。私は理解しています Pythonは保証しません__del__()
が呼び出されたときの "グローバル変数"(この文脈ではメンバーデータ?)の存在。これが事実であり、これが例外の理由である場合、どのように私はオブジェクトがきちんと破壊することを確かめますか。
クリーンアップが必要なリソースを管理するには、Pythonのwith
ステートメントを使用することをお勧めします。明示的なclose()
ステートメントを使用することの問題は、例外が発生したときにリソースリークを防ぐために、人々がそれを呼び出すのを忘れるか、finally
ブロックに入れるのを忘れることを心配しなければならないということです。
with
ステートメントを使用するには、以下のメソッドでクラスを作成してください。
def __enter__(self)
def __exit__(self, exc_type, exc_value, traceback)
上記の例では、
class Package:
def __init__(self):
self.files = []
def __enter__(self):
return self
# ...
def __exit__(self, exc_type, exc_value, traceback):
for file in self.files:
os.unlink(file)
それから、誰かがあなたのクラスを使いたがっているとき、彼らは以下をします:
with Package() as package_obj:
# use package_obj
変数package_objはPackage型のインスタンスになります(これは__enter__
メソッドによって返される値です)。例外が発生するかどうかにかかわらず、その__exit__
メソッドは自動的に呼び出されます。
このアプローチをさらに一歩進めることもできます。上の例では、with
節を使用せずに、コンストラクタを使用してPackageをインスタンス化することもできます。あなたはそれが起こりたくないのです。 __enter__
および__exit__
メソッドを定義するPackageResourceクラスを作成することによってこれを修正できます。その後、Packageクラスは__enter__
メソッド内で厳密に定義されて返されます。そのようにして、呼び出し元はwith
ステートメントを使用せずにPackageクラスをインスタンス化することはできません。
class PackageResource:
def __enter__(self):
class Package:
...
self.package_obj = Package()
return self.package_obj
def __exit__(self, exc_type, exc_value, traceback):
self.package_obj.cleanup()
これを次のように使用します。
with PackageResource() as package_obj:
# use package_obj
標準的な方法は atexit.register
を使うことです。
# package.py
import atexit
import os
class Package:
def __init__(self):
self.files = []
atexit.register(self.cleanup)
def cleanup(self):
print("Running cleanup...")
for file in self.files:
print("Unlinking file: {}".format(file))
# os.unlink(file)
しかし、Pythonが終了するまで、これはPackage
の作成されたすべてのインスタンスを永続化することに注意してください。
package.py として保存された上記のコードを使用してデモします。
$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...
Clint's answer の付録として、 contextlib.contextmanager
を使用してPackageResource
を単純化できます。
@contextlib.contextmanager
def packageResource():
class Package:
...
package = Package()
yield package
package.cleanup()
あるいは、おそらくPythonicとは異なりますが、Package.__new__
をオーバーライドすることができます。
class Package(object):
def __new__(cls, *args, **kwargs):
@contextlib.contextmanager
def packageResource():
# adapt arguments if superclass takes some!
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
yield package
package.cleanup()
def __init__(self, *args, **kwargs):
...
そして単にwith Package(...) as package
を使います。
短くするには、クリーンアップ関数にclose
という名前を付け、 contextlib.closing
を使用します。この場合、with contextlib.closing(Package(...))
を介して未修正のPackage
クラスを使用するか、その__new__
をより単純なものにオーバーライドできます。
class Package(object):
def __new__(cls, *args, **kwargs):
package = super(Package, cls).__new__(cls)
package.__init__(*args, **kwargs)
return contextlib.closing(package)
そしてこのコンストラクタは継承されるので、あなたは単純に継承することができます。
class SubPackage(Package):
def close(self):
pass
__del__
が呼ばれる前にインスタンスメンバーが削除されることは可能ではないと思います。私の考えでは、あなたの特定のAttributeErrorの理由はどこかにあるのでしょう(たぶんあなたは間違って他の場所にself.fileを削除します)。
ただし、他の人が指摘したように、__del__
を使用しないでください。これの主な理由は、__del__
を持つインスタンスはガベージコレクションされないことです(それらのrefcountが0になったときにのみ解放されます)。したがって、インスタンスが循環参照に関与している場合は、アプリケーションが実行されている限り、それらはメモリ内に存在します。 (私はこれについて誤解しているかもしれませんが、もう一度gcドキュメントを読む必要がありますが、それはこのように機能すると確信しています)。
デストラクタをtry/exceptステートメントでラップするだけで、グローバルがすでに破棄されている場合でも例外は発生しません。
編集
これを試して:
from weakref import proxy
class MyList(list): pass
class Package:
def __init__(self):
self.__del__.im_func.files = MyList([1,2,3,4])
self.files = proxy(self.__del__.im_func.files)
def __del__(self):
print self.__del__.im_func.files
それは、呼び出し時に存在することが保証されている del 関数にファイルリストを詰め込みます。 weakrefプロキシは、Pythonや自分自身がself.files変数をなんらかの方法で削除するのを防ぐためのものです(削除されても元のファイルリストには影響しません)。変数への参照がまだあるにもかかわらず、これが削除されていない場合は、プロキシカプセル化を削除できます。
もっと良い方法は weakref.finalize を使うことです。 ファイナライザオブジェクト および ファイナライザと__del __()メソッドの比較 の例を参照してください。
これが最小限の作業用のスケルトンです。
class SkeletonFixture:
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def method(self):
pass
with SkeletonFixture() as fixture:
fixture.method()
重要: 自分自身を返す
あなたが私のようで、( Clint Millerの正解 の)return self
の部分を見落としているなら、あなたはこのナンセンスを見つめているでしょう:
Traceback (most recent call last):
File "tests/simplestpossible.py", line 17, in <module>
fixture.method()
AttributeError: 'NoneType' object has no attribute 'method'
私はこれに半日を費やした。それが次の人に役立つことを願っています。
これを行うための慣用的な方法はclose()
メソッド(または類似のメソッド)を提供し、それを明示的に呼び出すことです。