Contextlib - Data Classes#
在 Python 中上下文管理器 (context) 语法 with context_manager_constructor(...) as obj: 语法是一个非常强大的语言特性, 也是一种非常优雅的资源管理方式. 官方标准库 contextlib 提供了一些工具函数, 用于简化上下文管理器的创建.
contextmanager#
test_contextmanager_1.py
1# -*- coding: utf-8 -*-
2
3"""
4这个例子展示了 contextmanager 的基本用法. 如果你用它来装饰一个简单函数, 那么这个函数的入参和
5yield 的值的 type hint 都会正常工作.
6"""
7
8import contextlib
9
10
11class User:
12 def __init__(self, name: str):
13 self.name = name
14
15 def say_hello(self):
16 print(f"Hello, I am {self.name}")
17
18
19@contextlib.contextmanager
20def start(name: str):
21 try:
22 print("Start")
23 user = User(name=name)
24 yield user
25 print("Happy End")
26 except Exception as e:
27 print(f"Exception: {e}")
28 raise e
29 finally:
30 print("Finally")
31
32
33# type hint in ``start()`` is working fine
34with start(name="Alice") as user:
35 # type hint in ``user`` is working fine
36 user.say_hello()
37
38"""
39Output:
40
41Start
42Hello, I am Alice
43Happy End
44Finally
45"""
46
47# type hint in ``start()`` is working fine
48with start(name="Alice") as user:
49 # type hint in ``user`` is working fine
50 user.say_hello()
51 raise ValueError("Something went wrong")
52
53"""
54Output:
55
56Start
57Hello, I am Alice
58Exception: Something went wrong
59Finally
60Traceback (most recent call last):
61 File "/path/to/learn_pylib-project/docs/source/contextlib/test_contextmanager_1.py", line 42, in <module>
62 raise ValueError("Something went wrong")
63ValueError: Something went wrong
64"""
test_contextmanager_2.py
1# -*- coding: utf-8 -*-
2
3"""
4这个例子展示了 contextmanager 和 classmethod 一起使用时的用法. @classmethod 必须在上面.
5由于标准库实现的时候还没有 type hint, 这个函数的入参和 yield 的值的 type hint 都 **不会** 正常工作.
6"""
7
8import typing as T
9import contextlib
10
11
12class User:
13 def __init__(self, name: str):
14 self.name = name
15
16 def say_hello(self):
17 print(f"Hello, I am {self.name}")
18
19 @classmethod
20 @contextlib.contextmanager
21 def start(cls, name: str) -> T.Generator["User", None, None]:
22 try:
23 print("Start")
24 user = cls(name=name)
25 yield user
26 print("Happy End")
27 except Exception as e:
28 print(f"Exception: {e}")
29 raise e
30 finally:
31 print("Finally")
32
33
34# type hint in ``start()`` is NOT working fine in PyCharm
35with User.start(name="Alice") as user:
36 # type hint in ``user`` is NOT working fine in PyCharm
37 user.say_hello()
38
39"""
40Output:
41
42Start
43Hello, I am Alice
44Happy End
45Finally
46"""
test_contextmanager_3.py
1# -*- coding: utf-8 -*-
2
3"""
4这个例子展示了 contextmanager 和 classmethod 一起使用时的用法. @classmethod 必须在上面.
5社区有一个库 `decorator <https://pypi.org/project/decorator/>`_ 对标准库做了一些改进,
6如果你使用的是 ``decorator.contextmanager`` 那么这个函数的入参和 yield 的值的 type hint 都 **会** 正常工作.
7"""
8
9from decorator import contextmanager
10
11
12class User:
13 def __init__(self, name: str):
14 self.name = name
15
16 def say_hello(self):
17 print(f"Hello, I am {self.name}")
18
19 @classmethod
20 @contextmanager
21 def start(cls, name: str): # 无需显式使用 ``-> T.Generator["User", None, None]``
22 try:
23 print("Start")
24 user = cls(name=name)
25 yield user
26 print("Happy End")
27 except Exception as e:
28 print(f"Exception: {e}")
29 raise e
30 finally:
31 print("Finally")
32
33
34# type hint in ``start()`` is working fine in PyCharm
35with User.start(name="Alice") as user:
36 user.say_hello() # type hint in ``user`` is working fine in PyCharm
37
38"""
39Output:
40
41Start
42Hello, I am Alice
43Happy End
44Finally
45"""