How to use the context manager to avoid the use of __del__ in python?

前端 未结 3 1517
刺人心
刺人心 2020-12-31 11:17

As it is common knowledge, the python __del__ method should not be used to clean up important things, as it is not guaranteed this method gets called. The alter

3条回答
  •  青春惊慌失措
    2020-12-31 12:11

    I suggest using the contextlib.contextmanager class instead of writing a class that implements __enter__ and __exit__. Here's how it would work:

    class MyWrapper(object):
        def __init__(self, device):
            self.device = device
    
        def open(self):
            self.device.open()
    
        def close(self):
            self.device.close()
    
        # I assume your device has a blink command
        def blink(self):
            # do something useful with self.device
            self.device.send_command(CMD_BLINK, 100)
    
        # there is no __del__ method, as long as you conscientiously use the wrapper
    
    import contextlib
    
    @contextlib.contextmanager
    def open_device(device):
        wrapper_object = MyWrapper(device)
        wrapper_object.open()
        try:
            yield wrapper_object
        finally:
            wrapper_object.close()
        return
    
    with open_device(device) as wrapper_object:
         # do something useful with wrapper_object
         wrapper_object.blink()
    

    The line that starts with an at sign is called a decorator. It modifies the function declaration on the next line.

    When the with statement is encountered, the open_device() function will execute up to the yield statement. The value in the yield statement is returned in the variable that's the target of the optional as clause, in this case, wrapper_object. You can use that value like a normal Python object thereafter. When control exits from the block by any path – including throwing exceptions – the remaining body of the open_device function will execute.

    I'm not sure if (a) your wrapper class is adding functionality to a lower-level API, or (b) if it's only something you're including so you can have a context manager. If (b), then you can probably dispense with it entirely, since contextlib takes care of that for you. Here's what your code might look like then:

    import contextlib
    
    @contextlib.contextmanager
    def open_device(device):
        device.open()
        try:
            yield device
        finally:
            device.close()
        return
    
    with open_device(device) as device:
         # do something useful with device
         device.send_command(CMD_BLINK, 100)
    

    99% of context manager uses can be done with contextlib.contextmanager. It is an extremely useful API class (and the way it's implemented is also a creative use of lower-level Python plumbing, if you care about such things).

提交回复
热议问题