2

I know this pattern is would be very bad, I'm asking the question out of curiosity not because I'm planning on doing this in production code.

Suppose I define a function:

def function(x, y):
    return x + y + z

And the variable z doesn't exist in the module the function was defined.

Is it possible to import the function from another module and somehow manipulate the code object, or play some sort of dirty trick with a decorator, or something, in order to make it work correctly?

I've tried setting co_varnames and co_argcount to (x, y, z) and 3 respectively, but this still doesn't bind the name z to the argument it seems.

To be clear, I know if I define a global named z in the same module this works, I'm asking about importing this function and somehow making z bind to a variable I want, in a different module.

Ignacio
  • 377
  • 3
  • 12
  • youd' also have to modify the bytecode to look up the `z` name from locals, currently, it is doing a `LOAD_GLOBAL` – juanpa.arrivillaga Apr 18 '23 at 21:29
  • "'ve tried setting co_varnames and co_argcount to (x, y, z) and 3 respectively, but this still doesn't bind the name z to the argument it seem" what exactly did you do? You want to call the function with something like `f(1,2,3e)` now, and have `z` assume the third arg? – juanpa.arrivillaga Apr 18 '23 at 21:31
  • 2
    You want something besides: `import module; module.z = 5; module.function(1, 2)`? – Mark Tolonen Apr 18 '23 at 21:32
  • @MarkTolonen yes, that is what they explicitly state – juanpa.arrivillaga Apr 18 '23 at 21:41
  • No, that's not what they explicitly state. They talked about defining a global in the module, but this is ADDING a global to the module after it is imported. I suspect this is exactly what they want. – Tim Roberts Apr 18 '23 at 21:46
  • @TimRoberts I'm referring to "somehow making z bind to a variable I want, in a different module.", so to me that seems to state that they *don't* want to modify `module.z`, they want `z` in the function to refer to something else arbitrarily. Which is why they bring up modifying the code object, but perhaps I'm wrong, the OP should clarify – juanpa.arrivillaga Apr 18 '23 at 21:51
  • @juanpa.arrivillaga Which is why I explicitly asked... – Mark Tolonen Apr 19 '23 at 01:00
  • @MarkTolonen actually, I hadn't thought of that, that would work. The answer that talks about using function.__globals__ is basically the same idea but restricted to the function, I think that is good too. – Ignacio Apr 19 '23 at 02:43

1 Answers1

1

Maybe use the function's code to create a new function using different globals:

# create module for demo
with open('module.py', 'w') as f:
    f.write('''
def function(x, y):
    return x + y + z
''')

from module import function

function = type(function)(function.__code__, globals())

z = 100
print(function(20, 3))

Output (Try it online!):

123

Or if you want a different variable name, maybe indeed use a "decorator" that sets z to the value of your variable before calling the function:

from module import function

f = function
def function(*args):
    f.__globals__['z'] = foobar
    return f(*args)

foobar = 100
print(function(20, 3))

Try it online!

Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65