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.
-
1I 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 argumentssolstice333– solstice3332018年03月14日 21:51:22 +00:00Commented Mar 14, 2018 at 21:51
3 Answers 3
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
2 Comments
if **'a==1': can I? that kind of thing is just impossible right?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
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()