Mit*_*ras 7 python contextmanager python-decorators
两者的主要区别是什么?我一直在研究Python并遇到了它们。装饰器本质上是一个包装另一个函数的函数,您可以在执行特定函数之前和之后执行任何操作。
def my_decorator(some_function):
def wrapper(*args, **kwargs):
print("Do something before the function is called")
some_function(*args, **kwargs)
print("Do something after the function is called")
return wrapper
@my_decorator
def addition(a, b):
result = a+b
print("Addition of {} and {} is {}".format(a,b,result))
Run Code Online (Sandbox Code Playgroud)
但是学习完Context Manager之后,我不禁注意到它也具有进入和退出的功能,您可以在其中执行大多数类似的操作。
from contextlib import contextmanager
@contextmanager
def open_file(path, mode):
the_file = open(path, mode)
yield the_file
the_file.close()
files = []
for x in range(100000):
with open_file('foo.txt', 'w') as infile:
files.append(infile)
for f in files:
if not f.closed:
print('not closed')
Run Code Online (Sandbox Code Playgroud)
收益率之前的所有内容都被视为“输入”的一部分,而收益率部分之后的所有内容均被视为“退出”的一部分。
尽管上下文管理器和装饰器在语法上都不同,但是可以将它们的行为视为相似。那么区别是什么呢?什么时候应该使用其中任何一种,会有哪些不同的方案?
Mar*_*ers 10
它们是完全独立的概念,不应一视同仁。
装饰器使您可以在定义函数或类时对其进行扩充或替换。这远不止是在函数调用之前或之后执行事情。当然,您可以使用特定的装饰器在函数调用之前和之后执行某些操作,前提是不会引发异常,也可以显式处理异常。但是,您也可以使用装饰器向功能对象添加属性,或更新某种注册表。或返回完全不同的内容并忽略原始功能。或产生一个包装器来处理传入的参数或原始函数的返回值。上下文管理器无法执行任何这些操作。
另一方面,上下文管理器使您可以抽象出try: ... finally:结构,无论块如何退出,都可以在块末执行更多代码。即使该块引发异常或用于return退出函数__exit__,无论如何,上下文管理器方法仍将被调用。上下文管理器甚至可以抑制块中引发的任何异常。
否则,这两个概念根本不相关。当需要在定义函数或类时对其进行处理时,请使用装饰器。当您要清除或在块结束后执行其他操作时,请使用上下文管理器。
使用创建的任何上下文管理器contextlib.contextmanager 也是装饰器,如下所述: https: //docs.python.org/3/library/contextlib.html#using-a-context-manager-as-a-function-decorator
上下文管理器可用于通过设置和拆卸步骤来包装代码。装饰器是一种更通用的构造,允许我们以多种方式修改函数,包括用设置/拆卸逻辑包装它们。因此,很自然地会问:为什么我们不能使用上下文管理器作为装饰器?
我们可以,事实上 contextlib 已经为您做到了。如果我们像这样编写一个上下文管理器:
from contextlib import contextmanager
@contextmanager
def my_context():
print("setup")
yield
print("teardown")
Run Code Online (Sandbox Code Playgroud)
with我们可以将其用作块中的上下文管理器,也可以将其用作装饰器:
def foo():
with my_context():
print("foo ran")
@my_context()
def bar():
print("bar ran")
Run Code Online (Sandbox Code Playgroud)
>>> foo()
setup
foo ran
teardown
>>> bar()
setup
bar ran
teardown
Run Code Online (Sandbox Code Playgroud)
你应该使用哪个?
with当您的封闭代码需要访问上下文管理器返回的对象时,请使用块,例如文件处理:
with open("my_file.txt") as file:
file.read() # needs access to the file object
Run Code Online (Sandbox Code Playgroud)
当整个函数需要包装在上下文中并且不需要任何上下文变量时,用作装饰器:
@contextmanager
def suppress_all_exceptions():
try: yield
except: pass
@suppress_all_exceptions()
def div_by_zero():
print("hi")
x = 1 / 0 # exception suppressed
Run Code Online (Sandbox Code Playgroud)
注意:同样的功能也可以通过子类化来实现contextlib.ContextDecorator:
class MyContext(contextlib.ContextDecorator):
def __enter__(): ...
def __exit__(*errs): ...
Run Code Online (Sandbox Code Playgroud)