Creating functions dynamically in Python -- f(2) cannot resolve (f1) - python

I am trying to dynamically create 2 functions defined within a string. Code:
def main():
fns = '''
def plus_one(x):
return x + 1
def plus_two(x):
return plus_one(x) + 1
'''
exec(fns)
result = eval('plus_two(11)')
print(result)
if __name__ == '__main__':
main()
Saving this code to a file called dyn_code.py and running it gives me the following error:
python dyn_code.py
Traceback (most recent call last):
File "dyn_code.py", line 19, in <module>
main()
File "dyn_code.py", line 14, in main
result = eval('plus_two(11)')
File "<string>", line 1, in <module>
File "<string>", line 7, in plus_two
NameError: name 'plus_one' is not defined
Problem here is that plus_one cannot be resolved inside plus_two.
plus_one on its own is fine here and can be called with the correct result.
Can anybody please give me an idea on how to inject code like this into the local namespace? Specifically, I want to create 2 functions, with one referring to the other.
I have intentionally used the most open form of both exec and eval, I do know how to restrict them, etc. I have also verified that after the call to exec both functions are present in the local namespace.
What makes this more frustrating is that the code works fine in an interpreter session! That is, after injecting these 2 functions into the interpreter namespace via exec, plus_two runs without any issues.
Ideally, I would like to avoid a function-in-function scenario i.e.
def plus_two(x):
def plus_one(x):
return x + 1
return plus_one(x) + 1
This technique actually works but I want 2 explicitly named and standalone functions.

indentations of your function in fns matters! and you have to pass globals() optional argument for mapping!
def main():
fns = '''def plus_one(x):
return x + 1
def plus_two(x):
return plus_one(x) + 1
'''
exec(fns,globals())
result = eval('plus_two(11)')
print(result)
if __name__ == '__main__':
main()
Output:
13
Hope it helps!

You need to add the globals() dictionary in your call to exec(). You can also omit the eval call for plus_two, like so:
def main():
exec('def plus_one(x):\n return x + 1\n\ndef plus_two(x): return plus_one(x) + 1', globals())
print(plus_two(11))
if __name__ == '__main__':
main()

Related

Why local variables go missing in Python exec while running bytecode?

I've built a function called foo to alter a function's code at bytecode level and execute it before returning to regular function execution flow.
import sys
from types import CodeType
def foo():
frame = sys._getframe(1) # get main's frame
main_code: CodeType = do_something(frame.f_code) # modify function code
# copy globals & locals
main_globals: dict = frame.f_globals.copy()
main_locals: dict = frame.f_locals.copy()
# execute altered bytecode before returning to regular code
exec(main_code, main_globals, main_locals)
return
def main():
bar: list = []
# run altered code
foo()
# return to regular code
bar.append(0)
return bar
if __name__ == '__main__':
main()
Though, there is a problem with the evaluation of the local variable during exec:
Traceback (most recent call last):
File "C:\Users\Pedro\main.py", line 31, in <module>
main()
File "C:\Users\Pedro\main.py", line 23, in main
foo()
File "C:\Users\Pedro\main.py", line 15, in foo
exec(main_code, main_globals, main_locals)
File "C:\Users\Pedro\main.py", line 26, in main
bar.append(0)
UnboundLocalError: local variable 'bar' referenced before assignment
If I print main_locals before the call to exec it shows exactly the same contents as if it was done before calling foo. I wonder if it has to do with any of the frame.f_code.co_* arguments passed to the CodeType constructor. They are pretty much the same, except for the actual bytecode frame.f_code.co_code, to which I made a few operations.
I need help to understand why the evaluation of the code under these globals and locals fail to reference main's local variables.
Note: I'm pretty sure that the changes made to main's bytecode prevent the process from going into unwanted recursion.
Edit: As asked in the comments, the basic behaviour of do_something can be resumed to remove all of main's code before call to foo. Some additional steps would involve applying changes to local variables i.e. bar.
import copy
import dis
## dump opcodes into global scope
globals().update(dis.opmap)
NULL = 0
def do_something(f_code) -> CodeType:
bytecode = f_code.co_code
f_consts = copy.deepcopy(f_code.co_consts)
for i in range(0, len(bytecode), 2):
cmd, arg = bytecode[i], bytecode[i+1]
# watch for the first occurence of calling 'foo'
if cmd == LOAD_GLOBAL and f_code.co_names[arg] == 'foo':
break # use 'i' variable later
else:
raise NameError('foo is not defined.')
f_bytelist = list(bytecode)
f_bytelist[i:i+4] = [
NOP, NULL, ## LOAD
LOAD_CONST, len(f_consts) ## CALL
# Constant 'None' will be added to 'f_consts'
]
f_bytelist[-2:] = [NOP, NULL] # 'main' function RETURN
# This piece of code removes all code before
# calling 'foo' (except for JUMP_ABSOLUTE) so
# it can be usend inside while loops.
null_code = [True] * i
j = i + 2
while j < len(f_bytelist):
if j >= i:
cmd, arg = f_bytelist[j], f_bytelist[j+1]
if cmd == JUMP_ABSOLUTE and arg < i and null_code[arg]:
j = arg
else:
j += 2
else:
null_code[j] = False
j += 2
else:
for j in range(0, i, 2):
if null_code[j]:
f_bytelist[j:j+2] = [NOP, NULL] # skip instruction
else:
continue
f_bytecode = bytes(f_bytelist)
f_consts = f_consts + (None,) ## Add constant to return
return CodeType(
f_code.co_argcount,
f_code.co_kwonlyargcount,
f_code.co_posonlyargcount, # Remove this if Python < 3.8
f_code.co_nlocals,
f_code.co_stacksize,
f_code.co_flags,
f_bytecode,
f_consts,
f_code.co_names,
f_code.co_varnames,
f_code.co_filename,
f_code.co_name,
f_code.co_firstlineno,
f_code.co_lnotab,
f_code.co_freevars,
f_code.co_cellvars
)

Can't run multiple functions in a main function in Python

So a problem I'm facing is this:
I defined 2 functions and one function uses the variable of the other function
Now when I run both of them using the following code, it works properly:
def type_anything():
use = input(">")
return use
def print_it():
print(use)
if __name__ == '__main__':
while True:
use = type_anything()
print_it()
Output:
> abcd
abcd
> efgh
efgh
> anything
anything
But when I decide to make a main function that will run both the above functions and then run the main function under the "if __name__ == ......" line, something like this:
def type_anything():
use = input("> ")
return use
def print_it():
print(use)
def run_it():
while True:
use = type_anything()
print_it()
if __name__ == '__main__':
run_it()
The program doesn't run properly instead shows this error:
> anything
Traceback (most recent call last):
File "C:/<location>/sample.py", line 17, in <module>
run_it()
File "C:/<location>/sample.py", line 13, in run_it
print_it()
File "C:/<location>/sample.py", line 7, in print_it
print(use)
NameError: name 'use' is not defined
Why is this happening? What do I need to do?
This should solve your problem:
def type_anything():
use = input("> ")
return use
def print_it(use):
print(use)
def run_it():
while True:
use = type_anything()
print_it(use)
if __name__ == '__main__':
run_it()
The print_it function is not aware of any variable use hence the error.
Noticed how the type_anything function returns the variable use and the print_it function accepts an argument and then prints that.
Please do not get into the habit of using global variables, in the long run these will break everything you write. This is just an example to help you with your understanding of the problem!
Your problem is variable scope. In the first example your variable use is global because you define it in the main program:
if __name__ == '__main__':
while True:
use = type_anything()
print_it()
It is not defined in type_anything, you could rename this variable to whatever and it would still work:
def type_anything():
x = input(">")
return x
In the second example you define it in a function:
def run_it():
while True:
use = type_anything()
print_it()
You could fix this by making use a global variable:
def run_it():
global use
while True:
use = type_anything()
print_it()
A much better way of doing this
Pass the variable use to the function that uses it:
def print_it(use):
print(use)
def run_it():
while True:
use = type_anything()
print_it(use)
You can't use a variable defined in one function in another function.
Each function needs to receive the arguments it uses.
def type_anything():
use = input(">")
return use
def print_it(use):
print(use)
if __name__ == '__main__':
while True:
use = type_anything()
print_it(use)

NameError: name 'run_once' is not defined in python

Python: 3.8.1
I wanted to run a method only once in my class. I have learned the following ways of achieving things and worked well.
def run_once():
# Code for something you only want to execute once
print("test")
run_once.__code__ = (lambda: None).__code__
return "Success"
print(run_once())
print(run_once())
print(run_once())
Output:- --> Expected and Actual
test
Success
None
None
Apparently, getting an error while trying to achieving the same via static method in a class.
class testing():
#staticmethod
def run_once(data):
# Code for something you only want to execute once
print(data)
run_once("").__code__ = (lambda: None).__code__
return "Success"
print(testing.run_once("test"))
Trackback:-
Traceback (most recent call last):
File "foo/test.py", line 11, in <module>
print(testing.run_once("test"))
File "foo/test.py", line 7, in run_once
run_once("").__code__ = (lambda: None).__code__
NameError: name 'run_once' is not defined
test
Could someone highlight where changes required to be made?
When you define run_once as a static method inside the testing class it must be referenced as testing.run_once, since run_once does not exists outside that class.
class testing():
#staticmethod
def run_once(data):
# Code for something you only want to execute once
print(data)
testing.run_once.__code__ = (lambda x: None).__code__
return "Success"
The correct way to write these lines of code is:
class testing():
#staticmethod
def run_once(data):
# Code for something you only want to execute once
print(data)
testing.run_once("").__code__ = (lambda: None).__code__
return "Success"
print(testing.run_once("test"))
Culprit line is run_once("").__code__ = (lambda: None).__code__ that is to be replaced with testing.run_once("").__code__ = (lambda: None).__code__. Howeveer it will also not solve the problem because now you are calling class function within the class that will cause exceeds the maxium limits error. Pleas try to improve your logic in this case. It is more about logical problem then syntax.
Correct logic and syntax will be:
class testing():
#staticmethod
def run_once(data):
# Code for something you only want to execute once
print(data)
testing.run_once.__code__ = (lambda x: None).__code__
return "Success"
print(testing.run_once("test"))

In Python, Is there a C++ equivalent to declaring a function and defining it after it's used?

I'm sorry if this has already been answered. I didn't really know how to search for this particular question.
In C++ you can define a variable at the top to later define. For example:
int printOne();
int main()
{
cout << printOne() << endl;
return 0;
}
int printOne
{
return 1;
}
I'm pretty new to Python though, so I was wondering if this was a possibility for Python as well.
you don't have to. Python evaluates everything at run-time:
def a():
print(b())
def b():
return 12
a()
so when a is called b is already defined.
note: that doesn't work because when a() is called b isn't defined yet:
def a():
print(b())
a()
def b():
return 12
Traceback (most recent call last):
File "<string>", line 420, in run_nodebug
File "<module1>", line 4, in <module>
File "<module1>", line 2, in a
NameError: name 'b' is not defined
There generally isn't a need. The function only needs to be defined by the time you call the function. For example this would work just fine even though the definition of printOne was after main.
def main():
print(printOne())
def printOne():
return 1
main()
I found this useful when I want to define all my parameters at the top, but their computation depends on not yet declared code:
# Parameter section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
param_a = 2
# Like to set param_b here for everyone to see, but it depends on big complex functions:
param_b = lambda: compute_param(param_a)
param_c = 'compute_param(param_a)'
# Function section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def fn(b = param_b, c = param_c):
''' Function using the global parameters defined at the top
'''
if callable(b):
b = b()
c = eval(str(c))
print(b + c)
def compute_param(x):
return 3*x
# MAin code section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if __name__ == '__main__':
fn() #default params
fn(10,10)

Function into a Python class

I am new in Python and I wrote the following code:
class Frazione:
def __init__(self, Numeratore, Denominatore=1):
mcd=MCD(Numeratore,Denominatore)
self.Numeratore=Numeratore/mcd
self.Denominatore=Denominatore/mcd
def MCD(m,n):
if m%n==0:
return n
else:
return MCD(n,m%n)
def __str__(self):
return "%d/%d" %(self.Numeratore, self.Denominatore)
def __mul__(self, AltraFrazione):
if type(AltraFrazione)==type(5):
AltraFrazione=Frazione(AltraFrazione)
return Frazione(self.Numeratore*AltraFrazione.Numeratore, self.Denominatore*AltraFrazione.Denominatore)
__rmul__=__mul__
Open shell at the same folder of Frazione.py:
>>> from Frazione import Frazione
end then
>>> f=Frazione(10,5)
When I press Enter, I receive this output:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".\Frazione.py", line 5, in __init__
mcd=MCD(Numeratore,Denominatore)
NameError: global name 'MCD' is not defined
PS. I apologize for my english!
MCD is a method of Frazione, but you're calling it as if it were a global function. The easiest (and cleanest, IMHO) fix is to just move it outside the class, because it doesn't need to access any class or instance members.
So:
def MCD(m, n):
if m % n == 0:
return n
else:
return MCD(n, m % n)
class Frazione:
# as before but without MCD
If you do want to keep it in the class, then you might rewrite it to be iterative instead of recursive and call it as self.MCD in __init__. That's a good idea anyway, as Python's support for recursion is rather weak.

Categories

Resources