问题描述
如果我有函数f(x)
和变量var
,且类型和作用域不限,是否可以修改对var
的调用中的f(var)
?
该函数f
做了一些神奇的工作,以引用原始(通过的)var
(例如C++
中的引用/指针)并修改了原始var
,适用于任何类型的var
(甚至int
/ float
/ str
/ bytes
)。
不允许将var
包装为dict
或list
或任何其他类。因为众所周知dict / list是通过引用传递的。
也不允许从函数返回新值以重新分配变量。
此变量可以是任何假想的类型和范围,不应对它们进行任何假设。
以便下一个代码可以工作(这样的代码可以放在另一个包装函数内,也可以全局放置):
def magic_inplace_add(x,val):
# ...
var = 111
magic_inplace_add(var,222) # modifies int var by adding 222
print(var) # prints 333
如果需要解决该任务,则此功能可以执行任何复杂的操作,例如使用inspect
模块。
基本上,我需要某种方式打破关于按值传递简单类型的约定,即使以某种非简单/神秘的方式实现这一点也是如此。
我很确定可以通过标准逆向工程模块(如inspect / ast / dis)中的工具来解决此任务。
只是为了阐明我对这项任务的需求-现在,对于该任务,我并不是想使用整洁的Python样式和方法来很好地编写代码,您可以想象,将来该任务对于专业化公司来说是一种面试问题逆向工程/防病毒/代码安全/编译器实现。同样的任务对于探索诸如Python之类的语言的所有隐藏可能性也很有趣。
解决方法
用于模块作用域变量。
import inspect
import re
def magic_inplace_add(var,val):
'''
Illustation of updating reference to var when function is called at module level (i.e. not nested)
'''
# Use inspect to get funcstion call syntax
previous_frame = inspect.currentframe().f_back
(filename,line_number,function_name,lines,index) = inspect.getframeinfo(previous_frame)
# lines contains call syntax
# i.e. ['magic_inplace_add(x,222)\n']
args = re.findall("\w+",lines[0]) # get arguments of calling syntax i.e. ('magic_inplace_add','x',222)
# Update variable in context of previous frame
# i.e. args[1] == 'x' in example
if args[1] in previous_frame.f_globals:
# update variable from global
previous_frame.f_globals.update({args[1]:var + val})
elif args[1] in previous_frame.f_locals:
# for nested function variable would be in f_locals
# but this doesn't work in Python 3 this f_locals is a copy of actual locals in calling frame
# see Martijn Pieters answer at
# https://stackoverflow.com/questions/36678241/how-can-i-force-update-the-python-locals-dictionary-of-a-different-stack-frame
previous_frame.f_locals.update({args[1]:var + val}) # this is what would be used for nested functions
示例
x = 111 # x module scope
y = 222 # y module scope
z = 333 # z module scope
magic_inplace_add(x,222)
magic_inplace_add(y,222)
magic_inplace_add(z,222)
print(x) # Output: 333
print(y) # Output: 444
print(z) # Output: 555
,
@rcvaram建议使用全局变量,例如
def magic_inplace_add(x,val):
global var
var = 333
global var
var = 111
magic_inplace_add(var,222) # modifies int var by adding 222
print(var)