How do I correctly clean up a Python object?

后端 未结 10 1030
北荒
北荒 2020-11-22 16:59
class Package:
    def __init__(self):
        self.files = []

    # ...

    def __del__(self):
        for file in self.files:
            os.unlink(file)
         


        
相关标签:
10条回答
  • 2020-11-22 17:30

    atexit.register is the standard way as has already been mentioned in ostrakach's answer.

    However, it must be noted that the order in which objects might get deleted cannot be relied upon as shown in example below.

    import atexit
    
    class A(object):
    
        def __init__(self, val):
            self.val = val
            atexit.register(self.hello)
    
        def hello(self):
            print(self.val)
    
    
    def hello2():
        a = A(10)
    
    hello2()    
    a = A(20)
    

    Here, order seems legitimate in terms of reverse of the order in which objects were created as program gives output as :

    20
    10
    

    However when, in a larger program, python's garbage collection kicks in object which is out of it's lifetime would get destructed first.

    0 讨论(0)
  • 2020-11-22 17:31

    As an appendix to Clint's answer, you can simplify PackageResource using contextlib.contextmanager:

    @contextlib.contextmanager
    def packageResource():
        class Package:
            ...
        package = Package()
        yield package
        package.cleanup()
    

    Alternatively, though probably not as Pythonic, you can override 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):
            ...
    

    and simply use with Package(...) as package.

    To get things shorter, name your cleanup function close and use contextlib.closing, in which case you can either use the unmodified Package class via with contextlib.closing(Package(...)) or override its __new__ to the simpler

    class Package(object):
        def __new__(cls, *args, **kwargs):
            package = super(Package, cls).__new__(cls)
            package.__init__(*args, **kwargs)
            return contextlib.closing(package)
    

    And this constructor is inherited, so you can simply inherit, e.g.

    class SubPackage(Package):
        def close(self):
            pass
    
    0 讨论(0)
  • 2020-11-22 17:35

    Just wrap your destructor with a try/except statement and it will not throw an exception if your globals are already disposed of.

    Edit

    Try this:

    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
    

    It will stuff the file list in the del function that is guaranteed to exist at the time of call. The weakref proxy is to prevent Python, or yourself from deleting the self.files variable somehow (if it is deleted, then it will not affect the original file list). If it is not the case that this is being deleted even though there are more references to the variable, then you can remove the proxy encapsulation.

    0 讨论(0)
  • 2020-11-22 17:37

    I'd recommend using Python's with statement for managing resources that need to be cleaned up. The problem with using an explicit close() statement is that you have to worry about people forgetting to call it at all or forgetting to place it in a finally block to prevent a resource leak when an exception occurs.

    To use the with statement, create a class with the following methods:

      def __enter__(self)
      def __exit__(self, exc_type, exc_value, traceback)
    

    In your example above, you'd use

    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)
    

    Then, when someone wanted to use your class, they'd do the following:

    with Package() as package_obj:
        # use package_obj
    

    The variable package_obj will be an instance of type Package (it's the value returned by the __enter__ method). Its __exit__ method will automatically be called, regardless of whether or not an exception occurs.

    You could even take this approach a step further. In the example above, someone could still instantiate Package using its constructor without using the with clause. You don't want that to happen. You can fix this by creating a PackageResource class that defines the __enter__ and __exit__ methods. Then, the Package class would be defined strictly inside the __enter__ method and returned. That way, the caller never could instantiate the Package class without using a with statement:

    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()
    

    You'd use this as follows:

    with PackageResource() as package_obj:
        # use package_obj
    
    0 讨论(0)
提交回复
热议问题