My coworker and I have been talking about some code that he wrote in a pull request. It involves defining a function within each branch of a conditional statement.
Original:
if is_something: # constant, but unknown until runtime
def do_thing():
return 'x'
else:
def do_thing():
return 'y'
while True:
# other code here
foo = do_thing()
My first thought was to shift the conditional into the function definition and use it like that, but that does mean the condition is needlessly evaluated for every loop.
My second thought was to define two different functions (with different names), then in a conditional to assign the correct function to a variable which is then later called in the loop.
Option 2:
def thing_x():
return 'x'
def thing_y():
return 'y'
if is_something: # constant, but unknown until runtime
thing_func = thing_x
else:
thing_func = thing_y
while True:
# other code here
foo = thing_func()
I like this version better, but I can't articulate why I like this version better.
Is there any practical reason for one or the other, or is it just a "pick one" situation?
I would go with Option 2. Defining a function inside an if statement is a poor way to organize your code. If you group all your functions together it will be easier to read and maintain. Pretend that you have left your company and someone else is trying to update your code. You want it to be as readable as possible.
I know this is just your example snippet but I also wanted to point out that having a "while True" condition is also a bad idea as it could create an infinite loop.
It seems like you could do the following and then the code would even work if is_something switches:
def do_thing(is_something):
if is_something:
# Some code
else:
# Other code
while True:
do_thing(is_something)
Option 2 is more reasonable. We define function so that we can call them and use as and when necessary without rewriting the code again and again. I don't understand why you need to write a function within a condition. Assuming do_thing() is same function :
Original code can be written as:
def do_thing():
return 'x'
if is_something:
do_thing()
else:
do_thing()
while True:
# other code here
foo = do_thing()
Related
I have a class/wrapper for a training a neural-network using PyTorch. To simplify the question say the class looks something like
class Wrapper:
def __init__(self,validate,n_epochs =100_000_000):
self.validate = validate
self.val_score = 0
def run(self):
for _ in self.n_epochs:
if self.validate:
self.val_score += val(self.X,self.y)
as you can see the check if self.validate has to be run 100.000.000 times, but the statement is always the same i.e it is always the same (it cannot suddenly switch from True to False).
I know I can write a function for each case of validate and then call the correct function in run. The issue is tho, that having, say, 3 of such "static" boolean variables would result in 9 functions in total where 99% of the code in each function is the same as the other 8 functions.
Is there a way to, somehow, define a "static" path in the "if-statement" without writing a function for each case to reduce the check-overhead? I have read some where, that some compilers tries to guess the direction of the if-statements to speed up the processes. If that's the case I assume the check-overhead of our run function is rather fast limited since it would start guessing correctly relative fast.
Note the order of the loops has to be as above.
Not sure if I understood your question correctly, but I think you're saying, self.validate value remains the same within, at least in run() function.
Then why not change the order of if and for?
if self.validate:
for _ in self.n_epochs:
self.val_score = val(self.X, self.Y)
If there's some reason why you can't do so, I'd like to know the reason and maybe come up with another solution for you
Edit: ok if order has to be maintained this is another option
outside of the loop just make a local boolean variable
_validate = self.validate
for _ in self.n_epochs: # probably range(self.n_epochs), btw
if _validate:
do_the_thing()
else:
break # or do_something_else()
or if you really just want to make the self.validate as static variable,
you need to declare it outside of init function
class YourClass:
validate = False # This is static variable
def __init__(self, something, somethingelse):
self.validate=False # This is instance variable
Last edit:
move whole thing in your for loop to a function or method defined by you, and make it take the validate boolean value (or your class object) as argument.
def do_for_loop(self):
if isinstance(self.validate, bool):
if validate:
for _ in self.n_epochs:
# do your thing in for loop
else:
# do something else when validate is False
else:
# raise error in case self.validate is not boolean for whatever reasons
I have a relatively large main() function where I need to check a variable several times at different places in the function and then decide if I should continue the loop accordingly, like so:
def main():
while True:
...
if foo.check():
reset()
continue
...
Where foo is working as a time keeper of sorts, therefore I need to check it at certain intervals. These 3 lines appear about 3 to 5 times within the function.
I hate how dirty this is, so is there a cleaner way to do this?
You haven't specified enough information. I have two questions:
Is the call foo.check() idempotent meaning it always returns the same value and has no side-effects?
Is there a path through the code where you can reach the nth call to foo.check() in the same block governed by the continue statement without first calling the n-1th occurrence?
If, for example, the answer to the answer is yes and the second question were no, then you could remove all but the first occurrence of the call to foo.check() because the return value is clearly False or else you would never reach the second occurence.
If the answer to the first question is yes and the second is yes, then if the call to foo_check() is expensive, I might consider up front setting:
check_result = foo.check()
and then replacing every call to foo_check() with check_result. But ultimately you still need to do all the checks. But in all cases you can create a function check_and_reset:
def check_and_reset():
if foo_check():
reset()
return True
return False
Then your code becomes:
if check_and_reset(): continue
I have two suggestions:
Suggestion 1:
Create another function and call wherever you want.
Suggestion 2:
Make it as a one-liner
if foo.check():reset();continue
If it is just a complex way to control some timing while running a set of tasks, I'd suggest to introduce a loop over the tasks. You can even easily pass partial results around:
def task_a():
...
def task_c(some_input):
...
tasks = [lambda x: task_a(), task_c]
last_stage_output = None
while True:
reset()
for task in tasks:
if not foo.check():
break
last_stage_output = task(last_stage_output)
This way, you make it clear that it is just a series of tasks to be done, it's simple to add, remove of reshuffle them, and the timing logic is concentrated in a single point.
I'm practicing writing functions that do only one thing. I'm getting stuck with the below functions. play_games is what the user calls to run the whole program. Ideally, it should only have the call to the play_many_games method which is in one of the program's classes.
But if I don't also add the line that calls the prepare_reports function, then the reports will never get taken care of. How do I "do reports and games" if I insist on only having functions that do one thing?
Is the bottom line that we need a main function that can do more than one thing (e.g. a main function that can do both games and reports calls)?
def play_games(number_of_games):
games_engine = prepare_reports(number_of_games)
games_engine.play_many_games(number_of_games)
def prepare_reports(number_of_games):
report_requests = []
if number_of_games <= 100:
report_on_game = GameReport()
report_requests.append(report_on_game)
report_on_many_games = ManyGamesReport()
report_requests.append(report_on_many_games)
return GamesEngine(*report_requests)
Having a main function that does nothing but drive the process and call other functions, is a function that only does one thing. I suggest you create one.
You could try this, since there's not much of a point in declaring games_engine:
def play_games(number_of_games):
prepare_reports(number_of_games).play_many_games(number_of_games)
def prepare_reports(number_of_games):
report_requests = []
if number_of_games <= 100:
report_on_game = GameReport()
report_requests.append(report_on_game)
report_on_many_games = ManyGamesReport()
report_requests.append(report_on_many_games)
return GamesEngine(*report_requests)
However, what you showed us is technically a function doing only one thing. You see, by declaring games_engine = prepare_reports(number_of_games), you are just making your code easier to read.
I'm currently trying to code a Python (3.4.4) GUI with tkinter which should allow to fit an arbitrary function to some datapoints. To start easy, I'd like to create some input-function and evaluate it. Later, I would like to plot and fit it using curve_fit from scipy.
In order to do so, I would like to create a dynamic (fitting) function from a user-input-string. I found and read about exec, but people say that (1) it is not safe to use and (2) there is always a better alternative (e.g. here and in many other places). So, I was wondering what would be the alternative in this case?
Here is some example code with two nested functions which works but it's not dynamic:
def buttonfit_press():
def f(x):
return x+1
return f
print(buttonfit_press()(4))
And here is some code that gives rise to NameError: name 'f' is not defined before I can even start to use xval:
def buttonfit_press2(xval):
actfitfunc = "f(x)=x+1"
execstr = "def {}:\n return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1])
exec(execstr)
return f
print(buttonfit_press2(4))
An alternative approach with types.FunctionType discussed here (10303248) wasn't successful either...
So, my question is: Is there a good alternative I could use for this scenario? Or if not, how can I make the code with exec run?
I hope it's understandable and not too vague. Thanks in advance for your ideas and input.
#Gábor Erdős:
Either I don't understand or I disagree. If I code the same segment in the mainloop, it recognizes f and I can execute the code segment from execstr:
actfitfunc = "f(x)=x+1"
execstr = "def {}:\n return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1])
exec(execstr)
print(f(4))
>>> 5
#Łukasz Rogalski:
Printing execstr seems fine to me:
def f(x):
return x+1
Indentation error is unlikely due to my editor, but I double-checked - it's fine.
Introducing my_locals, calling it in exec and printing in afterwards shows:
{'f': <function f at 0x000000000348D8C8>}
However, I still get NameError: name 'f' is not defined.
#user3691475:
Your example is very similar to my first example. But this is not "dynamic" in my understanding, i.e. one can not change the output of the function while the code is running.
#Dunes:
I think this is going in the right direction, thanks. However, I don't understand yet how I can evaluate and use this function in the next step? What I mean is: in order to be able to fit it, I have to extract fitting variables (i.e. a in f(x)=a*x+b) or evaluate the function at various x-values (i.e. print(f(3.14))).
The problem with exec/eval, is that they can execute arbitrary code. So to use exec or eval you need to either carefully parse the code fragment to ensure it doesn't contain malicious code (an incredibly hard task), or be sure that the source of the code can be trusted. If you're making a small program for personal use then that's fine. A big program that's responsible for sensitive data or money, definitely not. It would seem your use case counts as having a trusted source.
If all you want is to create an arbitrary function at runtime, then just use a combination of the lambda expression and eval. eg.
func_str = "lambda x: x + 1" # equates to f(x)=x+1
func = eval(func_str)
assert func(4) == 5
The reason why your attempt isn't working is that locals(), in the context of a function, creates a copy of the local namespace. Mutations to the resulting dictionary do not effect the current local namespace. You would need to do something like:
def g():
src = """
def f(x):
return x + 1
"""
exec_namespace = {} # exec will place the function f in this dictionary
exec(src, exec_namespace)
return exec_namespace['f'] # retrieve f
I'm not sure what exactly are you trying to do, i.e. what functions are allowed, what operations are permitted, etc.
Here is an example of a function generator with one dynamic parameter:
>>> def generator(n):
def f(x):
return x+n
return f
>>> plus_one=generator(1)
>>> print(plus_one(4))
5
I wanna to make my code look nicer.
Example:
result = somefunction()
if result == what_i_need:
do_something()
else:
print 'that\'s not what i need', result
I'm trying to make something like this:
if somefunction() == what_i_need:
do_something()
else:
print 'that\'s not what i need', <return value of somefunction>
Is this possible?
Here's something you could technically try, but shouldn't:
def somefunction():
val = 3 # calculate value
globals()['somefunction_return'] = val # store it in globals() first
return val # return as normal
def do_something():
print 'got it'
what_i_need = 3
if somefunction() == what_i_need:
do_something()
print 'that\'s not what i need', somefunction_return
# result:
#
# got it
# that's not what i need 3
Why shouldn't you do this?
The assignment is still happening somewhere else, so all its done is sweep the unwanted code out of the way. Pay no attention to the garbage behind the curtain.
Messing with globals() is more fragile than a simple assignment done in the way you're already doing it, partly because it's more difficult for the developer to avoid tripping over it.
It introduces something called "side effects," extra events that a function does without saying so. In this example, somefunction is doing something in addition to its planned purpose, without advertising as much. This makes code much harder to maintain.
It makes the code more difficult to read and follow, which goes against the intent of Python. Imagine that someone is reading the if structure above. How are they supposed to know where somefunction_return came from? They would have to look through every line of code until they found the offending side effect in somefunction. We might have even named it something else, like my_foobar_value, and then the reader wouldn't even have a hint that they should check somefunction.
Please continue to use the ordinary assignment you already have in place.
No, the only sensible way to capture a function's return value in Python is to explicitly assign it to a variable.
Even if there was a device for referring to a previous function's return value through implicit means, Python culture abhors this sort of thing. One oft-cited Python principle is "explicit is better than implicit".