Scribbling

Python: Context Manager 본문

Computer Science/Python

Python: Context Manager

focalpoint 2022. 4. 29. 09:36

 

Else block

For-Else

Else block is executed when the 'for loop' was not stopped by any break. 

for i in range(1, 1):
else:
    print('Else block is executed')

for i in range(1, 10):
    if i == 8:
        break
else:
    print('Else block is executed')

 

While-Else

Works the same for 'for-else'.

while False:
    break
else:
    print('Else block executed')

 

Try-Else

try:
    dangerous_call()
    after_call()
except OSError:
    log('OSError')

Above code should be modifed to below. Only the potential error-raising code should be in try block.

try:
    dangerous_call()
except OSError:
    log('OSError')
else:
    after_call()

Else block is executed when error is not raised.

try:
    dangerous_call()
except OSError:
    log('OSError')
else:
    after_call()
finally:
    return_resource()

Finally block is always executed regardless of error.

 

Context manager and With block

Context manager protocol consists of __enter__ and __exit__ methods. With block is designed to replace try/finally pattern ensuring that code in a certain range is always executed regardless of errors.

with open('mirror.py') as fp:
    src = fp.read(30)

Context manager object is returned with 'open()' and fp(variable after as) is the returned result of the object's __enter__() method. In the example, open() function returns TextIOWrapper object and its __enter__ method returns self(). __exit__() method is called in the end.

 

class Reverser:
    def __enter__(self):
        import sys
        self.original_write = sys.stdout.write
        sys.stdout.write = self.reverse_write

    def reverse_write(self, text):
        self.original_write(text[::-1])

    def __exit__(self, exc_type, exc_value, traceback):
        import sys
        sys.stdout.write = self.original_write
        if exc_type is ZeroDivisionError:
            print('Zero Division')
            return True


with Reverser():
    print('Hello World')
    print('What is happening')

__exit__ method must return True if the error is handled properly. If not, raised error is delivered upwards.

 

 

@contextmanager

@contextmanager decorator is to implement context manager with shorter code.

import contextlib

@contextlib.contextmanager
def Reverser():
    import sys
    original_write = sys.stdout.write

    def reverse_write(text):
        original_write(text[::-1])

    sys.stdout.write = reverse_write
    yield
    sys.stdout.write = original_write



with Reverser():
    print('Hello World')
    print('What is happening')

Code before yield is executed when with block calls __call__ method; Code after yield is executed when __exit__ is called.

However, the above code has a serious problem. __exit__ part is not executed if an error occurs.

import contextlib

@contextlib.contextmanager
def Reverser():
    import sys
    original_write = sys.stdout.write

    def reverse_write(text):
        original_write(text[::-1])
    msg = ''
    sys.stdout.write = reverse_write
    try:
        yield 'something'
    except ZeroDivisionError:
        msg = 'Zero Division'
    finally:
        sys.stdout.write = original_write
        if msg: print(msg)


with Reverser() as what:
    print('Hello World')
    print('What is happening')

You must incorporate 'yield part' into try block when using @contextmanager.

 

'Computer Science > Python' 카테고리의 다른 글

Python: Concurrency with Futures  (0) 2022.05.09
Python: Coroutines  (0) 2022.05.03
Python: Iterator, Generator  (0) 2022.04.22
Python: Operator Overloading  (0) 2022.04.20
Python: Inheritance  (0) 2022.04.19