I'm coding a simple test program in python as part of my greater program, but i would l like to pass a sub function name into the main function, so the main function can run the subfunction.
eg:
import datetime;
def cronjob(repeat, interval, task):
if (str(repeat) == 'inf'):
repeat = 99999999999999999999;
position = 0;
interval = datetime.timedelta(seconds=interval);
x = datetime.datetime.now()
while not (position >= repeat):
while (datetime.datetime.now() - x < interval):
pass;
x = datetime.datetime.now();
position += 1;
exec task;
def test():
print "hello";
cronjob(10, 0.1, 'test');
EDIT: Already fixed this, but since nothing is listed here, let me show you how to do it in case someone else needs it.
I fiddled with eval() and exec, and tried just eval(task). that didn't throw an error, so I tried print eval(task) and sure enough, it listed the function's memory address [that is, test()]. finally, I have used eval(task); to then call that function. below is the code fixing this:
import datetime;
def cronjob(repeat, interval, task):
if (str(repeat) == 'inf'):
repeat = 99999999999999999999;
position = 0;
interval = datetime.timedelta(seconds=interval);
x = datetime.datetime.now()
while not (position >= repeat):
while (datetime.datetime.now() - x < interval):
pass;
x = datetime.datetime.now();
position += 1;
eval(task);
def test():
print "hello";
cronjob(10, 0.1, 'test()');
Why not pass the function object itself to the scheduler ?
test is an object and can be used as an argument too!
def test():
print "test"
def exe(func):
func()
exe(test)
I believe since functions are objects, you can just pass one in to the "controlling" function by name, so you don't need the exec call (which is usually used for dynamic code execution).
e.g.
def foo(a_number, a_function):
print a_number
a_number += 1
a_function(a_number)
def bar(another_number):
print another_number
foo(5, bar)
should produce the output:
5
6
In case if you're absolutely sure you want to derive a function from a string, you may want to use a dict as a mapping from strings to functions like follows:
dispatcher = {'dothis': module1.do_this,
'dothat': module2.to_that}
def foo(fun):
fun(arg1, arg2)
def bar(action):
fun = dispatcher.get(action)
if fun:
foo(fun)
This will be much more secure (as action is likely to come from the outside) and provide better decoupling of internal code structure from the external API.
Related
I'm trying to pass information back and forth between 2 scripts. In one, we obtain a user input, in the other some modification is done to the user input, then back to the first one, we print out that modification.
#del2
def fun():
return int(user_input)+1
#script to run
user_input=input('some number')
from del2 import fun
print(fun())
So when we run our script, the user gives some input, the next line then runs the other script, which adds a value of 1 to the user inputted value, and then we print out that modified value. However, it appears you can't define a variable in one script, and have that defined variable transfer over to another script. Thus, I get this error when I try the above: NameError: name 'user_input' is not defined. I've tried to look at other posts regarding this, but they use tkinter and all are a bit too complicated/over my head to understand. So I made a very basic simple example to try and understand how this all works.
Edit:
I don't want to make another post, since its regarding the same issue. If I have to define every input used for every function, then it becomes quite crowded if you have multiple inputs. I.E.
#del2
def fun(user_input):
return int(user_input)+1
def fun2(user_input2):
return int(user_input2)+1
def fun3(user_input3):
return int(user_input3)+1
def fun4(user_input4):
return int(user_input4)+1
def fun5(user_input,user_input2,user_input3,user_input4):
return fun(user_input)+fun2(user_input2)+fun3(user_input3)+fun4(user_input4)
#script to run
user_input=input('some number')
user_input2=input('some number')
user_input3=input('some number')
user_input4=input('some number')
from del2 import fun5
print(fun5(user_input,user_input2,user_input3,user_input4))
Is there a better way to do this, so fun5 doesn't become extremely long if you have multiple inputs.
You need to define fun so it takes the variable as a parameter: def fun(user_input) then pass that variable to the imported function.
Also if you want user_inputs value to change after you call your fun() function you need to something like this:
#del2
def fun(user_input):
return int(user_input) + 1
#script to run
user_input = input('some number')
from del2 import fun
user_input = fun(user_input)
print(user_input)
Edit:
The fun() function isnt for just user_input. So you can use the same fun() function for another variables.
#del2
def fun(any_input): # i changed the variables name just to be clear
return int(any_input) + 1
#script to run
user_input = input('some number')
user_input2 = input('some number')
from del2 import fun
user_input = fun(user_input)
user_input2 = fun(user_input2)
print(user_input + ", " + user_input2)
and you can add the input variables to an array and do something like
#del2
def fun(any_input):
return int(any_input) + 2
def fun1(any_input):
return int(any_input) * 2
def fun2(any_input):
return int(any_input) // 2
def fun3(any_input):
return int(any_input) - 2
def fun5(input_array):
functions = [fun, fun1, fun2, fun3]
final = 0
if len(input_array) != 4:
raise Exception("Not enough functions for inputs")
for i in range(len(input_array)):
final += functions[i](input_array[i])
return final
#script to run
user_inputs = []
user_inputs.append(input('some number 0: ')) #you can use a for loop here too
user_inputs.append(input('some number 1: '))
user_inputs.append(input('some number 2: '))
user_inputs.append(input('some number 3: '))
from del2 import fun5
user_inputs = fun5(user_inputs)
print(user_inputs)
You can do this using the global keyword and a global variable within the imported module that is accessed by the different functions. Taking a simpler example that just adds or subtracts from a globally stored total:
# tally.py
total = 0
def add(n):
global total
total += n
def subtract(n):
global total
total -= n
# test_tally.py
import tally
tally.add(5)
tally.subtract(1)
print(tally.total)
However, global variables are bad. We do not generally think in terms of passing data back and forth between modules. The imported module is executed in its entirety at the time of importing, so data can only be passed to functions or other objects within the imported module.
Instead, modules often include classes, which can be used to generate objects that can store state. Data can be passed to these objects and stored within them. The objects can then operate upon the data and return different results by calling different methods of the object. This would be written like this:
# tally.py
class Tally(object):
def __init__(self):
self.total = 0
def add(self, n):
self.total += n
def subtract(self, n):
self.total -= n
# test_tally.py
from tally import Tally
tally = Tally()
tally.add(3)
tally.subtract(4)
print(tally.total)
So I'm making an integrator program with the simpson 1/3 method and I want the user to give me a function and interval of integration, and then return the result. I figure that I can use exec to make dynamic code, so I use it to create the function. This is my code:
from math import *
class CreaFormula:
def __init__(self,formula):
self.fun = "def f(x):\n return %s" % formula
class Integrador:
def __init__(self,f):
#integration interval
a = 0
b = 1
n = 600
h = (b-a)/n
#method
self.s = f(a) + f(b)
for i in range(1,n):
if i%2 == 0:
self.s = self.s + 2*f(a+i*h)
else:
self.s = self.s + 4*f(a+i*h)
self.s = self.s*(h/3)
vr = -cos(1) + e
self.er = abs((vr -self.s) /vr)
formula = input()
MiFo = CreaFormula(formula)
f1 = MiFo.fun
exec(f1)
MyInt = Integrador(f)
a = MyInt.s
b = MyInt.er
print(a)
So basically I want to put everything that is at the end of the code inside a class, so I can call it later by another code, but if I do that it shows an error that f is not defined, because it is created with exec. So is there a way to not use exec but still create a function from user's input?
Thanks in advance
If the question is just how to construct a function object that can evaluate a user-supplied expression, you'll probably have an easier time using eval than exec:
def create_function_from_formula(formula):
def user_function(x):
return eval(formula, globals(), {'x': x})
return user_function
Of course, even with eval, if someone provides a malicious formula, it can do anything, up to and including executing any other program included on the computer. So only do this if you trust the person providing the formula to essentially take over your computer. In particular, you should never do this if formula can come from a user who is not physically logged in to your computer already.
I am reading Hackers and Painters and am confused by a problem mentioned by the author to illustrate the power of different programming languages.
The problem is:
We want to write a function that generates accumulators—a function that takes a number n, and returns a function that takes another number i and returns n incremented by i. (That’s incremented by, not plus. An accumulator has to accumulate.)
The author mentions several solutions with different programming languages. For example, Common Lisp:
(defun foo (n)
(lambda (i) (incf n i)))
and JavaScript:
function foo(n) { return function (i) { return n += i } }
However, when it comes to Python, the following codes do not work:
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
f = foo(0)
f(1) # UnboundLocalError: local variable 's' referenced before assignment
A simple modification will make it work:
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
I am new to Python. Why doesn the first solution not work while the second one does? The author mentions lexical variables but I still don't get it.
s += i is just sugar for s = s + i.*
This means you assign a new value to the variable s (instead of mutating it in place). When you assign to a variable, Python assumes it is local to the function. However, before assigning it needs to evaluate s + i, but s is local and still unassigned -> Error.
In the second case s[0] += i you never assign to s directly, but only ever access an item from s. So Python can clearly see that it is not a local variable and goes looking for it in the outer scope.
Finally, a nicer alternative (in Python 3) is to explicitly tell it that s is not a local variable:
def foo(n):
s = n
def bar(i):
nonlocal s
s += i
return s
return bar
(There is actually no need for s - you could simply use n instead inside bar.)
*The situation is slightly more complex, but the important issue is that computation and assignment are performed in two separate steps.
An infinite generator is one implementation. You can call __next__ on a generator instance to extract successive results iteratively.
def incrementer(n, i):
while True:
n += i
yield n
g = incrementer(2, 5)
print(g.__next__()) # 7
print(g.__next__()) # 12
print(g.__next__()) # 17
If you need a flexible incrementer, one possibility is an object-oriented approach:
class Inc(object):
def __init__(self, n=0):
self.n = n
def incrementer(self, i):
self.n += i
return self.n
g = Inc(2)
g.incrementer(5) # 7
g.incrementer(3) # 10
g.incrementer(7) # 17
In Python if we use a variable and pass it to a function then it will be Call by Value whatever changes you make to the variable it will not be reflected to the original variable.
But when you use a list instead of a variable then the changes that you make to the list in the functions are reflected in the original List outside the function so this is called call by reference.
And this is the reason for the second option does work and the first option doesn't.
kk = ['kj','jk',5200,49]
def test_func1(kj):
for i in kj:
value= i
return value
def mainn_func(kk):
v = test_func1(kk)
print(v)
In the above function my print(v) statement does not print any thing , what are the possible changes that I can make , is there any problem with the return statement ?
You are not calling any functions.
At the bottom of your script, you will need to do mainn_func(kk)
If u call the function mainn_func(kk) you will get only the first value of the list, because you return the value in first looping itself.
If u want to print each value of the list. Try this:
kk = ['kj','jk',5200,49]
def test_func1(kj):
for i in kj:
value= i
print value
test_func1(kk)
This code is very simple and returning only first value.
kk = ['kj','jk',5200,49]
def test_func1(kj):
for i in kj:
value= i
return value
def mainn_func(kk):
v = test_func1(kk)
print(v)
The problem looks like you are not calling this function call this like:-
mainn_func(kk)
If you want to print only First value then use, don't write so much code, just write:-
print kk[1]
If you want to print all values then write:-
def test_func1(kj):
for i in kj:
print i
def mainn_func(kk):
test_func1(kk)
mainn_func(kk)
It will resolve your problem.
This is why, in some ways, Python is called a scripting language. It simply runs through what is in the program as if it were typed into the command line interpreter.
This is hard to get used to if you come from languages like C. In those languages, a function main(argc, argv) is the entry point of code. You also need to forward-declare functions. So, in C, you see patterns like:
float square(float); /* C forward declaration so the compiler is happy*/
main(argc, argv) {
int argc; char**argv;
float z = 2;
q = square(w);
printf("%d", q);
}
float square (a) {
float a;
return a*a;
}
Python needs no main(). If you just give it a 'script' it just runs through it and does what you say.
So, a Python script that says:
def square(a) :
return a*a
def main() :
z = 2
q = square(z)
print q
does absolutely nothing other than define functions. Either of these, however, would work:
Option 1: just say what you want:
def square(a) :
return a*a
z = 2
q = square(z)
print q
In that case, it defines 'square', then (after that) sets z = 2; calls square; and then prints the result.
Option 2 is to make the single command, if you will, in the script, 'call main to get things going':
def square(a) :
return a*a
def main() :
z = 2
q = square(z)
print q
main()
That approach (1) defines two functions, and (2) calls one of them (that being main) that then gets the ball rolling.
A Python program need not have a main. But, check out the use of this idiom:
if (__name__ == '__main__').....
which is useful for putting test code in each file.
I am creating a program which fits various curves to data. I am creating a number of functions which define a fit by doing the following:
for i in range(len(Funcs2)):
func = "+".join(Funcs2[i])
func = func.format("[0:3]","[3:6]")
exec('def Trial1{0}(x,coeffs): return {1}'.format(i, func))
exec('def Trial1{0}_res(coeffs, x, y): return y - Trial1{0}
(x,coeffs)'.format(i))
How do I then call each function of these created functions in turn. At the moment i am doing the following:
for i in range(len(Funcs2)):
exec('Trial1{0}_coeffs,Trial1{0}_cov,Trial1{0}_infodict,Trial1{0}_
mesg,Trial1{0}_flag =
scipy.optimize.leastsq(Trial1{0}_res,x02, args=(x, y),
full_output = True)'.format(i))
In this loop, each created function is called in each iteration of the loop.The problem is that i have to keep using exec() to do want I want to do. This is probably bad practice and there must be another way to do it.
Also, i cannot use libraries other than numpy,scipy and matplotlib
Sorry for the bad formatting. The box can only take lines of code that are so long.
Functions are first-class objects in python! You can put them in containers like lists or tuples, iterate through them, and then call them. exec() or eval() are not required.
To work with functions as objects instead of calling them, omit the parentheses.
EG:
def plus_two(x):
return x+2
def squared(x):
return x**2
def negative(x):
return -x
functions = (plus_two, squared, negative)
for i in range(1, 5):
for func in functions:
result = func(i)
print('%s(%s) = %s' % (func.__name__, i, result))
--> OUTPUT
plus_two(1) = 3
squared(1) = 1
negative(1) = -1
plus_two(2) = 4
squared(2) = 4
negative(2) = -2
plus_two(3) = 5
squared(3) = 9
negative(3) = -3
plus_two(4) = 6
squared(4) = 16
negative(4) = -4