Facing issues in writing def inside if in python - python
This I need to do
def x1(ap,dp,ph,e1,e2,e3,e4,e5,y):
if Delta(ap,dp,ph,e1,e2,e3,e4,e5,y) >= 0:
return 2*(b(ap,dp,ph,e1,e2,e3,e4,e5,y)**3) - 9*a*b(ap,dp,ph,e1,e2,e3,e4,e5,y)*c(ap,dp,ph,e1,e2,e3,e4,e5,y) + 27*(a**2)*d(ap,dp,ph,e1,e2,e3,e4,e5,y)
else:
return None
def y1(ap,dp,ph,e1,e2,e3,e4,e5,y):
if Delta(ap,dp,ph,e1,e2,e3,e4,e5,y) >= 0:
return 3*np.sqrt(3)*a*np.sqrt(Delta(ap,dp,ph,e1,e2,e3,e4,e5,y))
else:
return None
This is creating a problem, output:
return np.sqrt(x1(ap,dp,ph,e1,e2,e3,e4,e5,y)**2+y1(ap,dp,ph,e1,e2,e3,e4,e5,y)**2)
TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'
so I want to write it like
def Delta(ap,dp,ph,e1,e2,e3,e4,e5,y):
Delta = 18*a*b(ap,dp,ph,e1,e2,e3,e4,e5,y)*c(ap,dp,ph,e1,e2,e3,e4,e5,y)*d(ap,dp,ph,e1,e2,e3,e4,e5,y) - 4*(b(ap,dp,ph,e1,e2,e3,e4,e5,y)**3)*d(ap,dp,ph,e1,e2,e3,e4,e5,y) + (b(ap,dp,ph,e1,e2,e3,e4,e5,y)**2)*(c(ap,dp,ph,e1,e2,e3,e4,e5,y)**2) - 4*a*(c(ap,dp,ph,e1,e2,e3,e4,e5,y)**3) - 27*(a**2)*(d(ap,dp,ph,e1,e2,e3,e4,e5,y)**2)
if Delta(ap,dp,ph,e1,e2,e3,e4,e5,y) >= 0:
def x1(ap,dp,ph,e1,e2,e3,e4,e5,y):
return 2*(b(ap,dp,ph,e1,e2,e3,e4,e5,y)**3) - 9*a*b(ap,dp,ph,e1,e2,e3,e4,e5,y)*c(ap,dp,ph,e1,e2,e3,e4,e5,y) + 27*(a**2)*d(ap,dp,ph,e1,e2,e3,e4,e5,y)
def y1(ap,dp,ph,e1,e2,e3,e4,e5,y):
return 3*np.sqrt(3)*a*np.sqrt(Delta(ap,dp,ph,e1,e2,e3,e4,e5,y))
return x1, y1
but it is also making a definition error.
Actually by defining such functions i have to construct some new functions like U1(),U2(),U3() using the x1(),y1() and atlast I have to satisfy the best-fit values for U1(),U2(),U3() for this I am doing a for loop run of the variables ap,dp.... Etc. In range (-10,10) to find the all possible set of variables which satisfies the best fit
Your definition error appears to be due to improper indentation. You're returning x1 and y1 outside of the if statement where they're defined.
Your TypeError TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int' implies that either x1(...) or y1(...) returns None, which you allow in your function definition. I'd suggest changing that to explicitly throw an error, assuming None is not a desirable return value. Looking at your code, it seems that Delta(...) is negative when it shouldn't be.
def x1(ap,dp,ph,e1,e2,e3,e4,e5,y):
"x1 is a function that does something"
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
a = 5 # keep `a` in local scope, or include it as a function argument
# e.g., along with ap,dp,ph,e1,e2,e3,e4,e5,y
if Delta(**keywordargs) >= 0:
return 2*(b(**keywordargs)**3) - 9*a*b(**keywordargs)*c(**keywordargs) + 27*(a**2)*d(**keywordargs)
else:
raise ValueError(f"Delta should be non-negative, but instead was {Delta(**keywordargs)}")
def y1(ap,dp,ph,e1,e2,e3,e4,e5,y):
"y1 is a function that does something"
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
a = 5 # keep `a` in local scope, or include it as a function argument
if Delta(**keywordargs) >= 0:
return 3*np.sqrt(3)*a*np.sqrt(Delta(**keywordargs))
else:
raise ValueError(f"Delta should be non-negative, but instead was {Delta(**keywordargs)}")
And...
def Delta(ap,dp,ph,e1,e2,e3,e4,e5,y):
"Delta does some crazy stuff"
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
a = 5 # keep `a` in local scope, or include it as a function argument
# Avoid defining variables with the same name as their function, to avoid confusion and accidental recursion...
delta_val = 18*a*b(**keywordargs)*c(**keywordargs)*d(**keywordargs) \
- 4*(b(**keywordargs)**3)*d(**keywordargs) \
+ (b(**keywordargs)**2)*(c(**keywordargs)**2) \
- 4*a*(c(**keywordargs)**3) \
- 27*(a**2)*(d(**keywordargs)**2)
if delta_val >= 0:
def x1(ap,dp,ph,e1,e2,e3,e4,e5,y):
return 2*(b(ap,dp,ph,e1,e2,e3,e4,e5,y)**3) - 9*a*b(ap,dp,ph,e1,e2,e3,e4,e5,y)*c(ap,dp,ph,e1,e2,e3,e4,e5,y) + 27*(a**2)*d(ap,dp,ph,e1,e2,e3,e4,e5,y)
def y1(ap,dp,ph,e1,e2,e3,e4,e5,y):
return 3*np.sqrt(3)*a*np.sqrt(Delta(ap,dp,ph,e1,e2,e3,e4,e5,y))
return x1(**keywordargs), y1(**keywordargs)
else:
raise ValueError(f"Delta should be non-negative, but instead was {delta_val}")
Also, what is a? I hope it's not a mutable global variable that gets changed throughout your application... because that will create hard to find bugs. I'd recommend avoiding having functions depend on variables that are defined outside of the function (or class containing the function) if you can.
Also, for the love of all that is sacred, make your function names longer or capitalized or something, to help distinguish them from all your one letter variable names. Also, consider having more descriptive variable names, or include comments. Code is read more than it is written. You don't want to make debugging harder than it has to be.
EDIT
After looking at your code a second time, it would be even better if you simplified things further and kept all your functions separate from each other.
# Define your functions separately
def X1(ap,dp,ph,e1,e2,e3,e4,e5,y,a,a):
"""X1 is a function that does something
Input: ap,dp,ph,e1,e2,e3,e4,e5,y,a
Output: some crazy number
Dependencies: b(), c(), d()
"""
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y,a]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y,a".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
result = 2*(b(**keywordargs)**3) \
- 9*a*b(**keywordargs)*c(**keywordargs) \
+ 27*(a**2)*d(**keywordargs)
return result
def Y1(ap,dp,ph,e1,e2,e3,e4,e5,y,a):
"""Y1 is a function that does something
Input: ap,dp,ph,e1,e2,e3,e4,e5,y,a
Output: some crazy number
Dependencies: Delta(), b(), c(), d()
"""
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y,a]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y,a".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
if Delta(**keywordargs) >= 0:
return 3*np.sqrt(3)*a*np.sqrt(Delta(**keywordargs))
else:
raise ValueError(f"Delta should be non-negative, but instead was {Delta(**keywordargs)}")
def Delta(ap,dp,ph,e1,e2,e3,e4,e5,y,a):
"""Delta does some crazy stuff
Input: ap,dp,ph,e1,e2,e3,e4,e5,y,a
Output: some crazy number
Dependencies: b(), c(), d()
"""
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y,a]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y,a".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
# Avoid defining variables with the same name as their function, to avoid confusion and accidental recursion...
delta_val = 18*a*b(**keywordargs)*c(**keywordargs)*d(**keywordargs) \
- 4*(b(**keywordargs)**3)*d(**keywordargs) \
+ (b(**keywordargs)**2)*(c(**keywordargs)**2) \
- 4*a*(c(**keywordargs)**3) \
- 27*(a**2)*(d(**keywordargs)**2)
return delta_val
# Run your already defined functions
if (__name__ == "__main__"):
# ... give values to ap,dp,ph,e1,e2,e3,e4,e5,y,a
variables = [ap,dp,ph,e1,e2,e3,e4,e5,y,a]
varnames = "ap,dp,ph,e1,e2,e3,e4,e5,y,a".split(',')
keywordargs = {k:a for (k,a) in zip(varnames, variables)}
if Delta(**keywordargs) >= 0:
x1,y1 = X1(**keywordargs), Y1(**keywordargs)
Basically, you want to keep the logic of your program separate from individual function definitions. Build your program up from smaller, simpler functions that don't depend on outside global variables or functions.
Also capitalize B(),C(),D(), or give them different names. Do something to show that they're functions and not variables.
Related
Is there an alternative to exec in python?
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.
Calculations - Using functions
I am a bit confused over this. I have a function. Inside that function it asks a number of questions, one is the following based on the number of gardens they have - so if they say they have 2 gardens it will ask this question twice and should add the 100 twice to the calculation: gardens = int(input("How many gardens do you have: ")) def gard(): calc = 0 gardener = input("Do you need a gardener? Y or N ") if gardener == "Y" or gardener == "y": calc = calc + 100 else: calc = calc + 0 for i in range(gardens): gard() How do I keep a running total outside of the function? When I put print(calc) inside the function it just displays 100 each time they say Y but doesn't add it together. Edited to include updated code: The eMake section (the IF statement) returns a value - but it only ever returns the first in the calculation at the end? Also struggling to do the area section since there are numerous ws. It only stores the last value for the variable. noGard = int(input("Enter number of gards which require cleaning: ")) #Defining variables Calc = 0 Area = 0 emCalc = 0 #Room information def GInfo(): global Calc global Area gName = input("Enter gard name: ") noW = int(input("How many w are in the "+gName + "? ")) #Repeats the questions for each W for i in range(noW): Height = float(input("What is the w height of in metres? ")) Width = float(input("What is the w width in metres? ")) Area = Height * Width #Asks if w needs to be removed w = input("Does w need removing? Y or N ") if w == "Y" or w == "y": Calc = Calc + 70 else: Calc = Calc + 0 print (" ") #Returns the values return Calc return Area #Calculate Sarea #Identifying e def e(): global emCalc #eMake eMake = input("What make of e - HH or NN? ") if eMake == "HH" or "hh": emCalc = emCalc + 200 elif eType == "NN" or "nn": emCalc = emCalc + 50 else: print("You have entered an invalid e make") #Returns the values return emCalc #Repeats the g information questions for each g for i in range(noGard): GInfo() # Runs the E function e() #Print total without VAT total = Calc + emCalc print(total) print(Area)
Your function should return the calculated value. def gard(): ... return calc total = 0 for _ in range(gardens): total += gard() print 'Total: ', total
The whole point of functions, really, is that they take parameters and return values. (Some languages, although not Python, refer to functions that don't do this as "procedures".) That is what you need to do here: your gard function needs to return the value of calc. You probably don't want to actually do the addition inside the function itself, but if you did, you would also need to accept the current value of calc as a parameter, which you would pass in from your for loop.
Functions, in the strictest sense, do not have state. When writing functional programs, one typically aims to keep their functions pure, meaning that the result of the function does not depend on anything but its inputs and does not cause observable side effects. But Python is not a purely functional language. It is an object-oriented procedural language which models functions as objects, and objects can be stateful. So you can do what you're aiming to, if you don't take the word "function" too literally. The Right Thing™ Create a class which models your data and the operations on it: >>> class F(object): ... def __init__(self): ... self.x = 0 ... def f(self): ... self.x += 1 ... return self.x ... >>> my_f = F() >>> my_f.f() 1 >>> my_f.f() 2 Fun and naughty ways Add state to the function object, taking advantage of the fact that function bodies aren't executed until the function is called: >>> def f(): ... f.x += 1 ... return f.x ... >>> f.x = 0 >>> f() 1 >>> f() 2 If you want to do this transparently (that is, make it so that you don't have to add this state to the function right after defining it) you can close over the state by having a function create a function: >>> def g(): ... def func(): ... func.x += 1 ... return func.x ... func.x = 0 ... return func ... >>> f = g() >>> f() 1 >>> f() 2 To take it a step further, create a decorator so you don't have to do any assignments after defining the function at all: >>> def with_x(func): ... func.x = 0 ... return func ... >>> #with_x ... def f(): ... f.x += 1 ... return f.x ... >>> f() 1 >>> f() 2 Or you can just use global to let a function refer to something outside of its local scope, not taking advantage of the fact that functions are objects: >>> x = 0 >>> def f(): ... global x ... x += 1 ... return x ... >>> f() 1 >>> f() 2 >>> x 2 Updates for your edit Since you went with global I'll first refer you to a good question that explains global. Using global variables in a function other than the one that created them Now, as for your particular problems: The eMake section (the IF statement) returns a value - but it only ever returns the first in the calculation at the end? Sure, there are a couple problems here and one of them is definitely a common one for beginners. or takes higher precedence than == so your condition parses like this: if (eMake == "HH") or ("hh"): This gets people all the time. In Python, if a value isn't a boolean and you put it in a conditional statement, it gets evaluated as a boolean using a series of truthiness rules. In this case a non-empty string is considered True so you are basically saying if (eMake == "HH") or True. To fix this, fix the right-hand side of the condition: if (eMake == "HH") or (eMake == "hh"): By the way, you probably meant elif (eMake == "NN") or (eMake == "nn"): instead of elif eType == "NN" or "nn": because you never defined eType (and for the reason above, too.) If you type nn there you'll get an exception. Also struggling to do the area section since there are numerous ws. It only stores the last value for the variable. This is because you repeatedly assign to the same variable with Area = Height * Width. Since Area is global, it's the same variable every time you call GInfo(). If it wasn't global it would be a new variable every time you called the function, but then you would need to return it and assign the return value to a variable in order to save the value. Otherwise it would disappear since it was never assigned to anything. Now, I don't know what you are trying to do with the areas you're calculating. Do you want to keep them separate or sum them together? If you want to keep them separate, you'll need to use a data structure. In this case, you'd definitely want to use a list. Using the append() method of lists, you can add an item to the list. So it would look something like this: areas = [] # empty list def GInfo(): global areas # the stuff before the loop for i in range(noW): Height = float(input("What is the w height of in metres? ")) Width = float(input("What is the w width in metres? ")) areas.append(Height * Width) # the stuff after the loop If you want to sum them together, just make sure you add each individual area calculation to the previous result, just like you did with Calc: Area += Height * Width One more thing: your GInfo() function is only returning Calc and not Area as well. Functions can only return one value. In the mathematical sense, a function is a many-to-one mapping between two sets. So in Python, a function ends at the return statement. Nothing else gets executed after that. In order to get both the value of Calc as well as the value of Area from the return value of GInfo(), you will have to return a data structure. Usually this would be a tuple. return (Calc, Area) But your code doesn't assign the return value of GInfo() to anything. Instead, it uses the global declaration to change the value of the global variables. So there shouldn't be an issue here.
Python static variable in function global name not defined
I have written a function to calculate the heading between two points only if a vehicle reports that it's moving and that the vehicle has moved 20cm between points. The function uses static variables - or at least it would if it worked - to keep track of previous positions and heading values. Here is the code: def withCan(pos): eastdist = pos[0]-previous_pos[0] northdist = pos[1]-previous_pos[1] canflag = pos[2] if (canflag == 1 or canflag==2): if (previous_canflag == 1 and canflag == 2): previous_heading += 180.0 previous_canflag = canflag elif (previous_canflag == 2 and canflag == 1): previous_heading += 180.0 previous_canflag = canflag else: previous_canflag = canflag if ( (canflag == 1 or canflag == 2) and math.sqrt(northdist*northdist+eastdist*eastdist) > canstep ): previous_heading = math.degrees(math.atan2(eastdist, northdist)) previous_pos[0] = pos[0] previous_pos[1] = pos[1] return previous_heading withCan.previous_pos = [0.0,0.0] withCan.previous_heading = 0.0 withCan.previous_canflag = 0 withCan.canstep = 0.2 positions = backandforth([100,100]) #populates an array of form [x,y,canflag] for p in positions: print withCan(p) I am getting an error that says eastdist = pos[0]-previous_pos[0] NameError: global name 'previous_pos' is not defined. Please could someone explain the cause of this error?
When you do this: def foo(): pass foo.name = 1 You are not creating a global name name. Instead you are adding a property to the foo function! You can access it with: def foo(): return foo.name foo.name = 1 But that is quite weird. If you need a global name, just do it: def foo(): global name name += 1 return name name = 1 Remember that if you want to modify the global name from the function, you have to declare it as global. If you fail to do this, you can use it but you cannot assign to it. Your confusion with static names may come from using classes. But note that in your code withCan is not a class, it is a plain function!
It looks like what you are trying to do is writing a class... class WithCan(): def __init(self, previous_pos)__: self.previous_pos=previous_pos def withCan(self, pos): # your function as class method Then you could initialize an instance withCan=WithCan(previous_pos) and access it withCan.previous_pos=...
You can do static variables in Python using function attributes, but you need to use the full name inside the function to access those attributes. Here's a short demo. def test(a): print a, a + test.b test.b += 1 test.b = 5 test(3) test(10) output 3 8 10 16 However, it would be more usual to do this sort of thing using a class, as shown in Tim's answer. Another way to do statics in Python is to give your function default mutable arguments, but many people are uncomfortable with that. But if you're curious, please see “Least Astonishment” in Python: The Mutable Default Argument.
Let me contribute a perhaps more streamlined way of emulating static variables in functions that could make the OP's example maybe easier to read: def with_can(pos): if not hasattr(with_can, "canflag"): # set up and initialise the static variables with_can.canflag = 0 with_can.previous_pos = [0.0,0.0] with_can.previous_heading = 0.0 with_can.canstep = 0.2 # ... use them ... eastdist = pos[0]-with_can.previous_pos[0] # ... etc ... Basically at the first invocation we detect that one of the "static" variables (canflag) is not yet there so we add and initialise all of them. After that they can be used as indicated. However, as others have pointed out already, it is much better to write a class with data members instead of these "static" function variables.
Decorators in PyMC
I have three question regarding decorators which I am not able to find answer to : Q1)What do the arguments to decorators in PyMC (#Deterministic, #Stochastic) denote ? Q2) #pymc.stochastic(dtype=int) def switchpoint(value=10, t_l=0, t_h=110): def logp(value, t_l, t_h): if value > t_h or value < t_l: return -np.inf else: return -np.log(t_h - t_l + 1) def random(t_l, t_h): from numpy.random import random return np.round( (t_l - t_h) * random() ) + t_l 1)print switchpoint.logp #prints log-probability as expected 2)print switchpoint.random #does not generate the random number 3)print switchpoint.random() # generates a random number 4)print switchpoint.logp() #error If 2 did not work and 3 worked then 1 should not have worked and instaed 4 should have worked (which is opposite of what I observed). Can someone explain what is going on ? Q3) #pymc.stochastic(dtype=int) def switchpoint(value=1900, t_l=1851, t_h=1962): if value > t_h or value < t_l: # Invalid values return -np.inf else: # Uniform log-likelihood return -np.log(t_h - t_l + 1) Here it is not specified that it is logp still if I type switchpoint.logp, this piece of code is executed ?
Q1) The meaning of all the arguments to stochastic is documented here. The arguments to deterministic are the same, plus the additional ones documented here. Q2) The difference in behavior is that there is some magic inside PyMC that actually executes the switchpoint.logp function and turns it into a Python property, while switchpoint.random doesn't get this treatment, and is kept as a function. If you're curious about what's actually going on, here's some of the relevant the source: def get_logp(self): if self.verbose > 1: print '\t' + self.__name__ + ': log-probability accessed.' logp = self._logp.get() if self.verbose > 1: print '\t' + self.__name__ + ': Returning log-probability ', logp try: logp = float(logp) except: raise TypeError, self.__name__ + ': computed log-probability ' + str(logp) + ' cannot be cast to float' if logp != logp: raise ValueError, self.__name__ + ': computed log-probability is NaN' # Check if the value is smaller than a double precision infinity: if logp <= d_neg_inf: if self.verbose > 0: raise ZeroProbability, self.errmsg + ": %s" %self._parents.value else: raise ZeroProbability, self.errmsg return logp def set_logp(self,value): raise AttributeError, 'Potential '+self.__name__+'\'s log-probability cannot be set.' logp = property(fget = get_logp, fset=set_logp, doc="Self's log-probability value conditional on parents.") There's some other stuff going on there, like during the logp function into something called a LazyFunction, but that's the basic idea. Q3) The stochastic decorator has some (more) magic in it that uses code introspection to determine if random and logp sub functions are defined inside switchpoint. If they are, it uses the logp sub-function to compute logp, if not, it just uses switchpoint itself. That source code for that is here: # This gets used by stochastic to check for long-format logp and random: if probe: # Define global tracing function (I assume this is for debugging??) # No, it's to get out the logp and random functions, if they're in there. def probeFunc(frame, event, arg): if event == 'return': locals = frame.f_locals kwds.update(dict((k,locals.get(k)) for k in keys)) sys.settrace(None) return probeFunc sys.settrace(probeFunc) # Get the functions logp and random (complete interface). # Disable special methods to prevent the formation of a hurricane of Deterministics cur_status = check_special_methods() disable_special_methods() try: __func__() except: if 'logp' in keys: kwds['logp']=__func__ else: kwds['eval'] =__func__ # Reenable special methods. if cur_status: enable_special_methods() for key in keys: if not kwds.has_key(key): kwds[key] = None for key in ['logp', 'eval']: if key in keys: if kwds[key] is None: kwds[key] = __func__ Again, there's some more stuff going on, and it's fairly complicated, but that's the basic idea.
Python: exec "function()"
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.