[英]Access variables of caller function in Python
我怀疑我想做的事情在 Python 中不太可行。 下面是一些相互调用的嵌套函数。 (一般来说,它们不必在词法范围内,但需要动态地相互调用。)
def outer() :
s_outer = "outer\n"
def inner() :
s_inner = "inner\n"
do_something()
inner()
现在,当我调用do_something()
时,我想进一步访问调用堆栈中调用函数的变量,在本例中s_outer
和s_inner
。
不幸的是,这里的nonlocal
关键字只有在我在inner()
function 中定义do_something()
时才对我有帮助。 但是,如果我将它定义在与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
接着
def do_something() :
print( reach("s_outer"), reach("s_inner") )
工作得很好。
我的两个问题是这些
有没有更好的方法来解决这个问题? (除了将各自的数据包装成字典并将这些字典显式传递给do_something()
)
有没有更优雅/更短的方法来实现reach()
function?
干杯
没有,在我看来,应该没有优雅的方式来实现reach
,因为它引入了一个新的非标准间接,它真的很难理解、调试、测试和维护。 正如 Python 口头禅(尝试import this
)所说:
显式优于隐式。
因此,只需通过 arguments。 来自未来的你会非常感谢今天的你。
我最终做的是
scope = locals()
并使scope
可从do_something
访问。 这样我就不必到达,但我仍然可以访问调用者的局部变量字典。 这与自己构建字典并传递它非常相似。
有没有更好的方法来解决这个问题? (除了将各自的数据包装成字典并将这些字典显式传递给 do_something())
明确地传递 dicts 是一种更好的方法。
你的提议听起来很不合常规。 当代码大小增加时,您必须将代码分解为模块化架构,并在模块之间使用干净的 API。 它还必须是易于理解、易于解释并且易于移交给其他程序员来修改/改进/调试它的东西。 您提出的建议听起来不是干净的 API,非常规,具有不明显的数据流。 我怀疑它可能会让许多程序员看到它时脾气暴躁。:)
另一种选择是使函数成为 class 的成员,数据位于 class 实例中。 如果您的问题可以建模为在数据 object 上运行的多个函数,那么这可能会很好。
我们可以变得更顽皮。
这是对“是否有更优雅/更短的方式来实现reach()
function?”的答案。 一半的问题。
我们可以为用户提供更好的语法:而不是reach("foo")
, outer.foo
。
这更好地键入,并且语言本身会立即告诉您是否使用了不能是有效变量的名称(属性名称和变量名称具有相同的约束)。
我们可以提出一个错误,以正确区分“这不存在”和“这被设置为None
”。
如果我们真的想将这些情况一起涂抹,我们可以使用默认参数getattr
,或者try
- except AttributeError
。
我们可以优化:不需要悲观地为所有帧一次构建一个足够大的列表。
在大多数情况下,我们可能不需要 go 一直到调用堆栈的根。
仅仅因为我们不恰当地到达堆栈帧,违反了最重要的编程规则之一,即不让远处的事物无形地影响行为,并不意味着我们不能文明。
如果有人试图在没有堆栈帧检查支持的情况下在 Python 上使用这个严重的 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()
出色的。 现在我们可以这样做:
>>> 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
变态优雅地实现了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.