Python - modifying variable passed to function

575 views Asked by At

If I have a function f(x) and variable var of any type and scope is there any way to modify var inside call to f(var)?

Meaning that function f does some magic stuff to get reference to original (passed) var (like reference/pointer in C++) and modifies that original var, for any type of var (even int/float/str/bytes).

Wrapping var into dict or list or any other class is not allowed. Because it is known that dict/list are passed by reference.

Returning new value from a function to re-assign variable is not allowed too.

Modifying scope of original variable (like making it global) is not allowed too.

In fact any change to function's caller's code is not allowed.

This variable can be of any imaginary type and scope, no assumptions should be done about them.

So that next code should work (such code can be placed both inside another wrapping function or globally, should not matter):

def test():
    def magic_inplace_add(x, val):
        # ...
    var = 111
    magic_inplace_add(var, 222) # modifies int var by adding 222
    print(var) # prints 333

test()

If needed to solve the task this function can do any complex manipulations like using inspect module.

Basically, I need somehow to break a convention about simple types being passed by value, even if to achieve this in some non-simple/cryptic way.

I'm pretty sure this task can be solved by tools from standard reverse-engineering modules like inspect / ast / dis.

Just to clarify my need for this task - right now for this task I'm not trying to code nicely using tidy Python style and methods, you can imagine that this task in future is a kind of interview question for companies specializing in reverse-engineering / antiviruses / code security / compilers implementation. Also tasks like this are interesting in exploring all hidden possibilities of languages like Python.

2

There are 2 answers

1
Shoaib Mirzaei On

as @rcvaram suggested use global variables, like this

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)
5
DarrylG On

For module scope variables.

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
     

Example

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