在这个问题中,我定义了一个包含上下文管理器的上下文管理器.完成此嵌套最简单的正确方法是什么?我最后打电话来self.temporary_file.__enter__()了self.__enter__().但是,self.__exit__我很确定我必须调用self.temporary_file.__exit__(type_, value, traceback)finally块,以防引发异常.如果出现问题,我应该设置type_,value和traceback参数self.__exit__吗?我检查过contextlib,但找不到任何实用程序来帮助解决这个问题.
来自问题的原始代码:
import itertools as it
import tempfile
class WriteOnChangeFile:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.temporary_file = tempfile.TemporaryFile('r+')
self.f = self.temporary_file.__enter__()
return self.f
def __exit__(self, type_, value, traceback):
try:
try:
with open(self.filename, 'r') as real_f:
self.f.seek(0)
overwrite = any(
l != real_l
for l, real_l in it.zip_longest(self.f, real_f))
except IOError:
overwrite = True
if overwrite:
with open(self.filename, 'w') as real_f:
self.f.seek(0) …Run Code Online (Sandbox Code Playgroud) 这应该工作,但只是说没有股票表 - 假设在上下文管理器内某处丢失了连接?
import sqlite3
from contextlib import contextmanager
@contextmanager
def doquery(conn, q, params=()):
c = conn.cursor()
c.execute(q, params)
conn.commit()
yield c
c.close()
with sqlite3.connect(':memory:') as db:
doquery(db,'''create table stocks
(date text, trans text, symbol text,
qty real, price real)''')
doquery(db,"""insert into stocks
values ('2006-01-05','BUY','RHAT',100,35.14)""")
with doquery(db, 'select * from stocks') as r:
for row in r:
print row
Run Code Online (Sandbox Code Playgroud) Python 3.4提供了这个简洁的工具来暂时重定向stdout:
# From https://docs.python.org/3.4/library/contextlib.html#contextlib.redirect_stdout
with redirect_stdout(sys.stderr):
help(pow)
Run Code Online (Sandbox Code Playgroud)
代码并不是非常复杂,但我不想一遍又一遍地写它,特别是因为有些想法已经进入它以使它重新进入:
class redirect_stdout:
def __init__(self, new_target):
self._new_target = new_target
# We use a list of old targets to make this CM re-entrant
self._old_targets = []
def __enter__(self):
self._old_targets.append(sys.stdout)
sys.stdout = self._new_target
return self._new_target
def __exit__(self, exctype, excinst, exctb):
sys.stdout = self._old_targets.pop()
Run Code Online (Sandbox Code Playgroud)
我想知道是否有一般方法使用该with语句来临时更改变量的值.从另外两个用例sys是sys.stderr和sys.excepthook.
在一个完美的世界中,这样的东西会起作用:
foo = 10
with 20 as foo:
print(foo) # 20
print (foo) # 10
Run Code Online (Sandbox Code Playgroud)
我怀疑我们能做到这一点,但也许这样的事情是可能的:
foo = 10
with temporary_set('foo', …Run Code Online (Sandbox Code Playgroud) 假设我们有一个类“A”,它本身就充当上下文管理器,因此它实现了
def __enter__()
def __exit__()
Run Code Online (Sandbox Code Playgroud)
界面。客户端代码直接使用with语句创建“A”对象是有效的。
现在,我们还有另一个类“B”,它封装了其他功能,并使用了“A”对象。
如果我们还想让“B”充当上下文管理器,那么管理它的“A”实例的正确方法是什么?
如若实施__enter__,并__exit__于“B”的呼叫__enter__,并__exit__在其上的一个对象实例(分别)?或者,还有更好的方法?
举一个具体的例子(这不是我在我的应用程序中使用的,这只是我想到的第一个非抽象例子)考虑两个类
DatabaseConnectionDatabaseConnectionPool单独使用单个是有效DatabaseConnection的,因此DatabaseConnection实现了上下文管理器接口。
DatabaseConnectionPool使用几个DatabaseConnections以及其他位和鲍勃。使用DatabaseConnectionPool(即“with”)应该对它的DatabaseConnection实例进行设置和拆除(以及它可能想做的任何其他事情)
更新: 我写了一些测试代码,我希望能给出以下输出:
在外部调用输入 在内部调用输入 在外部环境中... do_foo 调用了! 还在用外... 在内部调用退出 在外部调用退出 使用外部完成
但我得到了以下信息:
在外部调用输入 在内部调用输入 在内部调用退出 在外部环境中... do_foo 调用了! 还在用外... 在外部调用退出 使用外部完成
代码:
class Inner(object):
def __enter__(self):
print "Enter invoked on Inner"
return self
def __exit__(self, typ, val, tb):
print "Exit invoked on inner" …Run Code Online (Sandbox Code Playgroud) 我知道从上下文管理器的__exit__()方法中重新引发异常是不好的方式.所以,我想在实例上添加一个属性,该属性可以携带不可用的上下文信息,如果我让异常通过或如果我抓住它.这样可以避免重新提升它.
在异常上添加属性的替代方法是吞下异常,在实例上设置一些状态,该状态兼作相关的上下文管理器,然后检查该状态.问题是,这会导致捕获22,不是吗?由于异常意味着with正在退出块内执行.除了with再次进入阻止之外,没有办法重复操作,对吧?因此,一旦__exit__()方法返回,我试图存储上下文信息的实例就会消失.
简而言之:在方法中,如何操作待处理的实际异常(如果是,我将假设为此问题的假设)__exit__()?
有时,您需要动态定义值(如现在的datetime,随机字符串,随机整数,文件内容等),并在不同的步骤中使用它们,而不是显式或硬编码值.
所以,我的问题是如何在步骤中定义变量(正确的方法)在以下步骤中使用这些变量.
一些例子
Given A random string of length "100" as "my_text"
And I log in to my platform
And I ask to add the following post:
| title | description |
| Some example of title | {{my_text}} |
When I submit the post form
Then The posts table shows these posts:
| title | description |
| Some example of title | {{my_text}} |
And I delete any post containing in the description "{{my_text}}"
Run Code Online (Sandbox Code Playgroud)
这是一个基本的例子,试图解释为什么我想在步骤中定义变量并将它们保存在上下文中以便在以下步骤中使用它.
我的想法是修改before_step和after_step方法......在上下文中设置一个变量来存储我的自定义变量,如下所示:
def before_step(context): …Run Code Online (Sandbox Code Playgroud) 两者的主要区别是什么?我一直在研究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 …Run Code Online (Sandbox Code Playgroud) 首先,我需要道歉,因为我还不能为我的问题提供明确的MCVE。我的问题是关于我在代码库深处遇到的一个奇怪现象,我想了解这是如何发生的,所以在某种程度上,我首先要问的是如何为这种现象创建MCVE。
as在with根本不使用分配变量的语句中是否使用子句怎么会很重要?
我们正在使用 Airflow(Apache 项目),其中存在一个名为的类DAG。此类应该用作以下with子句的上下文管理器:
with DAG(**some_parameters) as dag:
do_something_with(dag)
Run Code Online (Sandbox Code Playgroud)
这按预期工作。
但是,在某些情况下,我们不会dag在with子句中使用该变量,因此 IDE 会发出警告,接下来将其重命名为_dag(以声明不使用),我尝试as dag完全删除该子句:
with DAG(**some_parameters):
do_something_without_passing_dag()
Run Code Online (Sandbox Code Playgroud)
根据我对 Python 的理解,这应该等同as dag于运行时带有子句的版本:
with DAG(**some_parameters) as dag:
do_something_without_passing_dag()
Run Code Online (Sandbox Code Playgroud)
但是,令人惊讶的是,在 Airflow 项目的背景下,两者之间似乎存在差异。使用该as dag子句,代码按预期工作;如果没有该as dag子句,则会显示错误(请参阅本文末尾)。令人沮丧的是,此错误出现在 Airflow 进程的日志中,并且根本不包含对我的代码的引用。
我需要指出的是,在 Airflow 上下文中,这些with语句位于小模块的顶层,因此该as语句会创建一个模块全局变量(如果存在)。我不知道这是否相关。如果是这样,我不明白为什么。
我的理解,它应该永远不会有任何区别我是否提供了一个as条款或不,如果我完全不使用变量。尽管如此,情况似乎仍然如此。
我已经调查了三个方面:
__enter__()了DAG …我想要一个 pyplot 图形的上下文管理器,基本上像这样:
from contextlib import contextmanager
import matplotlib.pyplot as plt
@contextmanager
def subplots():
(fig, ax) = plt.subplots()
try:
yield (fig, ax)
finally:
plt.close(fig)
Run Code Online (Sandbox Code Playgroud)
是否可以对返回的元组实现类型提示?天真的
import typing
def subplots() -> typing.Tuple[plt.Figure, plt.Axes]
Run Code Online (Sandbox Code Playgroud)
不起作用。
我正在学习上下文管理器,并试图自己构建一个。以下是一个虚拟上下文管理器,它以读取模式打开一个文件(我知道我可以这样做with open(...): ...。这只是我构建的一个示例,旨在帮助我了解如何创建自己的上下文管理器):
@contextmanager
def open_read(path: str):
f = open(path, 'r')
print('open')
yield f
f.close()
print('closed')
def foo():
try:
with open_read('main.py') as f:
print(f.readline())
raise Exception('oopsie')
except Exception:
pass
print(f.readline())
foo()
Run Code Online (Sandbox Code Playgroud)
我希望这段代码能够打印:
open
<line 1 of a.txt>
closed
ValueError: I/O operation on closed file.
Run Code Online (Sandbox Code Playgroud)
但它打印的是:
open
<line 1 of a.txt>
<line 2 of a.txt>
Run Code Online (Sandbox Code Playgroud)
它没有关闭文件!
这似乎与 python 的文档相矛盾,该文档声明无论语句成功退出还是出现异常__exit__都会调用该函数:with
目的。退出(自身、exc_type、exc_value、回溯)
退出与该对象相关的运行时上下文。参数描述导致上下文退出的异常。如果上下文退出时没有异常,则所有三个参数都将为 None。
有趣的是,当我重新实现上下文管理器(如下所示)时,它按预期工作:
open
<line 1 of a.txt>
closed
ValueError: I/O operation on closed …Run Code Online (Sandbox Code Playgroud) contextmanager ×10
python ×9
connection ×1
python-2.x ×1
python-3.x ×1
sqlite ×1
testing ×1
type-hinting ×1