像内联一样调用python函数

2 投票
4 回答
1605 浏览
提问于 2025-04-16 00:24

我想在一个不同的模块里写一个函数,当这个函数被调用时,它能够访问调用它的地方所有的变量,就像它的代码直接粘贴到调用的地方一样,而不是有自己的上下文。简单来说,就像C语言里的宏,而不是普通的函数。我知道我可以把locals()传给这个函数,这样它就能把局部变量当作字典来访问,但我想要的是能够正常访问这些变量(比如用x.y,而不是x["y"]),而且我希望能够访问调用者能接触到的所有名字,而不仅仅是局部变量,还包括那些在调用者文件中“导入”的东西,但在包含这个函数的模块中没有导入。

这样做可能吗?

编辑 2 这是我能想到的最简单的例子,说明我真正想做的事情:

def getObj(expression)
  ofs = expression.rfind(".")
  obj = eval(expression[:ofs])  
  print "The part of the expression Left of the period is of type ", type(obj), 

问题是,'expression'需要调用者的导入和局部变量才能正确执行而不出错。实际上,涉及的内容远不止一个eval,所以我想避免仅仅把locals()传递给eval(),因为那样无法解决我一般情况下的问题。

4 个回答

2

这样做可能吗?

可以(有点复杂,绕了个大圈),但我一般不推荐这样做(稍后会详细说明)。

考虑一下:

myfile.py

def func_in_caller():
    print "in caller"

import otherfile
globals()["imported_func"] = otherfile.remote_func

imported_func(123, globals())  

otherfile.py

def remote_func(x1, extra):
    for k,v in extra.iteritems(): 
        globals()[k] = v
    print x1
    func_in_caller()

这样做会得到(如预期的那样):

123
in caller

我们在这里做的是一些小把戏:我们只是把每个项目复制到另一个命名空间中,以便让它工作。这种做法很容易出问题,可能会导致难以发现的错误。

几乎肯定有更好的方法来解决你的问题或组织你的代码(我们需要更多信息来了解你想要实现的目标)。

4

还有一种更糟糕的方法来实现这个功能——请不要这样做,尽管它是可能的——

import sys

def insp():
    l = sys._getframe(1).f_locals
    expression = l["expression"]
    ofs = expression.rfind(".")
    expofs = expression[:ofs]
    obj = eval(expofs, globals(), l)
    print "The part of the expression %r Left of the period (%r) is of type %r" % (expression, expofs, type(obj)), 

def foo():
    derp = 5
    expression = "derp.durr"
    insp()

foo()

输出结果是

表达式 'derp.durr' 中,点左边的部分 ('derp') 是类型 (类型为 'int')

3

我知道这可能不是你想听的答案,但从调用模块的范围去访问局部变量其实不是个好主意。如果你平时用PHP或C语言编程,可能会习惯这种做法。

如果你还是想这么做,可以考虑创建一个类,然后把这个类的实例传递进去,代替locals()

#other_module.py
def some_func(lcls):
    print(lcls.x)

然后,

>>> import other_module
>>> 
>>> 
>>> x = 'Hello World'
>>> 
>>> class MyLocals(object):
...     def __init__(self, lcls):
...             self.lcls = lcls
...     def __getattr__(self, name):
...             return self.lcls[name]
... 
>>> # Call your function with an instance of this instead.
>>> other_module.some_func(MyLocals(locals()))
'Hello World'

试试看吧。

撰写回答