在 python 中尝试使用生成器最终阻止
Try finally block with generator in python
谁能给我解释一下这段代码中 generator
和 try except
的概念:
from contextlib import contextmanager
@contextmanager
def file_open(path):
try:
f_obj = open(path, 'w')
yield f_obj
except OSError:
print("We had an error!")
finally:
print('Closing file')
f_obj.close()
if __name__ == '__main__':
with file_open('test.txt') as fobj:
fobj.write('Testing context managers')
据我所知,不管 try
中的表达式是否正确,finally 总是被执行。所以在我看来,这段代码应该像这样工作:如果我们没有异常,我们打开文件,转到生成器,然后我们转到 finally 块和函数中的 return。但我无法理解 generator
在此代码中的工作原理。我们只使用了一次,这就是为什么我们不能在文件中写入所有文本。但我认为我的想法是不正确的。为什么?
所以,第一,您的实施不正确。您将尝试关闭打开的文件对象,即使它无法打开,这是一个问题。在这种情况下你需要做的是:
@contextmanager
def file_open(path):
try:
f_obj = open(path, 'w')
try:
yield f_obj
finally:
print('Closing file')
f_obj.close()
except OSError:
print("We had an error!")
或更简单地说:
@contextmanager
def file_open(path):
try:
with open(path, 'w') as f_obj:
yield f_obj
print('Closing file')
except OSError:
print("We had an error!")
到“发电机一般是如何工作的?”我会推荐你参考 the existing question 关于那个主题。这个特定的案例很复杂,因为使用 @contextlib.contextmanager
装饰器 将生成器重新用于 一个基本上不相关的目的,使用它们在两种情况下天生暂停的事实:
- 创建时(直到请求第一个值)
- 在每个
yield
上(当请求每个后续值时)
实施上下文管理。
contextmanager
只是滥用它来制作这样的 class(actual source code 覆盖边缘情况相当复杂):
class contextmanager:
def __init__(self, gen):
self.gen = gen # Receives generator in initial state
def __enter__(self):
return next(self.gen) # Advances to first yield, returning the value it yields
def __exit__(self, *args):
if args[0] is not None:
self.gen.throw(*args) # Plus some complicated handling to ensure it did the right thing
else:
try:
next(self.gen) # Check if it yielded more than once
except StopIteration:
pass # Expected to only yield once
else:
raise RuntimeError(...) # Oops, it yielded more than once, that's not supposed to happen
允许生成器的协程元素支持更简单的方法来编写简单的上下文管理器。
谁能给我解释一下这段代码中 generator
和 try except
的概念:
from contextlib import contextmanager
@contextmanager
def file_open(path):
try:
f_obj = open(path, 'w')
yield f_obj
except OSError:
print("We had an error!")
finally:
print('Closing file')
f_obj.close()
if __name__ == '__main__':
with file_open('test.txt') as fobj:
fobj.write('Testing context managers')
据我所知,不管 try
中的表达式是否正确,finally 总是被执行。所以在我看来,这段代码应该像这样工作:如果我们没有异常,我们打开文件,转到生成器,然后我们转到 finally 块和函数中的 return。但我无法理解 generator
在此代码中的工作原理。我们只使用了一次,这就是为什么我们不能在文件中写入所有文本。但我认为我的想法是不正确的。为什么?
所以,第一,您的实施不正确。您将尝试关闭打开的文件对象,即使它无法打开,这是一个问题。在这种情况下你需要做的是:
@contextmanager
def file_open(path):
try:
f_obj = open(path, 'w')
try:
yield f_obj
finally:
print('Closing file')
f_obj.close()
except OSError:
print("We had an error!")
或更简单地说:
@contextmanager
def file_open(path):
try:
with open(path, 'w') as f_obj:
yield f_obj
print('Closing file')
except OSError:
print("We had an error!")
到“发电机一般是如何工作的?”我会推荐你参考 the existing question 关于那个主题。这个特定的案例很复杂,因为使用 @contextlib.contextmanager
装饰器 将生成器重新用于 一个基本上不相关的目的,使用它们在两种情况下天生暂停的事实:
- 创建时(直到请求第一个值)
- 在每个
yield
上(当请求每个后续值时)
实施上下文管理。
contextmanager
只是滥用它来制作这样的 class(actual source code 覆盖边缘情况相当复杂):
class contextmanager:
def __init__(self, gen):
self.gen = gen # Receives generator in initial state
def __enter__(self):
return next(self.gen) # Advances to first yield, returning the value it yields
def __exit__(self, *args):
if args[0] is not None:
self.gen.throw(*args) # Plus some complicated handling to ensure it did the right thing
else:
try:
next(self.gen) # Check if it yielded more than once
except StopIteration:
pass # Expected to only yield once
else:
raise RuntimeError(...) # Oops, it yielded more than once, that's not supposed to happen
允许生成器的协程元素支持更简单的方法来编写简单的上下文管理器。