I came across a situation where I need to implement a for loop with more than one loop control variable. Basically this is what I am trying to do
Java:
for (int i=0,j=n; i<n,j>=0; i++, j--)
do my stuff
How do I do this in Python?
for i in range(0,n), j in range(n-1,-1,-1):
do my stuff
But this doesn't work. What would be the right syntax here? Also, is there a more elegant(pythonic) construct for the use-case?
For your specific example, it doesn't look like you really need separate variables in the loop definition itself. You can just iterate over the i values and construct the j values by doing n-i:
for i in range(0, n):
j = n-i
# do stuff
In general, you can't specify multiple values in the for statement if they depend on each other. Instead, use the for to iterate over some "base" value or values from which the others can be derived.
You can specify multiple values in the for loop, but they must be drawn from a single iterable:
for i, j in zip(range(0, n), range(n, 0, -1)):
# do stuff
This takes i from the first range (0 to n-1) and j from the second (n to 1). zip creates a new iterable by componentwise pairing the elements of the iterables you give it.
The thing to remember is that Python for loops are not like loops in Java/C, which have an initialize/check end condition/update structure that repeatedly modifies some persistent loop index variable. Python for loops iterate over a "stream" of values provided by a "source" iterable, grabbing one value at a time from the source. (This is similar to foreach-type constructs in some other languages.) Whatever you want to iterate over, you need to get it into an iterable before you begin the loop. In other words, every Python for loop can be thought of as roughly analogous to something like:
for (i=iterable.getNextValue(); iterable.isNotEmpty(); i=iterable.getNextValue())
You can't have the loop initialization be different from the loop update, and you can't have those be any operation other than "get the next value", and you can't have the end condition be anything other than "is the iterable exhausted". If you want to do anything like that, you have to either do it yourself inside the loop (e.g., by assigning a secondary "loop variable" as in my example, or by checking for a custom loop exit condition and breaking out), or build it into the "source" that you're iterating over.
A lot depends on iterators you want. Here's a couple of options. What they have in common is that for... in... will traverse over lists, tuples, and anything else which supports iteration. So you could loop over a list of known values or a generator which produces an arbitrary series of values. The for... in... is the same regardless.
Here's some standard python tricks:
nested loops
for i in range(10):
for j in range (10):
print i, j
This is simple and clear, but for complex iterations it may get very deeply nested. Here range is a generator, which means it produces an iteration over a series (in this case, 10 numbers - but it could be any arbitrary stream of values))
zip
for multiple iterators you can use zip() which creates an iterable object that produces a value from each of several of iterables at the same time. You can use multple assignment inside the for loop to grab pieces out of the zip
a = [1,2,3,4]
b = ['a','b','c','d']
for number, letter in zip (a, b):
print letter, ":", number
# a : 1
# b : 2
# c : 3
# d : 4
zip will stop when the first member is exhausted:
a = [1,2]
b = ['a','b','c','d']
for number, letter in zip (a, b):
print letter, ":", number
# a : 1
# b : 2
zip also uses generators:
test = zip (range(10), range(10,20))
for item in test: print item
#(0, 10)
#(1, 11)
#(2, 12)
#(3, 13)
#(4, 14)
#(5, 15)
#(6, 16)
#(7, 17)
#(8, 18)
#(9, 19)
itertools
For more complex iterations there's a lot of great tools in the itertools module This is particularly nice for things like getting all of the products or permutations of multiple iterators. It's worth checking out but I think it's more than you need
You can create a list comprehension using more than one loop control variable:
>>> n = 10
>>> stuff = [i*j for i in range(n) for j in range(n-1,-1,-1)]
>>> stuff
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0, 54, 48, 42, 36, 30, 24, 18, 12, 6, 0, 63, 56, 49, 42, 35, 28, 21, 14, 7, 0, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0, 81, 72, 63, 54, 45, 36, 27, 18, 9, 0]
Related
I am studying rotating a list, and made a function to rotate the list left and right, but how can I write a code for how many times to rotate? if that makes a sense. I want to pass it as an argument to the functions.
table = [1, 10 ,20, 0, 59, 86, 32, 11, 9, 40]
def rotate_left():
(table.append(table.pop(0)))
return table
print(rotate_left())
def rotate_right():
(table.insert(0,table.pop()))
return table
print(rotate_right())
You can use for loop inside your functions and pass how many times you want to rotate as a argument.
table = [1, 10 ,20, 0, 59, 86, 32, 11, 9, 40]
def rotate_left(rotate_times):
for _ in range(rotate_times):
table.append(table.pop(0))
return table
>>> print(rotate_left(2))
>>> [20, 0, 59, 86, 32, 11, 9, 40, 1, 10]
def rotate_right(rotate_times):
for _ in range(rotate_times):
table.insert(0,table.pop())
return table
>>> print(rotate_right(2))
>>> [1, 10, 20, 0, 59, 86, 32, 11, 9, 40]
NOTE
In above scenario, be aware of the fact that, when you pass a list to a method and modify it inside that method, the changes are made in original list unless you make a deep copy, because list is a mutable type.
So, when you call rotate_left(2), it rotates the original list twice towards left. Then when you call rotate_right(2), it rotates the original list, which is already rotated by rotate_left(2), so we got the list as in initial order.
As, the functions are already modifying the original list, you can remove return table from the function (unless you want a new deep copy of list). And simply print the list after that like:
def rotate_left(rotate_times):
for _ in range(rotate_times):
table.append(table.pop(0))
>>> rotate_left(2)
>>> print(table)
>>> [20, 0, 59, 86, 32, 11, 9, 40, 1, 10]
You can write a 'for loop' and use 'range' to decide how many times you want to rotate. In this example 'rotate_left()' is called three times:
table = [1, 10 ,20, 0, 59, 86, 32, 11, 9, 40]
def rotate_left():
(table.append(table.pop(0)))
return table
def rotate_right():
(table.insert(0,table.pop()))
return table
for i in range(3):
print(rotate_left())
You can write a 'loop' and use 'range' to decide how many times you want to loop. In this example, there is a program that asks the user in which direction and how many times to turn and calculates.
def rotate_left(count,table):
for i in range (count):
(table.append(table.pop(0)))
return table
def rotate_right(count,table):
for i in range (count):
(table.insert(0,table.pop()))
return table
def main():
table = [1, 10 ,20, 0, 59, 86, 32, 11, 9, 40]
isSelect = True
while(isSelect):
rotate = int(input("1- Rotate Left\n2- Rotate Right\n: "))
count = int(input("\nHow many times to rotate ?\n: "))
if((rotate == 1 or rotate == 2) and count > 0):
isSelect = False
if (rotate == 1):
print(rotate_left(count,table))
elif (rotate == 2):
print(rotate_right(count,table))
else:
print("\nInvalid Parameter. Please choose again.\n")
main()
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
Is using one line loops, even nested loops always a good practice in Python? I see a lot of people just love "one-liners", but to me they're hard to read sometimes, especially if we're talking about nested loops.
Moreover most of nested loops I've seen so far exceed the recommended 79 characters per line.
So I'd like to know if "one-liners" offer anything extra apart being compact? Do they use less memory maybe?
Yes, they may be easily faster, since more code may be run in C (in explicit loop all of immediate steps has to be available to interpreter, in one-liner list comprehension it does not have to). There is also overhead of .append method call, method look-up etc. In list comprehension all of that is avoided:
import timeit
def f1():
result = []
for x in range(100):
for y in range(100):
result.append(x * y)
return result
def f2():
return [x * y for y in range(100) for x in range(100)]
print('loop: ', timeit.timeit(f1, number=1000))
print('oneliner:', timeit.timeit(f2, number=1000))
Results (Python 3):
loop: 1.2545137699926272
oneliner: 0.6745600730064325
Depends on the one-liner, some can be much more efficient and very readable.
Take the case of list comprehension. Lets say you want to take all numbers from 1 to 10 and output a list of their product multiplied by 2.
Input: [1,2,3,...,9,10]
Output: [2,4,6,...,18,20]
You can do it in a for loop like so:
output = []
for i in range(1, 11):
output.append(i*2)
Or you can just use list comprehension:
[i*2 for i in range(1,11)]
You'll see the list comprehension is MUCH faster, and also quite readable.
The same can be said for dict comprehension, set comprehension and generator expressions. Also the use of map() and filter() are highly encouraged as long as it's understandable.
For discussion... they all do the same.
'a' a list comprehension... the smoking one-liner...
'b' same thing, but you can provide annotation within a list comprehension... it is still technically a one-liner
'c' the conventional approach.
If speed is a concern, I am less worried about the speed unless, I perform the same task day in day out and it takes a really long time to perform. Comparing speed when you are talking micro or nanoseconds may be of academic interest, but it will not impact the vast majority of users.
I always vote for clarity of expression over compactness of code.
a = [i**2 for i in range(20) if i > 5 and i < 10]
b = [i**2 # do this
for i in range(20) # using these
if (i > 5) and # where this and
(i < 10) # this is good
]
c = []
for i in range(20):
if (i > 5) and (i < 10):
c.append(i**2)
EDIT
The example given of producing the product of a range of numbers, is a good indicator, that the oneliner need not be the issue, but the method used to obtain the result. I will illustrate with determining the product using only 10 values... try it with 100 if you like. I will do it in full format, but could reduce everything to a oneliner if need (import excluded).
>>> import numpy as np
>>> a = np.arange(10)
>>> b = np.arange(10).reshape(10,1)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> b
array([[0],
[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9]])
>>> a*b
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18],
[ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
[ 0, 4, 8, 12, 16, 20, 24, 28, 32, 36],
[ 0, 5, 10, 15, 20, 25, 30, 35, 40, 45],
[ 0, 6, 12, 18, 24, 30, 36, 42, 48, 54],
[ 0, 7, 14, 21, 28, 35, 42, 49, 56, 63],
[ 0, 8, 16, 24, 32, 40, 48, 56, 64, 72],
[ 0, 9, 18, 27, 36, 45, 54, 63, 72, 81]])
or as a one-liner
>>> out = np.arange(10) * np.arange(10).reshape(10,1)
and of course there are built-in functions to do this without the full expression I have shown.
The point being made, speed isn't everything. Clarity of code AND choosing the right tools to do the job should be considered first.
Yes. This is good practice.
Usually experienced programmers use anonymous functions (see, lambda: http://www.secnetix.de/olli/Python/lambda_functions.hawk) in one-line loops and it gives better performance.
I'm trying to create a list of all the prime numbers less than or equal to a given number. I did that successfully using for loops. I was trying to achieve the same using list comprehension using python. But my output has some unexpected values.
Here is my code..
pr=[2]
pr+=[i for i in xrange(3,num+1) if not [x for x in pr if i%x==0]]
where num is the number I had taken as input from user.
The output of the above code for
num=20 is this: [2, 3, 5, 7, 9, 11, 13, 15, 17, 19]
I'm puzzled as to why 9 and 15 are there in the output. What am I doing wrong here?
It simply doesn’t work that way. List comprehensions are evaluated separately, so you can imagine it like this:
pr = [2]
tmp = [i for i in xrange(3,num+1) if not [x for x in pr if i%x==0]]
pr += tmp
By the time tmp is evaluated, pr only contains 2, so you only ever check if a number is divisible by 2 (i.e. if it’s even). That’s why you get all uneven numbers.
You simply can’t solve this nicely† using list comprehensions.
† Not nicely, but ugly and in a very hackish way, by abusing that you can call functions inside a list comprehension:
pr = [2]
[pr.append(i) for i in xrange(3,num+1) if not [x for x in pr if i%x==0]]
print(pr) # [2, 3, 5, 7, 11, 13, 17, 19]
This abuses list comprehensions and basically collects a None value for each prime number you add to pr. So it’s essentially like your normal for loop except that we unnecessarily collect None values in a list… so you should rather allow yourself to use a line break and just use a normal loop.
Your list pr doesn't update until after your entire list comprehension is done. This means your list only contains 2, so every number dividable by 2 is not in the list (as you can see). You should update the list whenever you found a new prime number.
This is because the pr += [...] is evaluated approximately as this:
pr = [2]
tmp = [i for i in xrange(3,num+1) if not [x for x in pr if i%x==0]]
pr.extend(tmp)
So while tmp is generated, contents of pr remains the same ([2]).
I would go with function like this:
>>> import itertools
>>> def primes():
... results = []
... for i in itertools.count(2):
... if all(i%x != 0 for x in results):
... results.append(i)
... yield i
...
# And then you can fetch first 10 primes
>>> list(itertools.islice(primes(), 10))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
# Or get primes smaller than X
>>> list(itertools.takewhile(lambda x: x < 50, primes()))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
Note, that using all is more efficient than creating array and testing whether it's empty.
I have seen this question been asked before but I'm looking for an answer with a twist:
Consider I have a range like 1-100 and i want to generate a list, with a specific stepsize like: numbers(1,100,5). This would return [1,25,50,75,100]. However, I would like it to return [1,100,50,25,75] or [1,100,50,75,25] Another example would be numbers(1,10,10) which would give me something similar to [1,10,5,2,7,3,8,4,9].
Is this even possible to do? The reason for this would be to be able to render image sequences without going from frame 1 to frame 2 to frame 3 and so forth. Instead I want to render the first frame, the last frame, the middle frame, the middle of the middle frame until all frames are accounted for.
Your question is kind of underspecified, but this should help you get started.
def numbers(first, last, count):
nums = [first, last]
width = last - first
while len(nums) < count:
width /= 2
stepper = first
while stepper < last:
rounded = round(stepper)
if rounded not in nums:
nums.append(rounded)
if len(nums) == count:
break
stepper += width
return nums
Then:
>>> numbers(0, 100, 5)
[0, 100, 50, 25, 75]
>>> numbers(0, 10, 10)
[0, 10, 5, 2, 8, 1, 4, 6, 9, 3, 7]
>>> numbers(0, 50, 50)
[0, 50, 25, 12, 38, 6, 19, 31, 44, 3, 9, 16, 22, 28, 34, 41, 47, 2, 5, 8, 11, 14, 17, 20, 23, 27, 30, 33, 36, 39, 42, 45, 48, 1, 4, 7, 10, 13, 15, 18, 21, 24, 26, 29, 32, 35, 37, 40, 43, 46]
The basic algorithm is as follows:
Start with a list of nums containing the two endpoints
Initialize width to the distance between the two endpoints
Then, loop:
Halve width
Step through first, first+width, first+2*width, ..., last-width, last, and add whichever among those are not already in nums to nums (so, for numbers(0, 100, 5), the first loop iteration will try 0, 50, and 100, and only add 50 since that wasn't already present; the second iteration will try 0, 25, 50, 75, and 100, and only add 25 and 75).
If we have enough numbers in nums, we're done
Return nums
Ok, so your desired frames are kind of wierd, especially as the elements in the first example aren't evenly ranged, eg. 100-75=25, 75-50=25, 50-25=25 , 25-1=24.
But, if we assume that you always want the start and end value in the frame and want the evenly spaced values pegged against the maximum value, we can do this:
def numbers(start,stop,step=1):
frame = [start]+range(y,stop,(start-stop)/(step-1))
return frame
The random module includes a shuffle() method that takes an array and shuffles it in-place.
Which means the function becomes:
from random import shuffle
def numbers(start,stop,step=1):
frame = [start]+range(y,stop,(start-stop)/(step-1))
shuffle(frame)
return frame
Giving us the following test runs:
>>> numbers(1,100,5)
[100, 50, 75, 1, 25]
>>> numbers(1,10,10)
[1, 3, 10, 9, 6, 5, 8, 4, 7, 2]
Original (but wrong) answer
The random module includes a shuffle() method that takes an array and shuffles it in-place.
For example:
from random import shuffle
def numbers(start,stop,step=1):
frame = range(start,stop,step)
shuffle(frame)
return frame
Then calling this function we get:
>>> numbers(1,100,25)
[1, 51, 26, 76]
>>> numbers(1,100,25)
[76, 26, 1, 51]
Note, that as per the range() function, the step value is repeatedly added to start such that the final array is of the form [start, start+1*step, start+2*step ... start + n*step], where start+n*step is less than stop while start+(n+1)*step is greater than stop.
I have a string of numbers that I want to read from a file and parse into sub-sections, with lengths of the subsections based on numbers within the string. The first number in the string is the length of the first sub-section. So for example, if I have a string of data as follows:
4, 11, 22, 33, 3, 44, 55, 5, 44, 55, 66, 77
I want to divide up as follows:
first subsection is length 4, so, 4, 11, 22, 33
second subsection is length 3, so 3, 44, 55
third subsection is length 5, so 5, 44, 55, 66, 77
I tried using variables in slice, so that I could increment the start/stop values as I march through the data, but it doesn't take vars. I worked out a way to delete each subsection as I go so that the first value will always be the length of the next subsection, but it seems sort of clunky.
I'd appreciate any suggestions - thx
You can do something like:
your_list = [4, 11, 22, 33, 3, 44, 55, 5, 44, 55, 66, 77]
subsec = []
it = iter(your_list)
for n in it:
subsec.append([n] + map(lambda x: next(it), range(int(n-1))))
This way you only loop once over your list.
or
for n in it:
subsec.append([n] + [next(it) for _ in range(int(n-1))])
When dealing with more complex logic, I prefer to use regular loops.
In this case I would go with a while loop, running until the list is empty, and removing the elements already processed. If the sections are wrong (i.e. the last section goes beyond the size of the string), the assert will tell you.
sequence = [4, 11, 22, 33, 3, 44, 55, 5, 44, 55, 66, 77]
sections = []
while sequence:
section_size = sequence[0]
assert len(sequence) >= section_size
sections.append(sequence[:section_size])
sequence = sequence[section_size:]
print sections
This splits the sections and save them in a list called sections, with the size as first element, like in your examples.
Edit: added error checking.
Just thought I'd throw this out there. Very similar to both BoppreH's solution, but it avoids the overhead of creating n additional lists by iterating over indices:
def generateSlices(seq):
i = 0
while i < len(seq):
n = x[i]
yield x[i:i + n]
i += n
You can check for errors after generating a list of sublists by doing:
mySubLists = [[5, 23, 33, 44, 2], [10]]
all(len(x) == x[0] for x in mySubLists)
Incidentally, why is your data structured in this strange way? It seems error-prone.