我经常需要用别的东西临时切换一个变量的值,做一些依赖于这个变量的计算,然后将变量恢复到它的原始值。例如:
var = 0
# Assign temporary value and do computation
var_ori = var
var = 1
do_something_with_var() # Function that reads the module level var variable
# Reassign original value
var = var_ori
Run Code Online (Sandbox Code Playgroud)
这似乎是使用上下文管理器(with语句)的明显机会。Python 标准库是否包含任何这样的上下文管理器?
我知道这种事情通常由其他更好的方法处理,而不是临时更改变量。然而,我并不是要求明显的解决方法。
在我的实际工作案例中,我无法更改该do_something_with_var功能。实际上,这甚至不是一个函数,而是作为元编程的一部分在全局命名空间上下文中求值的一串代码。我给出的例子是我能想到的最简单的例子,它使我的问题与临时变量有关。我没有要求获得我的示例代码的解决方法(正确版本),而是要求获得我书面问题的答案。
密切相关: 在python中,在setup/teardown中使用上下文管理器是否有一个很好的习惯用法
我有一个上下文管理器,用于测试以修复时间/时区.我想把它放在pytest funcarg(或夹具中,我们使用pytest2.2.3,但我可以向后翻译).我可以这样做:
def pytest_funcarg__fixedTimezone(request):
# fix timezone to match Qld, no DST to worry about and matches all
# Eastern states in winter.
fixedTime = offsetTime.DisplacedRealTime(tz=' Australia/Brisbane')
def setup():
fixedTime.__enter__()
return fixedTime
def teardown(fixedTime):
# this seems rather odd?
fixedTime.__exit__(None, None, None)
Run Code Online (Sandbox Code Playgroud)
......但它有点icky.在相关的问题中,jsbueno指出:问题在于,__exit__如果发生异常,您的代码无法正确调用对象的方法.
他的回答使用了元类方法.但这对pytest来说并不是那么有用,因为pytest通常只是函数,而不是类.那么解决这个问题的方法是什么?什么涉及测试钩?
我对Python with声明的理解如下:
withstatement = with+ expression + as+ target + :+ suit
__enter__返回一个目标值__exit__调用上下文管理器的方法我知道可以在step2和step3中处理异常,我的问题是如果在执行表达式的step1期间抛出异常,我可以获得上下文管理器吗?
如果不是,这是否意味着该with声明只是确保诉讼被正确执行和关闭?
就像with open("file") as f,如果文件不存在会发生什么?
我是否可以拥有一个偶尔不会产生的上下文管理器,在这种情况下,with语句中的代码根本不会被执行?
import contextlib
@contextlib.contextmanager
def MayNotYield(to_yield):
if to_yield:
yield
with MayNotYield(True):
print 'This works.'
with MayNotYield(False):
print 'This errors.'
Run Code Online (Sandbox Code Playgroud)
我可以要求用户使用try-catch包装with语句,但这不是首选.我也可以做以下但它也很难看.
import contextlib
@contextlib.contextmanager
def AlwaysYields(to_yield):
if to_yield:
yield 1
else:
yield 2
with AlwaysYields(True) as result:
if result == 1:
print 'This works.'
Run Code Online (Sandbox Code Playgroud) 我有一个类,可以将 API 引发的低级异常转换为高级异常。该类充满了复杂、重复的错误处理逻辑。我正在寻找减少这种重复的Pythonic 方法。
这是一个人为的例子。
class ApiWrapperException(Exception):
pass
class ApiWrapper(object):
def __init__(self, api):
self._api = api
def do_one_thing(self):
print 'do_one_thing stuff before API call'
try:
self._api.do_one_thing()
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
print 'do_one_thing stuff after API call'
def do_another_thing(self):
print 'do_another_thing stuff before API call'
try:
self._api.do_another_thing()
except ApiException:
print 'ApiWrapper caught an ApiException. Doing complicated error handling logic. Raising a different exception.'
raise ApiWrapperException
print 'do_another_thing …Run Code Online (Sandbox Code Playgroud) 在这个问题中,我定义了一个包含上下文管理器的上下文管理器.完成此嵌套最简单的正确方法是什么?我最后打电话来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) 有没有办法在Python中编写一个会出错的类,除非它与with语句一起使用?
# Okay:
with Foo() as f1:
f1.func1()
f1.func2()
# Not okay:
f2 = Foo()
f2.func1()
Run Code Online (Sandbox Code Playgroud)
我可以手动完成:__enter__设置一个标志并让其他方法检查该标志.但有更好的方法吗?
以下是不那么自然的代码:
class Foo(object):
def __init__(self):
self._entered = False
def __enter__(self):
self._entered = True
return self
def _verify_entered(self):
if not self._entered:
raise Exception("Didn't get call to __enter__")
def __exit__(self, typ, val, traceback):
self._verify_entered()
print("In __exit__")
def func1(self):
self._verify_entered()
# do stuff ...
def func2(self):
self._verify_entered()
# do other stuff
Run Code Online (Sandbox Code Playgroud) 我正在将代码从 sqlite 数据库移动到 mysql,并且上下文管理器出现问题,出现以下属性错误。
我尝试过 mydb.cursor() 作为光标、mydb: 等的组合...
mydb = mysql.connector.connect(
host="localhost",
user="root",
passwd="",
database="database_name"
cur = mydb.cursor()
Run Code Online (Sandbox Code Playgroud)
with mydb as cursor:
AttributeError: __enter__
Run Code Online (Sandbox Code Playgroud) 正如标题所述,有没有办法做到这样的事情:
def call_back():
if called inside context:
print("running in context")
else:
print("called outside context")
Run Code Online (Sandbox Code Playgroud)
这将导致:
with CTM() as context:
call_back()
call_back()
>>> "running in context"
>>> "called outside context"
Run Code Online (Sandbox Code Playgroud) 我知道一般来说 python 只会为类、函数等创建新的作用域,但我对astry/ except 块或上下文管理器中的语句感到困惑。在块内部分配的变量可以在块外部访问,这是有道理的,但是与as则不然。
所以这失败了:
try:
raise RuntimeError()
except RuntimeError as error:
pass
print(repr(error))
Run Code Online (Sandbox Code Playgroud)
但这成功了:
try:
raise RuntimeError()
except RuntimeError as e:
error = e
print(repr(error))
Run Code Online (Sandbox Code Playgroud)
与 绑定的变量发生了什么as,为什么正常的 python 作用域规则不适用?PEP表明它只是一个正常绑定的 python 变量,但情况似乎并非如此。