Python-修改传递给函数的变量

问题描述

如果我有函数f(x)和变量var,且类型和作用域不限,是否可以修改var调用中的f(var)

函数f做了一些神奇的工作,以引用原始(通过的)var(例如C++中的引用/指针)并修改了原始var,适用于任何类型的var(甚至int / float / str / bytes)。

不允许将var包装为dictlist或任何其他类。因为众所周知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)