When to use "while" or "for" in Python - python

When I should use a while loop or a for loop in Python? It looks like people prefer using a for loop (for brevity?). Is there any specific situation which I should use one or the other? Is it a matter of personal preference? The code I have read so far made me think there are big differences between them.

Yes, there is a huge difference between while and for.
The for statement iterates through a collection or iterable object or generator function.
The while statement simply loops until a condition is False.
It isn't preference. It's a question of what your data structures are.
Often, we represent the values we want to process as a range (an actual list), or xrange (which generates the values) (Edit: In Python 3, range is now a generator and behaves like the old xrange function. xrange has been removed from Python 3). This gives us a data structure tailor-made for the for statement.
Generally, however, we have a ready-made collection: a set, tuple, list, map or even a string is already an iterable collection, so we simply use a for loop.
In a few cases, we might want some functional-programming processing done for us, in which case we can apply that transformation as part of iteration. The sorted and enumerate functions apply a transformation on an iterable that fits naturally with the for statement.
If you don't have a tidy data structure to iterate through, or you don't have a generator function that drives your processing, you must use while.

while is useful in scenarios where the break condition doesn't logically depend on any kind of sequence. For example, consider unpredictable interactions:
while user_is_sleeping():
wait()
Of course, you could write an appropriate iterator to encapsulate that action and make it accessible via for – but how would that serve readability?¹
In all other cases in Python, use for (or an appropriate higher-order function which encapsulate the loop).
¹ assuming the user_is_sleeping function returns False when false, the example code could be rewritten as the following for loop:
for _ in iter(user_is_sleeping, False):
wait()

The for is the more pythonic choice for iterating a list since it is simpler and easier to read.
For example this:
for i in range(11):
print i
is much simpler and easier to read than this:
i = 0
while i <= 10:
print i
i = i + 1

for loops is used
when you have definite itteration (the number of iterations is known).
Example of use:
Iterate through a loop with definite range: for i in range(23):.
Iterate through collections(string, list, set, tuple, dictionary): for book in books:.
while loop is an indefinite itteration that is used when a loop repeats
unkown number of times and end when some condition is met.
Note that in case of while loop the indented body of the loop should modify at least one variable in the test condition else the result is infinite loop.
Example of use:
The execution of the block of code require that the user enter specified input: while input == specified_input:.
When you have a condition with comparison operators: while count < limit and stop != False:.
Refrerences: For Loops Vs. While Loops, Udacity Data Science, Python.org.

First of all there are differences between the for loop in python and in other languages.
While in python it iterates over a list of values (eg: for value in [4,3,2,7]), in most other languages (C/C++, Java, PHP etc) it acts as a while loop, but easier to read.
For loops are generally used when the number of iterations is known (the length of an array for example), and while loops are used when you don't know how long it will take (for example the bubble sort algorithm which loops as long as the values aren't sorted)

Consider processing iterables. You can do it with a for loop:
for i in mylist:
print i
Or, you can do it with a while loop:
it = mylist.__iter__()
while True:
try:
print it.next()
except StopIteration:
break
Both of those blocks of code do fundamentally the same thing in fundamentally the same way. But the for loop hides the creation of the iterator and the handling of the StopIteration exception so that you don't need to deal with them yourself.
The only time I can think of that you'd use a while loop to handle an iterable would be if you needed to access the iterator directly for some reason, e.g. you needed to skip over items in the list under some circumstances.

For loops usually make it clearer what the iteration is doing. You can't always use them directly, but most of the times the iteration logic with the while loop can be wrapped inside a generator func. For example:
def path_to_root(node):
while node is not None:
yield node
node = node.parent
for parent in path_to_root(node):
...
Instead of
parent = node
while parent is not None:
...
parent = parent.parent

while loop is better for normal loops
for loop is much better than while loop while working with strings, like lists, strings etc.

If your data is dirty and it won't work with a for loop, you need to clean your data.

For me if your problem demands multiple pointers to be used to keep
track of some boundary I would always prefer While loop.
In other cases it's simply for loop.

Related

Out of memory when use for loop to input parameter in recursive function Python

I have a recursive function:
def recursive_func(parameter1):
.....
if __name__ == "__main__":
list_parameter_input = [a,b,c,d,e]
for item in list_parameter_input:
recursive_func(item)
Note: my recursive_func is very complex but I'm sure it won't have any error.
The problem is when I input a parameter1 with a single item in list_parameter_input, it's fast and needs small memory. When I input a parameter1 using for loop, it's prolonged and makes out of memory.It's so weird.
I'm checked time when recursive_func complete with each value in list_parameter_input.
When input with not using for loop: recursive_func(a) need x time.
When input with using for loop: recursive_func(a) need more x time (10x,11x,...) but with b,c,d,e give a same time.
I don't know why it's happen but I'm really sure recursive_func(a) is very fast when I input a with not using for loop
I mean recursive_func(i) is probably recursive_func(item) as you're never using i anywhere.
But the real problem seems to be that you're running the entire loop for each recursion. So each element of the list starts a recursion where it loops through the entire list, not just the rest of the list but the entire list. And each of the element of that loop does the same so:
5 recursions for the elements in the list
5*5 recursions for the elements in the list in the recursions
5*5*5 recursions for the elements in the list in the recursions of the recursion
...
That soon leads to a stack overflow.
Maybe I misunderstand your description of the code (you are technically not providing a Minimal, Reproducible Example), but if the code looks like this:
def recursive_func(parameter1):
...
list_parameter_input = [a,b,c,d,e]
for item in list_parameter_input:
recursive_func(item)
... then you have an infinite loop.
I.e. Unless the list_parameter_input is modified (items are removed) you are not making progress.
Can you verify that you are actually making progress through the input parameters?

Is there any better way (than by variable) to find if loop block was executed (even once)?

I know that python has else loop feature:
for item in items:
# loop block
else:
# will execute if there is no exception or break in loop block
And because of that feature I wonder if there is any other smart things about loops in python. And for now I want to find a better way (than by variable) to find if loop block was executed (even once):
items = []
for item in items:
# loop block
if #loop block was executed:
print("Big brother is watching you!")
items = []
for item in items:
# loop block
if items:
print("Big brother is watching you!")
Somewhat magical approach:
emptysentinel = item = object()
for item in items:
# loop block
if item is not emptysentinel:
print("Big brother is watching you!")
This approach provides exactly two benefits:
It avoids adding any per loop overhead (same as Jean-François Fabre's approach)
The cost of checking if any loops occurred at the end is the second cheapest conditional check in the language (the only thing cheaper is direct truthiness testing of a variable with the value True, False, or None; this is one bytecode that performs a direct C pointer comparison and stores True or False, then the same direct truthiness testing) (similar to the cost of Jean-François Fabre's approach when items is non-empty, and much cheaper when it's empty)
The downside is that it:
Requires creation of a new object to use as the sentinel (not a huge deal; plain object is very low overhead, using only 16 bytes of memory, with negligible CPU time)
Is kinda magical (using sentinels this way isn't "the obvious way to do it")
Personally, I'd stick with the flagged loop:
empty = True
for item in items:
empty = False
# loop block
if not empty:
print("Big brother is watching you!")
Yes, it has to store False over and over, but (at least on CPython 3, as long as you're in function scope), this is just a couple of incredibly cheap bytecodes:
LOAD_CONST (a C level array lookup operation to pull False out of the function's constants)
STORE_FAST (a C level array store operation to shove the just loaded value into the frame's array slot assigned to the empty variable)
As long as you're doing any real work in the loop, the cost of an extra LOAD_CONST/STORE_FAST is pretty meaningless; on my machine, it adds 12-13 nanoseconds to the cost of each loop. The overhead of something as simple as creating the emptysentinel object in the first place is around 90 ns; in simple microbenchmarks, the emptysentinel-based approach doesn't pull ahead of the empty-flag-based approach until items contains at least 25 elements (you could pull ahead as early as eight elements if you change the sentinel creation to emptysentinel = item = [] which avoids loading a global and constructing through general function call mechanisms, but using an empty list for a sentinel makes the intent even less obvious).
If the common case is that items is non-empty, a slightly modified (for style/minimally targeted try blocks) version of Jean-François Fabre's approach is the fastest:
for item in items:
pass
try:
del item
except NameError:
pass
else:
# Everything but the del should go here, not the try block, so you don't
# accidentally catch NameErrors from other sources
print("Big brother is watching you!")
It doesn't need to initialize a sentinel or a flag, and a try block that doesn't raise an exception is incredibly cheap (when items is a tuple of one element, it's a tiny bit faster than the flag approach, and gets faster as items gets larger; it's also faster than the sentinel approach when items is non-empty, but it's a matter of fixed overhead; the cost per additional item is obviously the same).
Problem is, it's much more expensive when items turns out to be empty. A microbenchmark of each approach (with the print and # loop block replaced with pass) has the costs for an empty items as:
Flag based: 64.8 ns
Sentinel based: 159 ns (87.6 if emptysentinel is created with [] instead of object())
try/except/else based: 661 ns
So the final rules for which approach to use that I'll give you are:
Use the flags based approach
Seriously, use the flags based approach; it's the fastest if items is empty a non-trivial amount of the time, and not too long when non-empty, and more importantly, it's obvious
Really? Okay then: If your non-empty inputs are largish and you really need the speed, but empty inputs are still semi-common, use the sentinel based approach; it scales as well as the try/except, and handles empty inputs without slowing down pathologically
If your inputs are almost always non-empty, you can use the try/except/else approach, which is consistently the fastest for non-empty items, at the cost of paying the overhead of a 75 long loop whenever items turns out to be empty.
Side-note: Another relatively obvious approach (equivalent to flags in obviousness to me) would be to use enumerate; this is more useful if you want to know how many items there were, not just yes/no "were there items?", but it's not terrible:
numitems = 0
for numitems, item in enumerate(items, 1):
# loop block
if numitems:
print("Big brother is watching you!")
Downside is that it's slower than all the other approaches, in every scenario but one; it's faster than try/except/else when items is empty. It has higher per loop overhead than the flags based approach, and higher fixed overhead than all other options. So it's obvious, but so is the flags based approach, and flags are faster, so just use them.
If item isn't defined anywhere else, you could just check if item has been assigned:
items = []
for item in items:
pass
try:
del item
except NameError:
print("loop wasn't executed")
else:
print("loop was executed")
so if items is empty, the loop isn't executed, so item isn't defined and you get the exception.
The del item call ensures that item doesn't exist when you execute this code a second time.
(ok not using another variable, but it's still overcomplex :))
Another solution is to check a loop variable in locals():
for item in items:
# loop block
# loop block was executed
if 'item' in locals():
print("Big brother is watching you!")
It has some limitations though:
If you had your variable defined before loop, condition will be met.
If you'll use global renaming of that variable, string representation of variable name will not satisfy the search term.

How to iterate a function n times until a certain outcome?

I need to iterate a function until a certain outcome is reached. There are actually three different outcomes that will need to stop the loop from running. However, I need the function to iterate over the naturals, so basically I don't want to limit my range. How would I go about doing this? I assume I need to make a for loop or a while loop of some sort, but I have limited Python experience. Any feedback would be greatly appreciated.
Have a look at the itertools module. Documentation here
itertools.count() will give you the naturals and will never run out. itertools.takewhile() will continue to consume a sequence until some specified function returns True.
index = 0
while True:
if end_conditions:
break
index += 1
A for loop is usually used to loop through items in an iterable, which tend to be bounded. This while True method indicates that the loop could theoretically loop forever. You can use an if statement to break the loop when you satisfy the conditions.

making python code block with loop faster

Is there a way I can implement the code block below using map or list comprehension or any other faster way, keeping it functionally the same?
def name_check(names, n_val):
lower_names = names.lower()
for item in set(n_val):
if item in lower_names:
return True
return False
Any help here is appreciated
A simple implementation would be
return any(character in names_lower for character in n_val)
A naive guess at the complexity would be O(K*2*N) where K is the number of characters in names and N is the number of characters in n_val. We need one "loop" for the call to lower*, one for the inner comprehension, and one for any. Since any is a built-in function and we're using a generator expression, I would expect it to be faster than your manual loop, but as always, profile to be sure.
To be clear, any short-circuits, so that behaviour is preserved
Notes on Your Implementation
On using a set: Your intuition to use a set to reduce the number of checks is a good one (you could add it to my form above, also), but it's a trade-off. In the case that the first element short circuits, the extra call to set is an additional N steps to produce the set expression. In the case where you wind up checking each item, it will save you some time. It depends on your expected inputs. If n_val was originally an iterable, you've lost that benefit and allocated all the memory up front. If you control the input to the function, why not just recommend it's called using lists that don't have duplicates (i.e., call set() on its input), and leave the function general?
* #Neopolitan pointed out that names_lower = names.lower() should be called out of the loop, as your original implementation called it, else it may (will?) be called repeatedly in the generator expression

Iterate a certain number of times without storing the iteration number anywhere [duplicate]

This question already has answers here:
Is it possible to implement a Python for range loop without an iterator variable?
(15 answers)
Closed 5 years ago.
I was wondering if it is possible to perform a certain number of operations without storing the loop iteration number anywhere.
For instance, let's say I want to print two "hello" messages to the console. Right now I know I can do:
for i in range(2):
print "hello"
but then the i variable is going to take the values 0 and 1 (which I don't really need). Is there a way to achieve the same thing without storing those unwanted values anywhere?
Needless to say, using a variable is not a big deal at all... I'm just curious.
The idiom (shared by quite a few other languages) for an unused variable is a single underscore _. Code analysers typically won't complain about _ being unused, and programmers will instantly know it's a shortcut for i_dont_care_wtf_you_put_here. There is no way to iterate without having an item variable - as the Zen of Python puts it, "special cases aren't special enough to break the rules".
exec 'print "hello";' * 2
should work, but I'm kind of ashamed that I thought of it.
Update: Just thought of another one:
for _ in " "*10: print "hello"
Well I think the forloop you've provided in the question is about as good as it gets, but I want to point out that unused variables that have to be assigned can be assigned to the variable named _, a convention for "discarding" the value assigned. Though the _ reference will hold the value you gave it, code linters and other developers will understand you aren't using that reference. So here's an example:
for _ in range(2):
print('Hello')
Others have addressed the inability to completely avoid an iteration variable in a for loop, but there are options to reduce the work a tiny amount. range has to generate a whole bunch of numbers after all, which involves a tiny amount of work; if you want to avoid even that, you can use itertools.repeat to just get the same (ignored) value back over and over, which involves no creation/retrieval of different objects:
from itertools import repeat
for _ in repeat(None, 200): # Runs the loop 200 times
...
This will run faster in microbenchmarks than for _ in range(200):, but if the loop body does meaningful work, it's a drop in the bucket. And unlike multiplying some anonymous sequence for your loop iterable, repeat has only a trivial setup cost, with no memory overhead dependent on length.
Although I agree completely with delnan's answer, it's not impossible:
loop = range(NUM_ITERATIONS+1)
while loop.pop():
do_stuff()
Note, however, that this will not work for an arbitrary list: If the first value in the list (the last one popped) does not evaluate to False, you will get another iteration and an exception on the next pass: IndexError: pop from empty list. Also, your list (loop) will be empty after the loop.
Just for curiosity's sake. ;)
This will print 'hello' 3 times without storing i...
[print('hello') for i in range(3)]
Sorry, but in order to iterate over anything in any language, Python and English included, an index must be stored. Be it in a variable or not. Finding a way to obscure the fact that python is internally tracking the for loop won't change the fact that it is. I'd recommend just leaving it as is.
for word in ['hello'] * 2:
print word
It's not idiomatic Python, but neither is what you're trying to do.
You can simply do
print 2*'hello'

Categories