我想将上下文装饰器与带参数或不带参数的可能性结合起来。
让我们从一个可以使用参数和不使用参数的装饰器开始,例如:
import functools
def decorator(func=None, *, label=""):
if func is None:
return functools.partial(decorator, label=label)
@functools.wraps(func)
def wrap(*args, **kwargs):
result = func(*args, **kwargs)
print(f"RESULT {label}: {result}")
return result
return wrap
if __name__ == "__main__":
@decorator(label="with arguments")
def dec_args():
return 1
@decorator
def dec_no_args():
return 0
dec_args()
dec_no_args()
Run Code Online (Sandbox Code Playgroud)
还有ContextDecorator可以用作上下文管理器或装饰器的:
from contextlib import ContextDecorator
class ctxtdec(ContextDecorator):
def __init__(self, label:str=""):
self.label = label
print(f"initialized {self.label}")
def __enter__(self):
print(f"entered {self.label}")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited {self.label}")
if __name__ == "__main__":
def …Run Code Online (Sandbox Code Playgroud) 我有一个多个测试所需的现有上下文管理器。with我认为最好从这个上下文管理器中制作一个固定装置,并用@pytest.mark.usefixtures("my_fixture").
我可以将上下文管理器重新实现为固定装置,但这似乎是重复的代码。所以我想在新的装置中引用原始的上下文管理器。
这就是我所拥有的:
import my_context_manager
@pytest.fixture
def my_fixture(arg1, arg2):
with my_context_manager(arg1, arg2) as c:
yield c
Run Code Online (Sandbox Code Playgroud)
这是将现有上下文管理器转换为固定装置的适当方法吗?
我应该提到,我知道contextlib.ContextDecorator编写一个可以用作装饰器的上下文管理器。但我的上下文管理器需要参数,而当它们出现在像@my_context_decorator(arg1, arg2).
假设我想实现一个具有以下签名的 Python 脚本:
myscript.py INPUT OUTPUT
Run Code Online (Sandbox Code Playgroud)
...其中INPUT和分别OUTPUT代表脚本将读取和写入的文件的路径。
用于实现具有此类签名的脚本的代码可能具有以下构造:
with open(inputarg, 'r') as instream, open(outputarg, 'w') as outstream:
...
Run Code Online (Sandbox Code Playgroud)
...这里的inputarg和变量保存通过其和命令行参数outputarg传递给脚本的文件路径(字符串) 。INPUTOUTPUT
到目前为止没有什么特别或不寻常的事情。
但现在,假设对于脚本的版本 2,我想为用户提供-为其一个(或两个)参数传递特殊值的选项,以指示脚本应分别读取stdin和写入stdout。
换句话说,我希望以下所有形式都能产生相同的结果:
myscript.py INPUT OUTPUT
myscript.py - OUTPUT <INPUT
myscript.py INPUT - >OUTPUT
myscript.py - - <INPUT >OUTPUT
Run Code Online (Sandbox Code Playgroud)
现在,with之前的说法已经不再适用。一方面,表达式open('-', 'r')oropen('-', 'w')都会引发异常:
FileNotFoundError: [Errno 2] No such file or directory: '-'
Run Code Online (Sandbox Code Playgroud)
我无法想出一种方便的方法来扩展with上面基于 …
我有以下代码来使用 ExitStack 而不是 with 语句。
from contextlib import contextmanager
from contextlib import ExitStack
from tempfile import NamedTemporaryFile
@contextmanager
def myfile():
temp_file = NamedTemporaryFile(suffix='.txt')
temp_file.seek(0)
yield temp_file
os.unlink(temp_file.name)
with ExitStack() as stack:
files = []
for idx in range(5):
files.append(stack.enter_context(myfile()))
# do something with the files
Run Code Online (Sandbox Code Playgroud)
上面的代码给出了 5 个错误消息,如下所示
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpbupwinzt.txt'
Run Code Online (Sandbox Code Playgroud)
我是否以错误的方式使用 ExitStack?做上述事情的正确方法是什么。
注意:我无法更改myfile()功能,但可以更改其余代码。
所以我有一个简单的声明示例with。它适用于 Python 3.8 和 3.9:
class Foo:
def __enter__(self, *args):
print("enter")
def __exit__(self, *args):
print("exit")
with Foo() as f, Foo() as b:
print("Foo")
Run Code Online (Sandbox Code Playgroud)
输出(如预期):
enter
enter
Foo
exit
exit
Run Code Online (Sandbox Code Playgroud)
但如果我像这样添加括号,它只适用于 Python 3.9:
class Foo:
def __enter__(self, *args):
print("enter")
def __exit__(self, *args):
print("exit")
with (Foo() as f, Foo() as b):
print("Foo")
Run Code Online (Sandbox Code Playgroud)
3.8 中的输出:
File "foo.py", line 8
with (Foo() as f, Foo() as b):
^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)
我知道,我可以删除括号,但我不明白为什么它首先在 Python 3.9 中工作。我找不到相关的变更日志。
请参阅下面的 Python 3.10 代码片段:
import contextlib
class Foo:
@contextlib.contextmanager
@classmethod
def state(cls):
try:
yield
finally:
pass
with Foo.state():
pass
Run Code Online (Sandbox Code Playgroud)
它抛出一个TypeError:
Traceback (most recent call last):
File "/path/to/code/play/quick_play.py", line 12, in <module>
with Foo.state():
File "/path/to/.pyenv/versions/3.10.5/lib/python3.10/contextlib.py", line 281, in helper
return _GeneratorContextManager(func, args, kwds)
File "/path/to/.pyenv/versions/3.10.5/lib/python3.10/contextlib.py", line 103, in __init__
self.gen = func(*args, **kwds)
TypeError: 'classmethod' object is not callable
Run Code Online (Sandbox Code Playgroud)
可以用classmethod来装饰吗contextlib.contextmanager?如果是的话,该怎么做?
一些背景
我正在使用一个包,它允许您使用文件中存储的信息来计算有关行星的一些信息(例如它们的速度或位置)。该包包含加载和卸载文件的方法,因此其基本用法如下所示:
load(["File_1", "File_2"])
try:
function()
finally:
unload(["File_1", "File_2"])
Run Code Online (Sandbox Code Playgroud)
由于这是上下文管理器实用程序的教科书示例,并且该包缺少一个,因此我正在编写自己的示例。
class file_manager:
def __init__(self, file_list) -> None:
self.file_list = file_list
load(self.file_list)
return None
def __enter__(self) -> None:
return None
def __exit__(self, exc_type, exc_value, traceback) -> None:
unload(self.file_list)
return None
Run Code Online (Sandbox Code Playgroud)
使用新的上下文管理器,前面的示例可以重写如下:
with file_manager(["File_1", "File_2"]):
function()
Run Code Online (Sandbox Code Playgroud)
该方法保证如果出现错误__exit__,文件仍然会被卸载。function
我的问题
该load函数逐个加载文件,而不首先检查所有文件是否可用。因此,如果File_1存在,但File_2不存在,File_1则会被加载,并且在加载时会引发异常File_2。根据 python 文档:
with 语句保证如果
__enter__()方法返回且没有错误,则__exit__()始终会调用 then。
因此,在前面的情况下,程序的执行将在没有File_2被卸载的情况下结束。
我在寻找什么
我显然可以通过try...except在方法中使用一个子句来解决这个问题__init__():
def __init__(self, …Run Code Online (Sandbox Code Playgroud) 在我的代码中,我需要能够正确地打开和关闭设备,因此需要使用上下文管理器.虽然上下文管理器通常被定义为带有__enter__和__exit__方法的类,但似乎还有可能装饰一个与上下文管理器一起使用的函数(参见最近的帖子和另一个很好的例子).
在下面(工作)的代码片段中,我实现了两种可能性; 一个只需要将注释行与另一个交换:
import time
import contextlib
def device():
return 42
@contextlib.contextmanager
def wrap():
print("open")
yield device
print("close")
return
class Wrap(object):
def __enter__(self):
print("open")
return device
def __exit__(self, type, value, traceback):
print("close")
#with wrap() as mydevice:
with Wrap() as mydevice:
while True:
time.sleep(1)
print mydevice()
Run Code Online (Sandbox Code Playgroud)
我尝试的是运行代码并停止它CTRL-C.当我Wrap在上下文管理器中使用该类时,该__exit__方法被调用为已解决(文本'close'在终端中打印),但是当我尝试使用该wrap函数时,文本'close'不会打印到终奌站.
我的问题:代码片段是否存在问题,我是否遗漏了某些内容,或者为什么print("close")没有使用装饰函数调用该行?
给出文件名列表filenames = [...].
是否可能重写I/O安全的下一个列表理解:[do_smth(open(filename, 'rb').read()) for filename in filenames]?使用with语句,.close方法或其他东西.
另一个问题是:是否可能为下一个代码编写I/O安全列表理解?
results = []
for filename in filenames:
with open(filename, 'rb') as file:
results.append(do_smth(file.read()))
Run Code Online (Sandbox Code Playgroud) PyCharm警告这段代码,说最后一次返回是无法访问的:
def foo():
with open(...):
return 1
return 0
Run Code Online (Sandbox Code Playgroud)
我希望如果open()失败,第二次返回将会执行.谁是对的?
contextmanager ×10
python ×10
python-3.x ×2
class-method ×1
decorator ×1
file-io ×1
fixtures ×1
pycharm ×1
pytest ×1
python-2.7 ×1
python-3.9 ×1
stdin ×1
stdout ×1