class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
위의 AttributeError 예외로 실패합니다. 이해 파이썬은 보장하지 않습니다 때 “전역 변수”(이 맥락에서 구성원 데이터?)의 존재를 __del__()
호출됩니다. 그 경우이고 이것이 예외의 원인 인 경우, 객체가 올바르게 파괴되도록하려면 어떻게해야합니까?
답변
with
정리해야 할 리소스를 관리하기 위해 Python의 문장을 사용하는 것이 좋습니다 . 명시 적 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는 패키지 유형의 인스턴스입니다 ( __enter__
메소드가 반환 한 값 ). 그 __exit__
방법에 관계없이 자동으로 예외가 발생 여부에 호출됩니다.
이 접근법을 한 단계 더 발전시킬 수도 있습니다. 위의 예에서 누군가는 with
절 을 사용하지 않고 생성자를 사용하여 패키지를 인스턴스화 할 수 있습니다 . 당신은 그런 일이 일어나기를 원하지 않습니다. __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)
그러나 이것은 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의 답변에 대한 부록으로 다음을 사용 PackageResource
하여 단순화 할 수 있습니다 contextlib.contextmanager
.
@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
.이 경우 수정되지 않은 Package
클래스를 사용 with contextlib.closing(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__
가비지 수집되지 않은 인스턴스입니다 (참조 횟수가 0에 도달 할 때만 해제 됨). 따라서 인스턴스가 순환 참조에 관련된 경우 응용 프로그램이 실행되는 한 메모리에 존재합니다. (하지만이 모든 것에 대해 잘못 생각할 수 있습니다 .gc 문서를 다시 읽어야하지만 오히려 다음과 같이 작동한다고 확신합니다).
답변
더 나은 대안은 weakref.finalize 를 사용하는 것 입니다. Finalizer 개체 및 __del __ () 메서드를 사용한 종료 자 비교 의 예제를 참조하십시오 .
답변
__init__
표시된 것보다 더 많은 코드가 있으면 문제가 발생할 수 있다고 생각 합니까?
__del__
__init__
제대로 실행되지 않았거나 예외를 던진 경우에도 호출됩니다 .
답변
최소한의 작동 골격이 있습니다.
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()
중요 : 자기 반품
당신이 나와 같고 return self
( 클린트 밀러의 정답 중 ) 부분을 간과한다면 , 당신은이 말도 안될 것입니다.
Traceback (most recent call last):
File "tests/simplestpossible.py", line 17, in <module>
fixture.method()
AttributeError: 'NoneType' object has no attribute 'method'
그것이 다음 사람에게 도움이되기를 바랍니다.