Python 中上下文管理器和装饰器之间的区别
Difference between Context Managers and Decorators in Python
两者的主要区别是什么?我一直在研究 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))
但是在学习了 Context Manager 之后,我不禁注意到它也有一个 enter 和 exit,你可以在那里做最相似的操作。
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')
yield 之前的所有内容都被视为 "enter" 的一部分,而 yield 之后的所有内容都被视为 "exit" 的一部分。
虽然上下文管理器和装饰器在语法上不同,但它们的行为可以看作是相似的。那么区别是什么呢?应该使用其中任何一种的不同场景是什么?
它们是完全不同的概念。
上下文管理器是与 python with
关键字一起使用的对象。它在进入块和退出块时运行代码。
装饰器是对函数或 class 定义的修改。它运行代码来替换正在定义的函数。
@D
def Y(...):
...
只是另一种写法
def Y(...):
....
Y = D(Y)
它们是完全不同的概念,不应从同一角度看待。
装饰器可以让您增加或替换函数或 class 已定义的 。这比仅仅在函数调用之前或之后执行事情要广泛得多。当然,您的特定装饰器允许您在函数调用之前和之后做一些事情,前提是没有引发异常,或者您显式处理异常。但您也可以使用装饰器向函数对象添加属性,或更新某种注册表。或者 return 一些完全不同的东西而忽略原来的功能。或者生成一个包装器来处理传入的参数,或者原始函数的 return 值。上下文管理器不能做任何这些事情。
另一方面,上下文管理器可以让您抽象化 try: ... finally:
constructs,因为 无论块如何退出 ,您都可以在块的末尾。即使块引发异常,或使用 return
退出函数,上下文管理器 __exit__
方法仍将被调用,regardless。上下文管理器甚至可以抑制块中引发的任何异常。
这两个概念在其他方面完全没有关系。当你需要对函数或 classes 做一些事情时使用装饰器,当它们被定义时。当您想在块结束后进行清理或采取其他操作时,请使用上下文管理器。
好想法,确实这些概念有很多相似之处,虽然有重要的区别,所以将它们视为完全不同的概念更安全
两者的主要区别是什么?我一直在研究 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))
但是在学习了 Context Manager 之后,我不禁注意到它也有一个 enter 和 exit,你可以在那里做最相似的操作。
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')
yield 之前的所有内容都被视为 "enter" 的一部分,而 yield 之后的所有内容都被视为 "exit" 的一部分。
虽然上下文管理器和装饰器在语法上不同,但它们的行为可以看作是相似的。那么区别是什么呢?应该使用其中任何一种的不同场景是什么?
它们是完全不同的概念。
上下文管理器是与 python with
关键字一起使用的对象。它在进入块和退出块时运行代码。
装饰器是对函数或 class 定义的修改。它运行代码来替换正在定义的函数。
@D
def Y(...):
...
只是另一种写法
def Y(...):
....
Y = D(Y)
它们是完全不同的概念,不应从同一角度看待。
装饰器可以让您增加或替换函数或 class 已定义的 。这比仅仅在函数调用之前或之后执行事情要广泛得多。当然,您的特定装饰器允许您在函数调用之前和之后做一些事情,前提是没有引发异常,或者您显式处理异常。但您也可以使用装饰器向函数对象添加属性,或更新某种注册表。或者 return 一些完全不同的东西而忽略原来的功能。或者生成一个包装器来处理传入的参数,或者原始函数的 return 值。上下文管理器不能做任何这些事情。
另一方面,上下文管理器可以让您抽象化 try: ... finally:
constructs,因为 无论块如何退出 ,您都可以在块的末尾。即使块引发异常,或使用 return
退出函数,上下文管理器 __exit__
方法仍将被调用,regardless。上下文管理器甚至可以抑制块中引发的任何异常。
这两个概念在其他方面完全没有关系。当你需要对函数或 classes 做一些事情时使用装饰器,当它们被定义时。当您想在块结束后进行清理或采取其他操作时,请使用上下文管理器。
好想法,确实这些概念有很多相似之处,虽然有重要的区别,所以将它们视为完全不同的概念更安全