Python: break out of for loop calling a function [duplicate] - python

This question already has answers here:
Break out of a while loop using a function
(3 answers)
Closed 7 years ago.
I need to break out of a for loop according to the result obtained after calling a function. This is an example of what I'm after (does not work obviously):
def break_out(i):
# Some condition
if i > 10:
# This does not work.
return break
for i in range(1000):
# Call function
break_out(i)
Of course this is a very simple MWE, my actual function is much bigger which is why I move it outside of the for loop.
This answer says it is not possible and I should make the function return a boolean and add an if statement inside the for loop to decide.
Since it's a rather old question and it didn't get much attention (also, it's applied to while loops), I'd like to re-check if something like this is possible.

No, it's not (reasonably) possible. You could raise an exception, I suppose, but then you'll have to catch it with a try/except statement outside the loop.
Since OP has expressed curiosity about this, I'm going to explain why Python doesn't allow you to do this. Functions are supposed to be composable elements. They're meant to work in different contexts.
Allowing a function to break out of a loop has two separate problems:
Client code might not expect this behavior.
What if there is no loop?
For (1), if I read some code like this:
import foo # some library I didn't write
while condition:
foo.bar()
assert not condition
I reasonably assume the assertion will not fire. But if foo.bar() was able to break out of the loop, it could.
For (2), perhaps I wrote this somewhere else:
if condition:
foo.bar()
It's not clear what that should do if foo.bar() tries to break out of the loop, since there is no loop.

Rather than return break, return a value that forces a break condition. To extend your example:
def break_out(i):
# Some condition
return i > 10 # Returns `True` if you should break from loop.
for i in range(1000):
# Call function
if break_out(i):
break
# Insert the rest of your code here...

Related

Reusing a variable from an if clause [duplicate]

This question already has answers here:
Python: avoiding if condition for this code?
(8 answers)
What does colon equal (:=) in Python mean?
(6 answers)
Closed 27 days ago.
Let's say we have the following code:
if re.search(r"b.", "foobar"):
return re.search(r"b.", "foobar").group(0)
This is obviously a redundant call, which can be avoided by assigning the condition to a variable before the if block:
match = re.search(r"b.", "foobar")
if match:
return match.group(0)
However, this means that the condition is always evaluated. In the example above, that makes no difference, but if the match is only used in an elif block, that's an unnecessary execution.
For example:
match = re.search(r"b.", "foobar")
if somecondition:
return "lorem ipsum"
elif match:
return match.group(0)
If somecondition is true and we had the redundant version like in the first code block, we would never call re.search. However, with the variable placed before the if-elif-Block like this, it would be called unnecessarily. And even with the duplicated call, we're instead executing it twice if somecondition is false.
Unfortunately, depending on the use case, evaluating the condition could be very computationally expensive. With the two variants above, if performance is the goal, a choice can be made depending on the likelyhood of somecondition evaluating to true. More specifically, if the elif block is called more often than not, declaring the variable before if the if block is more performant (unless Python somehow caches the result of two identical stateless function calls), whereas the alternative is better if the elif block is rarely reached.
Is there a way to avoid this duplication by reusing the variable from the (el)if block?

Variable value is kept into next iteration of "for" loop?

Take the pseudocode below for example:
Python keeps the previous value for x, so if get_sum() fails, the conditional is still checked using the previous value of x.
Is this because python for loop doesn't introduce a new scope and is it ok to simply del the object at the end of each iteration?
for number in number_list:
try:
x = get_sum()
except:
....
if x > 100:
do something
Every variable in python is created in the scope of their respective functions and classes rather than at different levels of indentation, or in for loops or while loops. There's no block scope in python like there is in java.
If you need x to not retain its old value, you can always set x = None in your except clause and have a conditional catch it later on. If i'm misinterpreting your question please leave a comment

Refactoring a huge function into a set of smaller functions

In terms of clean code, how should a function that has nested for loops, if-else statements, and while loops be refactored? What would be the ideal, clean structure for a big function like this? Is it acceptable to break a function like this up to nested functions?
def main():
try:
for
if
for
while
for
for
if
for
if
else
if
else
if
except:
if __name__ == "__main__":
main()
Only nest loops iniside loops if you really need, otherwise avoid nesting them (for algorithmic performance reasons).
Use the Omri answer advice to identify each step you are doing, give each step a clear name and extract the step into its own function (that you call to perform the step in the original function).
This is different from nesting functions, something done for different reasons.
You are just making calls to helper functions placed somewhere else (not nested in your function).
Do not surround everything in a try block, and avoid the catch all empty except:. Surround only around the specific (or the few statements) than can cause trouble, and be specific to list in the expect clause only the error or error category(ies) you are expecting there.
It is mainly opinion-based and depends on the code itself. A good rule of thumb is that each function needs to have one logical purpose.

Python: Should I avoid initialization of variables inside blocks?

Problem
I have a code like this
if condition:
a = f(x)
else:
a = g(y)
Initialization of a inside of the block looks bad for me. Can it be written better?
I cannot use ternary operator, because names of functions and/or lists of arguments are long.
Saying "long" I mean that the following expression
a = f(x) if condition else g(y)
will take more than 79 (sometimes even more than 119) symbols with real names instead of a, f, g, x, y and condition.
Usage of multiple slashes will make the code ugly and messy.
I don't want to initialize a with result of one of the functions by defaul, because both function are slow and I cannot allow such overhead
a = g(y)
if condition:
a = f(x)
I can initialize the variable with None, but is this solution pretty enough?
a = None
if condition:
a = f(x)
else:
a = g(y)
Let me explain my position: in C and C++ variables inside of a block have the block as their scope. In ES6 the let keyword was introduced — it allows to create variables with the same scoping rules as variables in C and C++. Variables defined with old var keyword have similar scoping rules as in Python.
That's why I think that initialization of variables should be made outside blocks if I want to use the variables outside these blocks.
Update
Here is more complicated example
for obj in gen:
# do something with the `obj`
if predicate(obj):
try:
result = f(obj)
except Exception as e:
log(e)
continue
else:
result = g(obj)
# do something useful with the `result`
else:
result = h(obj)
display(result)
I go through elements of some generator gen, process them and perform some actions on the result on each iteration.
Then I want to do something with the last result outside of the loop.
Is it pythonic enough to not assign a dummy value to the result beforehand?
Doesn't this make the code less readable?
Question
Is it good to initialize variables inside if/else/for/etc. in Python?
Python has no block scope... the scope is the whole function and it's perfectly pythonic to write
if <condition>:
a = f()
else:
a = g()
If you want to write in C++ then write in C++ using C++, don't write C++ using Python... it's a bad idea.
Ok, there are two points that need to be clarified here which are fundamental to python.
There is no variable declaration/initialization in python. An expression like a = f(x) is simply a scheme to name the object that is returned by f as a. That namea can be later used to name any other object no matter what its type is. See this answer.
A block in python is either the body of a module, a class or a function. Anything defined/named inside these objects are visible to later code until the end of a block. A loop or an if-else is not a block. So any name defined before outside the loop or if/else will be visible inside and vice versa. See this. global and nonlocal objects are a little different. There is no let in python since that is default behavior.
In your case the only concern is how you are using a further in the code. If you code expects the type of objects returned by f or g it should work fine unless there is an error. Because at least one of the if or the else should run in a normal operation so a will refer to some kind of an object (if the names were different in if and else that would be a problem). If you want to make sure that the subsequent code does not break you can use a try-except-else to catch any error generated by the functions and assign a default value to a in the except clause after appropriate reporting/logging of the error.
Hence to summarize and also to directly address your question, assigning names to objects inside an if-else statement or a loop is perfectly good practice provided:
The same name is used in both if and else clause so that the name is guaranteed to refer to an object at the end of the statement. Additional try-except-else error catching can take care of exceptions raised by the functions.
The names should not be too short, generic or something that does not make the intention of the code clear like a, res etc. A sensible name will lead to much better readability and prevent accidental use of the same name later for some other object thereby losing the original.
Let me clarify what I meant in my comments.
#this is not, strictly, needed, but it makes the
#exception handler more robust
a = b = None
try:
if condition:
a = f(x)
b = v(x)
else:
a = g(y)
b = v2(x)
return w(a, b)
except Exception, e:
logger.exception("exception:%s" % (e))
logger.exception(" the value of a was:%s" % (a))
logger.exception(" the value of b was:%s" % (b))
raise
This is pretty std code, you just want to wrap the whole thing in some logging code in case of exceptions. I re-raise the original exception, but could just as easily return a default value.
Problem is, unless the exception waits until return w(a, b) to happen, any access to a and b will throw its own NameError based on those variables not having been declared.
This has happened to me, a lot, with custom web unittesting code - I get a response from a get or post to an url and I run a bunch of tests against the response. If the original get/post that failed, the response doesn't exist, so any diagnostics like pretty printing that response's attributes will throw an exception, forcing you to clean things up before your exception handler is useful.
So, to guard against this, I initialize any variable referred to in the exception handler to None. Of course, if needed, you also have to guard against a being None, with something like logger("a.attr1:%s" % (getattr(a, "attr1","?")

Calling a function within a function creates loop?

I was teaching a python intro course this morning , one of my student came with a question I could not answer
I create a function and within that function, I'm calling the same function and it is looping
def prenom():
print("Nadia")
prenom()
Why?
This is called recursion with no base-case.
You call a function, it (recursively) calls itself and so on. There's no stopping condition, so it will loop forever. This is how infinite loops are created in assembly.
Obviously it will loop.
You haven't set a terminating condition.
Set exit() before calling the function again and you will terminate it successfully (and by termination I mean you will end the program).
Alternatively you may use an if-else condition

Categories

Resources