I get the below error when I try to run this code. I'm trying to initialize a list with function pointers. It appears the eval doesn't see the functions. I'm guessing there's some kind of scoping going on that I don't understand. It works when I create the table by hand as you can see. I'm trying to avoid having to maintain a list of functions. This also worked when I made the functions global.
Traceback (most recent call last):
File "/home/westgate/code/python/try/x.py", line 27, in <module>
main()
File "/home/westgate/code/python/try/x.py", line 23, in main
fxns_eval = [eval(fxn_name) for fxn_name in dir() if fxn_name.startswith('fxn_')]
File "/home/westgate/code/python/try/x.py", line 23, in <listcomp>
fxns_eval = [eval(fxn_name) for fxn_name in dir() if fxn_name.startswith('fxn_')]
File "<string>", line 1, in <module>
NameError: name 'fxn_bar' is not defined
import inspect
def main():
def fxn_foo():
print('in foo '+inspect.currentframe().f_code.co_name)
def fxn_bar():
print('in bar '+inspect.currentframe().f_code.co_name)
for i in dir():
if i.startswith('fxn_'):
print(i)
fxn_bar()
fxns = [ fxn_foo, fxn_bar ]
fxns[1]()
fxns_eval = [eval(fxn_name) for fxn_name in dir() if fxn_name.startswith('fxn_')]
fxns_eval[1]()
main()
The problem is you are not passing locals
explicitly, so . From the docs:
If both dictionaries are omitted, the expression is executed in the environment where eval() is called.
However, the environment is the list comprehension, which creates it's own scope, and neither of those functions are in the list-comprehension's local namespace, nor in the global namespace. You want main
's local namespace So note, this works:
>>> def main():
... def foo(): 'foo'
... print(eval('foo'))
...
>>> main()
<function main.<locals>.foo at 0x10c3ba6a8>
Pass globals
and locals()
explicitly! But you have to use them as free variables to the list-comprehension's scope, since it is essentially like another nested function scope, you can't just do this either:
>>> def main():
... def foo(): 'foo'
... def bar(): 'bar'
... print([eval(x, globals(), locals()) for x in dir()])
...
>>> main()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in main
File "<stdin>", line 4, in <listcomp>
File "<string>", line 1, in <module>
NameError: name 'bar' is not defined
Indeed, that is equivalent to not passing the arguments at all.
You could do:
>>> def main():
... def foo(): 'foo'
... def bar(): 'bar'
... locs, globs = locals(), globals()
... print([eval(x, globs, locs) for x in dir()])
...
>>> main()
[<function main.<locals>.bar at 0x10bded1e0>, <function main.<locals>.foo at 0x10c3ba6a8>, {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'n1': 1014.308, 'n0': 961.06, 'p1': 18.59, 'p0': 19.65, 'q1': 1014.308, 'q0': 961.06, 'main': <function main at 0x10c3ba620>}, {'foo': <function main.<locals>.foo at 0x10c3ba6a8>, 'bar': <function main.<locals>.bar at 0x10bded1e0>, 'globs': {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'n1': 1014.308, 'n0': 961.06, 'p1': 18.59, 'p0': 19.65, 'q1': 1014.308, 'q0': 961.06, 'main': <function main at 0x10c3ba620>}, 'locs': {...}}]
>>>