Jen*_*ens 11 python python-3.x
我怀疑我想做的事情在Python中不是那么干净.以下是一些相互调用的嵌套函数.(通常,它们不必是词法范围的,但需要动态地相互调用.)
def outer() :
s_outer = "outer\n"
def inner() :
s_inner = "inner\n"
do_something()
inner()
Run Code Online (Sandbox Code Playgroud)
现在,当我打电话do_something(),然后我想访问的调用函数的变量进一步向上调用堆栈,在这种情况下s_outer和s_inner.
不幸的是,nonlocal这里的关键字只有在我定义函数do_something()内部时才有帮助inner().但是,如果我在同一级别定义它,outer()则nonlocal关键字将不起作用.
但是,我想do_something()从各种其他函数调用,但始终在各自的上下文中执行它并访问它们各自的范围.
感觉顽皮我然后写了一个小的访问者,我可以从do_something()这里调用:
def reach(name) :
for f in inspect.stack() :
if name in f[0].f_locals : return f[0].f_locals[name]
return None
Run Code Online (Sandbox Code Playgroud)
然后
def do_something() :
print( reach("s_outer"), reach("s_inner") )
Run Code Online (Sandbox Code Playgroud)
工作得很好.
我的两个问题是这些
有没有更好的方法来解决这个问题?(除了将各自的数据包装成dicts并明确地将这些dicts传递给do_something())
是否有更优雅/缩短的方式来实现该reach()功能?
干杯!
没有,在我看来,应该没有优雅的实现方式,reach因为它引入了一种新的非标准间接方式,它真的很难理解、调试、测试和维护。正如 Python 口头禅 (try import this) 所说:
显式优于隐式。
所以,只需传递参数。未来的你将从今天开始真的很感激你。
我最终做的是
scope = locals()
Run Code Online (Sandbox Code Playgroud)
并scope从do_something. 这样我就不必访问了,但我仍然可以访问调用者的局部变量字典。这与自己构建字典并将其传递非常相似。
我们可以变得更顽皮。
reach()这是对“是否有更优雅/更简短的方式来实现该功能?”的回答。问题的一半。
我们可以为用户提供更好的语法:而不是reach("foo"), outer.foo。
这更适合键入,并且语言本身会立即告诉您是否使用了不能是有效变量的名称(属性名称和变量名称具有相同的约束)。
我们可以提出一个错误,以正确区分“这不存在”和“这已设置为None”。
如果我们真的想将这些情况混合在一起,我们可以getattr使用默认参数或try- except AttributeError。
我们可以优化:不需要悲观地一次性构建一个足够大的列表来容纳所有帧。
在大多数情况下,我们可能不需要一路走到调用堆栈的根部。
仅仅因为我们不恰当地到达堆栈帧,违反了最重要的编程规则之一,即不让远处的事物无形地影响行为,并不意味着我们不能文明。
如果有人尝试在没有堆栈帧检查支持的情况下在 Python 上使用这个 Serious API 进行实际工作,我们应该帮助他们知道。
import inspect
class OuterScopeGetter(object):
def __getattribute__(self, name):
frame = inspect.currentframe()
if frame is None:
raise RuntimeError('cannot inspect stack frames')
sentinel = object()
frame = frame.f_back
while frame is not None:
value = frame.f_locals.get(name, sentinel)
if value is not sentinel:
return value
frame = frame.f_back
raise AttributeError(repr(name) + ' not found in any outer scope')
outer = OuterScopeGetter()
Run Code Online (Sandbox Code Playgroud)
出色的。现在我们可以这样做:
>>> def f():
... return outer.x
...
>>> f()
Traceback (most recent call last):
...
AttributeError: 'x' not found in any outer scope
>>>
>>> x = 1
>>> f()
1
>>> x = 2
>>> f()
2
>>>
>>> def do_something():
... print(outer.y)
... print(outer.z)
...
>>> def g():
... y = 3
... def h():
... z = 4
... do_something()
... h()
...
>>> g()
3
4
Run Code Online (Sandbox Code Playgroud)
变态优雅地实现了。
(PS这是我的库中更完整的实现的简化只读版本dynamicscope。)
| 归档时间: |
|
| 查看次数: |
5033 次 |
| 最近记录: |