[python] 파이썬 객체를 올바르게 정리하려면 어떻게합니까?

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'

그것이 다음 사람에게 도움이되기를 바랍니다.