简体   繁体   English

Python中调用者function的访问变量

[英]Access variables of caller function in Python

My suspicion is that what I want to do isn't quite doable in a clean way in Python.我怀疑我想做的事情在 Python 中不太可行。 Here are a few nested functions that call each other.下面是一些相互调用的嵌套函数。 (In general, they don't have to be lexically scoped, but need to dynamically call each other.) (一般来说,它们不必在词法范围内,但需要动态地相互调用。)

def outer() :
    s_outer = "outer\n"

    def inner() :
        s_inner = "inner\n"
        do_something()

    inner()

Now when I call do_something() then I'd like to access the variables of the calling functions further up the callstack, in this case s_outer and s_inner .现在,当我调用do_something()时,我想进一步访问调用堆栈中调用函数的变量,在本例中s_outers_inner

Unfortunately, the nonlocal keyword here helps me only if I define do_something() inside of the inner() function.不幸的是,这里的nonlocal关键字只有在我在inner() function 中定义do_something()时才对我有帮助。 However, if I define it at the same level as outer() then the nonlocal keyword won't work.但是,如果我将它定义在与outer()相同的级别,那么nonlocal关键字将不起作用。

However, I want to call do_something() from various other functions, but always execute it in their respective context and access their respective scopes.但是,我想从各种其他函数调用do_something() ,但始终在它们各自的上下文中执行它并访问它们各自的范围。

Feeling naughty I then wrote a small accessor that I can call from within do_something() like this:感觉很顽皮,然后我写了一个小访问器,我可以从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 

and then接着

def do_something() :
    print( reach("s_outer"), reach("s_inner") )

works just fine.工作得很好。

My two questions are these我的两个问题是这些

  1. Is there a better way to solve this problem?有没有更好的方法来解决这个问题? (Other than wrapping the respective data into dicts and pass these dicts explicitly to do_something() ) (除了将各自的数据包装成字典并将这些字典显式传递给do_something()

  2. Is there a more elegant/shortened way to implement the reach() function?有没有更优雅/更短的方法来实现reach() function?

Cheers干杯

There is no and, in my opinion, should be no elegant way of implementing reach since that introduces a new non-standard indirection which is really hard to comprehend, debug, test and maintain.没有,在我看来,应该没有优雅的方式来实现reach ,因为它引入了一个新的非标准间接,它真的很难理解、调试、测试和维护。 As the Python mantra (try import this ) says:正如 Python 口头禅(尝试import this )所说:

Explicit is better than implicit.显式优于隐式。

So, just pass the arguments.因此,只需通过 arguments。 You-from-the-future will be really grateful to you-from-today.来自未来的你会非常感谢今天的你。

What I ended up doing was我最终做的是

scope = locals()

and make scope accessible from do_something .并使scope可从do_something访问。 That way I don't have to reach, but I can still access the dictionary of local variables of the caller.这样我就不必到达,但我仍然可以访问调用者的局部变量字典。 This is quite similar to building a dictionary myself and passing it on.这与自己构建字典并传递它非常相似。

Is there a better way to solve this problem?有没有更好的方法来解决这个问题? (Other than wrapping the respective data into dicts and pass these dicts explicitly to do_something()) (除了将各自的数据包装成字典并将这些字典显式传递给 do_something())

Passing the dicts explicitly is a better way.明确地传递 dicts 是一种更好的方法。

What you're proposing sounds very unconventional.你的提议听起来很不合常规。 When code increases in size, you have to break down the code into a modular architecture, with clean APIs between modules.当代码大小增加时,您必须将代码分解为模块化架构,并在模块之间使用干净的 API。 It also has to be something that is easy to comprehend, easy to explain, and easy to hand over to another programmer to modify/improve/debug it.它还必须是易于理解、易于解释并且易于移交给其他程序员来修改/改进/调试它的东西。 What you're proposing sounds like it is not a clean API, unconventional, with a non-obvious data flow.您提出的建议听起来不是干净的 API,非常规,具有不明显的数据流。 I suspect it would probably make many programmers grumpy when they saw it.:)我怀疑它可能会让许多程序员看到它时脾气暴躁。:)

Another option would be to make the functions members of a class, with the data being in the class instance.另一种选择是使函数成为 class 的成员,数据位于 class 实例中。 That could work well if your problem can be modelled as several functions operating on the data object.如果您的问题可以建模为在数据 object 上运行的多个函数,那么这可能会很好。

We can get naughtier.我们可以变得更顽皮。

This is an answer to the "Is there a more elegant/shortened way to implement the reach() function?"这是对“是否有更优雅/更短的方式来实现reach() function?”的答案。 half of the question.一半的问题。

  1. We can give better syntax for the user: instead of reach("foo") , outer.foo .我们可以为用户提供更好的语法:而不是reach("foo")outer.foo

    This is nicer to type, and the language itself immediately tells you if you used a name that can't be a valid variable (attribute names and variable names have the same constraints).这更好地键入,并且语言本身会立即告诉您是否使用了不能是有效变量的名称(属性名称和变量名称具有相同的约束)。

  2. We can raise an error, to properly distinguish "this doesn't exist" from "this was set to None ".我们可以提出一个错误,以正确区分“这不存在”和“这被设置为None ”。

    If we actually want to smudge those cases together, we can getattr with the default parameter, or try - except AttributeError .如果我们真的想将这些情况一起涂抹,我们可以使用默认参数getattr ,或者try - except AttributeError

  3. We can optimize: no need to pessimistically build a list big enough for all the frames at once.我们可以优化:不需要悲观地为所有帧一次构建一个足够大的列表。

    In most cases we probably won't need to go all the way to the root of the call stack.在大多数情况下,我们可能不需要 go 一直到调用堆栈的根。

  4. Just because we're inappropriately reaching up stack frames, violating one of the most important rules of programming to not have things far away invisibly effecting behavior, doesn't mean we can't be civilized.仅仅因为我们不恰当地到达堆栈帧,违反了最重要的编程规则之一,即不让远处的事物无形地影响行为,并不意味着我们不能文明。

    If someone is trying to use this Serious API for Real Work on a Python without stack frame inspection support, we should helpfully let them know.如果有人试图在没有堆栈帧检查支持的情况下在 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()

Excellent.出色的。 Now we can just do:现在我们可以这样做:

>>> 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

Perversion elegantly achieved.变态优雅地实现了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 python获取调用函数的当前变量 - python get current variables of the caller function Python:在“调用”class 中获取“调用者”class 实例并访问“调用者”实例变量 - Python: Get a 'caller' class instance in a 'calling' class and access 'caller's' instance variables 如何访问调用者库中的变量? - How to access variables in the caller library? 访问 Python 函数中定义的变量 - Access variables defined in a function in Python 功能之外的Python访问变量 - Python Access Variables Outside of Function 如何检测调用者是否在Python中将任何变量传递给了我的函数? - How can I detect if the caller passed any variables to my function in Python? 创建函数(被调用者,调用者)对的有向图:python`inspect`可以访问当前项目范围之外的模块吗? - Creating Directed graph of function (callee,caller) pairs: can python `inspect` access modules outside current project drectory? 如何从Python中的callee函数访问调用方法的__class__属性? - How to access a caller method's __class__ attribute from a callee function in Python? 在python中访问函数内部的类变量 - access class variables inside a function in python Python:访问嵌套函数中的所有父变量 - Python: Access all Parent Variables in Nested Function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM