【发布时间】:2019-08-22 22:29:42
【问题描述】:
我正在尝试为 Python 创建一个快速而肮脏的缓存系统,使用可以使上下文管理器有条件地跳过其上下文中的代码的技巧 - 请参阅Skipping execution of -with- block。我偶然发现了一个奇怪的失败案例,我想知道是否有人可以帮助理解和解决这个问题。
在任何人这么说之前,我知道我在做的事情很糟糕,我不应该这样做,等等等等。
无论如何,这里是棘手的上下文管理器的代码:
import sys
import inspect
class SkippableContext(object):
def __init__(self,mode=0):
"""
if mode = 0, proceed as normal
if mode = 1, do not execute block
"""
self.mode=mode
def __enter__(self):
if self.mode==1:
print(' ... Skipping Context')
# Do some magic
sys.settrace(lambda *args, **keys: None)
frame = inspect.currentframe(1)
frame.f_trace = self.trace
return 'SET BY TRICKY CONTEXT MANAGER!!'
def trace(self, frame, event, arg):
raise
def __exit__(self, type, value, traceback):
return True
这里是测试代码:
print('==== First Pass with skipping disabled ====')
c='not set'
with SkippableContext(mode=0) as c:
print('Should Get into here')
c = 'set in context'
print('c: {}'.format(c))
print('==== Second Pass with skipping enabled ====')
c='not set'
with SkippableContext(mode=1) as c:
print('This code is not printed')
c = 'set in context'
print('c: {}'.format(c))
c='not set'
with SkippableContext(mode=1) as c:
print('This code is not printed')
c = 'set in context'
print('c: {}'.format(c))
print('==== Third Pass: Same as second pass but in a loop ====')
for i in range(2):
c='not set'
with SkippableContext(mode=1) as c: # For some reason, assinging c fails on the second iteration!
print('This code is not printed')
c = 'set in context'
print('c: {}'.format(c))
测试代码生成的输出和预期的一样,除了最后一行没有设置c:
==== First Pass with skipping disabled ====
Should Get into here
c: set in context
==== Second Pass with skipping enabled ====
... Skipping Context
c: SET BY TRICKY CONTEXT MANAGER!!
... Skipping Context
c: SET BY TRICKY CONTEXT MANAGER!!
==== Third Pass: Same as second pass but in a loop ====
... Skipping Context
c: SET BY TRICKY CONTEXT MANAGER!!
... Skipping Context
c: not set
为什么第二次循环没有设置c?是否有一些 hack 可以修复此 hack 中的错误?
【问题讨论】:
-
在
inspect.currentframe(1)传递的1是什么?我没有看到currentFrame接受任何争论。 -
那是“堆栈中的一步”-即。上下文管理器的调用者的框架。
-
很奇怪。我找不到对带有参数的函数
inspect.currentframe的单个文档引用。这在 IntelliJ 中不适用于我。我只是想玩弄它。这个答案似乎在 Python 2 中,但即使是 2.7 文档也没有任何参数。 -
AFAIK,
frame = sys._getframe(1)会做正确的事——但这并不能解决/解释循环问题。 -
为了让大家明白,在Python 2中(甚至在.7.16中),
inspect.currentframe被定义为@987654333 @ (尽管文档没有反映它)。此外,该行为可以概括:当增加循环步骤时,偶数(基于0)迭代将产生SET BY TRICKY CONTEXT MANAGER!!,而奇数迭代将产生not set。
标签: python contextmanager