0

is it possible to call a function using a variable as the name for a variable? Example:

def my_method(foo="bar"):
    print(foo)
var = "foo='baz'"
my_method(var)

>>> baz

Now, I can't figure out a way to do this kind of thing (substitute the value in a variable for a variable name).

Is this kind of thing possible?

I know there are things you can do that are analogous to this, for instance:

def my_method(foo, bar, baz):
    print(foo, bar, baz)
var = ['one','two','three']
my_method(*var)

>>> one two three

But I can't find a uniform, generalized solution to any metaprogramming I might need in python. Is there one? Perhaps the language just isn't capable of a generalized metaprogramming solution.

1
  • 1
    I would normalize the string into json, and then use the json module to read it in as python structures. That way you'd have all sorts of lists or dicts to unpack out into arguments Commented Mar 14, 2018 at 21:51

3 Answers 3

1

You can provide exec with a dictionary inside which it will store variables and then unwrap it as your function's keyword arguments.

def my_method(foo="bar"):
    print(foo)

var_a = "foo='baz'"
kwargs = {}

# See safety note at the bottom of this answer.
exec(var_a, {'__builtins__': {}}, kwargs)

my_method(**kwargs )
# prints: 'baz'

You could even use a decorator to give that behaviour to functions.

def kwargs_as_string(f):

    def wrapper(string, **more_kwargs):
        kwargs = {}

        # See safety note at the bottom of this answer.
        exec(string, {'__builtins__': {}}, kwargs)
        return f(**kwargs, **more_kwargs)

    return wrapper

@kwargs_as_string
def my_method(foo="bar"):
    print(foo)

my_method("foo='baz'")
# prints: 'baz'

Safety note

To be safe, we provide exec with an empty global __builtins__, otherwise a reference to the dictionary of the built-in module is inserted under that key. This can lead to trouble.

var_a = '__import__("sys").stdout.write("You are in trouble")'
exec(var_a, {}, {})
# prints: You are in trouble

exec(var_a, {'__builtins__': {}}, {})
# raises a NameError: name '__import__' is not defined
Sign up to request clarification or add additional context in comments.

2 Comments

amazing answer, thank you! is there anything analogous to this for other situations such as if statements, or any other general case, etc? I can't do something like if **'a==1': can I? that kind of thing is just impossible right?
Not really, this is possible thanks to kwargs unwraping which is specific to functions. What you seem to need might be a script template that you can fill in.
1

Assuming you're allowed to have JSON formatted strings...

import json

args = json.loads(
   """
   {
      "kwargs": {
         "a": 2,
         "b": 1
      },
      "args": [3, 4] 
   }
   """)

def foo(a, b):
   print("a: {}".format(a))
   print("b: {}".format(b))

foo(**args["kwargs"])
foo(*args["args"])

# output:
# a: 2
# b: 1
# a: 3
# b: 4

Comments

0

can you use getattr to call a function within your scope?

I found these three options, the last one to be the most useful in my case.

def foo():
    def bar(baz):
        print('dynamically called bar method using', baz)

    packages = {'bar': bar}
    getattr(packages['bar'], "__call__")('getattr with map')

    packages['bar']('just the map')

    locals()['bar']('just locals()')

foo()

python test_dynamic_calling.py

dynamically called bar method using getattr with map
dynamically called bar method using just the map
dynamically called bar method using just locals()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.