Late code evaluation and also printing the code - python

I want to pass code to a test() routine, which has to :
print the code
execute it
and finally do stuff with the result.
should handle args in the background
For quick code snippets I can use eval(code-string), like this:
def test_eval(expr_str, expected):
global a,b
res = eval(expr_str) == expected
print(f'{res} : {expr_str}')
but for:
code with assignment
test() should do argumentless calling of fun(), even for fun(a, b...)
or longer code
the approach is unusable.
SOLVED
def test(fun,expected,args):
res = fun(*args) == expected
expr = inspect.getsource(fun)
print(f'{res} : {expr}')
def tests():fun()
def w(a,b):#args
a += b #assignment
return a.sym == "(a + b)"
a = ...
b = ...
test(w,True,(a,b))
better ideas?

Related

Python decorator TypeError 'object is not callable'

I am trying to get myself familiar with decorators.
This is a program I created to do so, but it keeps giving me an TypeError: 'int' object is not callable error, which I don't know how to fix.
#Filename: decorator_practice.py
def add(x):
def add_1():
add_1 = x() + 1
return add_1
def minus(x):
def minus_1():
return x() - 1
return minus_1
def multi(x, times=2):
def multi_2():
return x() * 2
def multi_3():
return x() * 3
def multi_4():
return x() * 4
if times == 2:
return multi_2
elif times == 3:
return multi_3
elif times == 4:
return multi_4
else:
return "Please enter times between 2 and 4"
def create_x():
x = input('Give variable x a value: ')
return x
add(create_x()())
I run this and type: 5
Can anyone help me? Thanks!
Your create_x function returns an integer:
def create_x():
x = input('Give variable x a value: ')
return x
so create_x()() is never going to work.
Part of the problem is that you've used poor parameter names, which is confusing you - you have two xs which refer to two completely different things. Using your add decorator as an example, modify to:
def add(func):
def add_1():
return func() + 1 # you should return from the inner function
return add_1
Now hopefully it is clear that the argument to add should be a function, which is called inside add_1. Therefore you could do:
adder = add(create_x) # don't call create_x yet!
adder() # calling add_1, which calls create_x
which simplifies to:
add(create_x)() # note ordering of parentheses
Note that this could also be written:
#add
def create_x():
...
create_x()
where the syntax #add means create_x = add(create_x).
Once you've mastered simple decorators, note that your multi will not work as you expect - see e.g. python decorators with parameters for creating decorators that take arguments.
You have unnecessary (), change add(create_x()()) to add(create_x()),
and I suggest using x = int(raw_input('Give variable x a value: '))
See the following example:
def add(x):
def add_1():
#add_1 = x() + 1 # remove this line
return x+1
return add_1
def create_x():
x = input('Give variable x a value: ')
return x
b = add(create_x())
print 'answer: ', b()
localhost# python t.py
Give variable x a value: 5
answer: 6

Get inner function result without interaction of outer function in python

I want to get inner function result so i code it like
def main():
def sub():
a = 1
print a
exec main.__code__.co_consts[1]
using above code working successfully but i want to pass the argument to the sub function like...
def main():
def sub(x):
a = x + 1
return a
ans = exec main.__code__.co_consts[1]
print ans
in that problem is i don't know how to pass that x value.
that work must need to exec so that how to pass that x value with exec without interaction of main function
Maybe something like the code below, as suggested by this SO answer
def main():
def sub():
a = x + 1
print a
return a
exec(main.__code__.co_consts[1], {'x': 1} )

Convert a while loop to something reusable

I often find myself using a pattern like this:
num_repeats = 123
interval = 12
for _ in xrange(num_repeats):
result = ...
if result meets condition:
break
time.sleep(interval)
else:
raise Failed despite multiple attempts
Basically, it repeats code until the correct result is returned, or the counter expires.
Although this works, it looks too verbose to me. Is it possible to "parametrize" this loop to a reusable function or context manager, like for example
with repeat(num_repeats, interval):
code
Or maybe there's something in the standard library that would do the trick?
You can use a generator which sleeps before returning repeated results.
The advantage is that your caller is still a genuine for loop, with
all the break, continue, else semantics still in tact.
def trickle_range(num_repeats, interval):
yield 0
for k in xrange(1, num_repeats):
time.sleep(interval)
yield k
for k in trickle_range(num_repeats, interval):
... do stuff, iterate or break as you like ...
You definately won't be able to use the with statement, as python only supplies hooks before and after the code has run, but not one for invoking it, ie. You can't hide a loop within a with statement.
A nice approach is to use a lambda function:
def repeat(repeats, interval, func):
for i in xrange(repeats):
if func(i):
break
time.sleep(interval)
Which you can then use quite easily:
repeat(123, 12, lambda i: condition(i))
Or something similar
One approach would be to decorate the functions you want to repeat:
def repeats_until(num_repeats, interval, condition):
def deco(f):
def func(*args, **kwargs):
for _ in xrange(num_repeats):
result = f(*args, **kwargs)
if condition(result):
return result
time.sleep(interval)
return func
return deco
And then use it like:
#repeats_until(3, 5, lambda s: s == "hello")
def take_input():
return raw_input("Say hello: ")
Example (although I can't show the wait!)
>>> take_input()
Say hello: foo
Say hello: bar
Say hello: baz
>>> take_input()
Say hello: hello
'hello'
Alternatively, to keep the condition with the called function, something like:
def repeats(num_repeats, interval):
def deco(f):
def func(*args, **kwargs):
for _ in xrange(num_repeats):
result = f(*args, **kwargs)
if result is not None: # or e.g. False if None is valid return
return result
time.sleep(interval)
return func
return deco
#repeats(3, 5)
def take_input(condition):
s = raw_input("Say hello: ")
if condition(s):
return s
ui = take_input(lambda s: s == "hello")
This relies on the decorated function returning a value (in this case the implicit None) that tells the decorator it isn't finished yet.

Importing modules and taking an output

I have a previously created a module of the form:
def function1():
....
return ....
def function2():
....
return ....
def function3():
....
return ....
if __name__ == "__main__":
do something
Now in another file I am importing this using:
from file1 import function3
def function4(a, b):
for n in range(b):
t = function3()[1] # getting the second output from the third function
if ...:
....
else:
....
return ....
print(function4(a,b)) # with some input a and b
Now when I run the function in the second file from the command prompt why does this produce the flashing underscore as if there is an infinite loop?
If b = 1, it produces one output as expected, but why does it not work for b > 1?
(Note: function3()[1] will produce a different output each time.)
There is a lack of detail in the functions as this is related to a coursework assignment but having difficulties importing.

make a parent function return - super return?

there is a check I need to perform after each subsequent step in a function, so I wanted to define that step as a function within a function.
>>> def gs(a,b):
... def ry():
... if a==b:
... return a
...
... ry()
...
... a += 1
... ry()
...
... b*=2
... ry()
...
>>> gs(1,2) # should return 2
>>> gs(1,1) # should return 1
>>> gs(5,3) # should return 6
>>> gs(2,3) # should return 3
so how do I get gs to return 'a' from within ry? I thought of using super but think that's only for classes.
Thanks
There's been a little confusion... I only want to return a if a==b. if a!=b, then I don't want gs to return anything yet.
edit: I now think decorators might be the best solution.
Do you mean?
def gs(a,b):
def ry():
if a==b:
return a
return ry()
As you mention "steps" in a function, it almost seems like you want a generator:
def gs(a,b):
def ry():
if a==b:
yield a
# If a != b, ry does not "generate" any output
for i in ry():
yield i
# Continue doing stuff...
yield 'some other value'
# Do more stuff.
yield 'yet another value'
(Generators can now also act as coroutines, since Python 2.5, using the new yield syntax.)
This should allow you to keep checking the state and return from the outer function if a and b ever end up the same:
def gs(a,b):
class SameEvent(Exception):
pass
def ry():
if a==b:
raise SameEvent(a)
try:
# Do stuff here, and call ry whenever you want to return if they are the same.
ry()
# It will now return 3.
a = b = 3
ry()
except SameEvent as e:
return e.args[0]
There's been a little confusion... I
only want to return a if a==b. if
a!=b, then I don't want gs to return
anything yet.
Check for that then:
def gs(a,b):
def ry():
if a==b:
return a
ret = ry()
if ret: return ret
# do other stuff
you return ry() explicitly instead of just calling it.
I had a similar problem, but solved it by simply changing the order of the call.
def ry ()
if a==b
gs()
in some languages like javascript you can even pass a function as a variable in a function:
function gs(a, b, callback) {
if (a==b) callback();
}
gs(a, b, ry);
I came here looking for an answer to the same type of problem. Instead I worked out a solution that (at least for me) makes the intent a bit more clear: define your steps as lambdas or defs, and place them in an array. Then you can simply loop and handle them as needed.
I adapted my solution to the OP's question below:
def gs(a,b):
def step1():
nonlocal a
a += 1
def step2():
nonlocal b
b *= 2
if a == b:
return a
steps = [step1, step2]
for step in steps:
step()
if a == b:
return a
# anything you else you want to do

Categories