繁体   English   中英

Python中调用者function的访问变量

[英]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_outers_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") )

工作得很好。

我的两个问题是这些

  1. 有没有更好的方法来解决这个问题? (除了将各自的数据包装成字典并将这些字典显式传递给do_something()

  2. 有没有更优雅/更短的方法来实现reach() function?

干杯

没有,在我看来,应该没有优雅的方式来实现reach ,因为它引入了一个新的非标准间接,它真的很难理解、调试、测试和维护。 正如 Python 口头禅(尝试import this )所说:

显式优于隐式。

因此,只需通过 arguments。 来自未来的你会非常感谢今天的你。

我最终做的是

scope = locals()

并使scope可从do_something访问。 这样我就不必到达,但我仍然可以访问调用者的局部变量字典。 这与自己构建字典并传递它非常相似。

有没有更好的方法来解决这个问题? (除了将各自的数据包装成字典并将这些字典显式传递给 do_something())

明确地传递 dicts 是一种更好的方法。

你的提议听起来很不合常规。 当代码大小增加时,您必须将代码分解为模块化架构,并在模块之间使用干净的 API。 它还必须是易于理解、易于解释并且易于移交给其他程序员来修改/改进/调试它的东西。 您提出的建议听起来不是干净的 API,非常规,具有不明显的数据流。 我怀疑它可能会让许多程序员看到它时脾气暴躁。:)

另一种选择是使函数成为 class 的成员,数据位于 class 实例中。 如果您的问题可以建模为在数据 object 上运行的多个函数,那么这可能会很好。

我们可以变得更顽皮。

这是对“是否有更优雅/更短的方式来实现reach() function?”的答案。 一半的问题。

  1. 我们可以为用户提供更好的语法:而不是reach("foo")outer.foo

    这更好地键入,并且语言本身会立即告诉您是否使用了不能是有效变量的名称(属性名称和变量名称具有相同的约束)。

  2. 我们可以提出一个错误,以正确区分“这不存在”和“这被设置为None ”。

    如果我们真的想将这些情况一起涂抹,我们可以使用默认参数getattr ,或者try - except AttributeError

  3. 我们可以优化:不需要悲观地为所有帧一次构建一个足够大的列表。

    在大多数情况下,我们可能不需要 go 一直到调用堆栈的根。

  4. 仅仅因为我们不恰当地到达堆栈帧,违反了最重要的编程规则之一,即不让远处的事物无形地影响行为,并不意味着我们不能文明。

    如果有人试图在没有堆栈帧检查支持的情况下在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM