"List comprehensions" directly in for loop line - python

Is it possible to add a "list comprehensions" directly in for loop line? Current code:
cubes = [i**3 for i in range(5)]
for value in cubes:
print(value)
I want to write something like (this obviously doesn't work):
for value in cubes = [i**3 for i in range(5)]:
print(value)
or
cubes = [ ]
for value in cubes = [i**3 for i in range(5)]:
print(value)

You can simply just put the expression in the loop as is:
for value in [i**3 for i in range(5)]:
print(value)
output:
0
1
8
27
64

If you want both the list cubes for later in the code and want to print, you can do everything in the list comprehension by exploiting the fact that print returns None:
cubes = [print(i**3) or i**3 for i in range(5)]
Is this recommended? NO! It's a clear breach of "The Zen of Python" (PEP20).

If n for your range is sufficiently large, using a list comprehension for your loop will hang before it even begins. You might want to abstract away the loop filtering by using yield:
def create_numbers(n):
for x in range(n):
yield x**3
for i in create_numbers(5):
print(i)
yield will give you those values one at a time, only calculating the next value when next() is invoked by the loop. This way your loop looks nicer and doesn't iterate over that list 2*n times

I strongly suggest you keep the code you have now:
cubes = [i**3 for i in range(5)]
for value in cubes:
print(value)
It's clear what it does, and it is correct. There's little reason to eliminate a single line of code at the risk of changing that.
That said, Python 3.8 will introduce assignment expressions, which can (if I am reading PEP-572 correctly; no similar examples appear there) be used to eliminate that line of code:
for value in cubes := [i**3 for i in range(5)]:
print(value)
Time will tell if this will be considered good, or at least acceptable, style.

Related

How to solve the error list index out of range ? (On python using pygame and sys) [duplicate]

I have written a simple python program
l=[1,2,3,0,0,1]
for i in range(0,len(l)):
if l[i]==0:
l.pop(i)
This gives me error 'list index out of range' on line if l[i]==0:
After debugging I could figure out that i is getting incremented and list is getting reduced.
However, I have loop termination condition i < len(l). Then why I am getting such error?
You are reducing the length of your list l as you iterate over it, so as you approach the end of your indices in the range statement, some of those indices are no longer valid.
It looks like what you want to do is:
l = [x for x in l if x != 0]
which will return a copy of l without any of the elements that were zero (that operation is called a list comprehension, by the way). You could even shorten that last part to just if x, since non-zero numbers evaluate to True.
There is no such thing as a loop termination condition of i < len(l), in the way you've written the code, because len(l) is precalculated before the loop, not re-evaluated on each iteration. You could write it in such a way, however:
i = 0
while i < len(l):
if l[i] == 0:
l.pop(i)
else:
i += 1
The expression len(l) is evaluated only one time, at the moment the range() builtin is evaluated. The range object constructed at that time does not change; it can't possibly know anything about the object l.
P.S. l is a lousy name for a value! It looks like the numeral 1, or the capital letter I.
You're changing the size of the list while iterating over it, which is probably not what you want and is the cause of your error.
Edit: As others have answered and commented, list comprehensions are better as a first choice and especially so in response to this question. I offered this as an alternative for that reason, and while not the best answer, it still solves the problem.
So on that note, you could also use filter, which allows you to call a function to evaluate the items in the list you don't want.
Example:
>>> l = [1,2,3,0,0,1]
>>> filter(lambda x: x > 0, l)
[1, 2, 3]
Live and learn. Simple is better, except when you need things to be complex.
What Mark Rushakoff said is true, but if you iterate in the opposite direction, it is possible to remove elements from the list in the for-loop as well. E.g.,
x = [1,2,3,0,0,1]
for i in range(len(x)-1, -1, -1):
if x[i] == 0:
x.pop(i)
It's like a tall building that falls from top to bottom: even if it is in the middle of collapse, you still can "enter" into it and visit yet-to-be-collapsed floors.
I think the best way to solve this problem is:
l = [1, 2, 3, 0, 0, 1]
while 0 in l:
l.remove(0)
Instead of iterating over list I remove 0 until there aren't any 0 in list
List comprehension will lead you to a solution.
But the right way to copy a object in python is using python module copy - Shallow and deep copy operations.
l=[1,2,3,0,0,1]
for i in range(0,len(l)):
if l[i]==0:
l.pop(i)
If instead of this,
import copy
l=[1,2,3,0,0,1]
duplicate_l = copy.copy(l)
for i in range(0,len(l)):
if l[i]==0:
m.remove(i)
l = m
Then, your own code would have worked.
But for optimization, list comprehension is a good solution.
The problem was that you attempted to modify the list you were referencing within the loop that used the list len(). When you remove the item from the list, then the new len() is calculated on the next loop.
For example, after the first run, when you removed (i) using l.pop(i), that happened successfully but on the next loop the length of the list has changed so all index numbers have been shifted. To a certain point the loop attempts to run over a shorted list throwing the error.
Doing this outside the loop works, however it would be better to build and new list by first declaring and empty list before the loop, and later within the loop append everything you want to keep to the new list.
For those of you who may have come to the same problem.
I am using python 3.3.5. The above solution of using while loop did not work for me. Even if i put print (i) after len(l) it gave me an error. I ran the same code in command line (shell)[ window that pops up when we run a function] it runs without error. What i did was calculated len(l) outside the function in main program and passed the length as a parameter. It worked. Python is weird sometimes.
I think most solutions talk here about List Comprehension, but if you'd like to perform in place deletion and keep the space complexity to O(1); The solution is:
i = 0
for j in range(len(arr)):
if (arr[j] != 0):
arr[i] = arr[j]
i +=1
arr = arr[:i]
x=[]
x = [int(i) for i in input().split()]
i = 0
while i < len(x):
print(x[i])
if(x[i]%5)==0:
del x[i]
else:
i += 1
print(*x)
Code:
while True:
n += 1
try:
DATA[n]['message']['text']
except:
key = DATA[n-1]['message']['text']
break
Console :
Traceback (most recent call last):
File "botnet.py", line 82, in <module>
key =DATA[n-1]['message']['text']
IndexError: list index out of range
I recently had a similar problem and I found that I need to decrease the list index by one.
So instead of:
if l[i]==0:
You can try:
if l[i-1]==0:
Because the list indices start at 0 and your range will go just one above that.

For Loop to determine lowest value of a list in python [duplicate]

This question already has answers here:
Can anyone explain me Python for loop (or iteration) algorithm
(4 answers)
Closed 2 years ago.
In the below, I have several questions. Firstly, why do we not define the starting value of i? In WHILE loops I came across this was the case. Secondly, I am finding it difficult to understand the "sequence" of what is happening. I understand that m = L[0] = 1 but the subsequent steps are not clear to me.
L=[1,2,4,-5,7,9,3,2]
m = L[0]
for i in L:
if i<m:
m = i
print(m)
For loops in python are really "for each" loops. Read it as "for each value 'i' in list 'L', do...".
i is not an index, it is the actual element in the list L.
So we set the initial minimum to just the first element of the list, and we say, for each element in our list, if there is a value smaller than our current minimum, we'll set our minimum to that element.
You don't want to define the starting value of i to make the program more variable to other cases. M is going to be the lowest value because that is what we are printing out. So if i is lower than m we want i to be m. The for loop goes through every number in the list.
L=[1,2,4,-5,7,9,3,2]
m = L[0] # start with m as the first value of your list which is 1.
for i in L: # for i in L means for i being consecutively 1 then 2 then 4 then -5 etc...
if i<m: # if i is less than m the starting point, give the value of i to m
# example if m = 1 and i =-3 after this condition m will take the value -3
m = i
print(m) # when you finish looping over all the examples print my m value
I would suggest writing the algorithm on a paper and you will see how easy it is.
m=1
loop
i=1
i<1? No.
go back to loop
i=2
i<1? No.
go back to loop
i=4
i<1? No.
go back to loop
i=-5
i<1? YES.
m = -5
go back to loop
.
.
.
The for loop proceeds via a form of hypothesis testing. First, we establish a loop invariant: something that remains true before and after each iteration. In this case, it is that m is the smallest value in the list that we have seen so far.
When we initialize m = L[0], it is true: having looked only at L[0], m is the smallest value we've seen so far.
Inside the loop, i takes on each value in the list exactly once. We compare it to m, and if i is indeed smaller, we use it as the new value of m. So after each iteration, m remains the smallest value seen so far.
Once the loop completes, having set i to each element in turn, we can conclude that m is not only the smallest value in L that we've seen so far, but it is the smallest value in L, period, because we have looked at every value in L.
The for loop is equivalent to the following while loop:
_index = 0
while True:
try:
i = L[_index]
except IndexError:
break
if i < m:
m = i
_index += 1
As you can see, we initialize _index to 0, and increment it explicitly at the end of each iteration. The for loop essentially keeps track of _index for us. i, on the other hand, is not initialized so much as it is derived from the current values of L and _index.
More generally, for loops don't manage repeated indexing operations, but are based on the iterator protocol. A for loop like
for i in L:
...
is roughly equivalent to
_itr = iter(L)
while True:
try:
i = next(_itr)
except StopIteration:
break
...
There is an easier way to do it without using a for loop so you can run your whatever code you want to run using the min() function for example in here:
L=[1,2,4,-5,7,9,3,2]
print(min(L)
I would recommend using something like this because its easier faster and runs smoother than the code in your question. The code in your question first runs a for loop that gets all the values out of the list and store them as i and runs threw them then it goes to the if statement which checks if the value which is i in this example is smaller than m which has the value of 1 then rewrites the variable m which was the value 1 to the value that is smaller than i which is -5 and as you see there this is a lot of function to run throw and that makes your code slower if the code consist of many lines.(Tip: always try using as minimum for loops or if statements as you can.)
your example:
L=[1,2,4,-5,7,9,3,2]
m = L[0]
for i in L:
if i<m:
m = i
print(m)

Python - I dont know how to min works in this code

My teacher gave me this code:
def n_o_c(Q,v):
M=[None]*(Q+1)
m={}
M[0]=0
for q in xrange(1,Q+1):
M[q]=min(M[q-a]+1 for a in v if q-a>=0)
return M[Q],m
print n_o_c(18,[1,2,5])
1st I explain the script, It's a supposed coin machine and I have to know how much coins I need for pay Q quantity having v coins(we have to do less coins as posible IE for 18 3x5 coin 1x2 coin and 1x1 coin)
I dont understand what does that M[q] line, I've tried to print M and the result I had was every number from 1 to 18 how many coins it needs to do that number.
M=[0,1,1,2,2,3,2,2,3,3,2,3,3,4,4,3,4,4,5]
q=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
Can someone explain me how that min works?
I've already done it (I know its not the good way to solve the exercise but I dont know a better way for). Solved:
def n_o_c(Q,v):
M=[None]*(Q+1)
m={}
M[0]=0
for q in xrange(1,Q+1):
M[q]=min(M[q-a]+1 for a in v if q-a>=0)
monedas=0
total=Q
m=[] # did this to change dictionary to array
while(monedas<M[Q]):
for a in v[::-1]:
if total-a >= 0:
total = total-a
monedas = monedas +1
m.append(a)
break #I forget this break
return M[Q],m
print n_o_c(18,[1,2,5])
The min function is the easy part:
Return the smallest item in an iterable
The tricky bit is, what's with that iterable?
M[q]=min(M[q-a]+1 for a in v if q-a>=0)
That (M[q-a]+1 for a in v if q-a>=0) is called a generator expression; more generally, it's a type of comprehension.
Start at List Comprehensions in the official tutorial to learn how comprehensions in general work, then Iterators and the following two sections (Generators and Generator Expressions) to learn how generator expressions are different.*
But I can summarize it here, at least enough to get you started.
First, a list comprehension:
[M[q-a]+1 for a in v if q-a>=0]
What this means is that you want to build a list, exactly as if you'd unrolled it into a loop like this:
value = []
for a in v:
if q-a>=0:
value.append(M[q-a]+1)
M[q] = min(value)
Or, more intuitively, try reading it aloud: a list of every M[q-a]+1 for each a in v if q-a>=0 makes sense as an English sentence, and means exactly the same thing as the Python. (If you have a math background, you might want to think of it in terms of set displays instead, but I'll assume you don't.)
A generator expression does the same thing, except that instead of building a list, it creates the values on demand as you iterate over it. You can think of this as sort of a magic list that doesn't waste memory or time for now. To spell it in Python, just turn the square brackets [] into parentheses () (which you can leave off in this case, because there are already parentheses from the min call). To read it aloud, just leave of the "a list of" part.
* And once you get that, if you want to learn more, take a look at the itertools module, read Generator Tricks for System Programmers by David Beazley and google for Greg Ewing's presentations on generators.
Why is m not being used in your teacher's code ?
Here is a solution:
Sort the v list and divide by it by each dimension to get num coins and continue with change remaining.
def n_o_c(Q,v):
m = {}
for dimension in sorted(v, reverse= True):
num_coins = int(Q/dimension)
## if Q is not divisible by the dimension, go to the next dimension
if num_coins != 0:
## if Q is divisible, store num_coins in the dictionary
m[dimension] = num_coins
## Get change remaining by modulo function and store it as remaining Q that needs to be broken down
Q = Q % dimension
return m
print n_o_c(18,[1,2,5])
Should print:
{1: 1, 2: 1, 5: 3}

python list generation/saving bug

I am trying to make program that prints all the possible combinations for a to zzz. I tried to add a save state feature, and it works fine but there is this bug.
Let's say I interrupted the program when it printed something like e. When I execute the program again, it works fine until z but after z instead of printing aa it prints ba and continues from ba. This happens right after it prints zz too. it prints baa instead of aaa. How can I fix this?
Here is what I did so far:
import pickle,os,time
alphabet="abcdefghijklmnopqrstuvwxyz"
try:
if os.path.isfile("save.pickle")==True:
with open("save.pickle","rb") as f:
tryn=pickle.load(f)
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
b=a[tryn:]
for k in b:
print(k)
time.sleep(0.01)
tryn+=1
else:
tryn=0
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
for k in a:
print(k)
tryn+=1
time.sleep(0.01)
except KeyboardInterrupt:
with open("save.pickle","wb") as f:
pickle.dump(tryn,f)
If you're using python2, or python3 as the tag suggests, this exists in the standard library already. See itertools, product py2, and product py3, for a simple way to solve this problem.
for i in range(3):
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
b=a[tryn:]
Here's your bug. You skip the first tryn strings of every length, rather than just the first tryn strings. This would be easier to recognize in the output if it weren't for the following:
for k in b:
print(k)
time.sleep(0.01)
tryn+=1
You modify tryn, the number of things you're skipping. When you print out length-2 strings, you skip a number of them equal to the number of length-1 strings. When you print out length-3 strings, you skip a number of them equal to the number of length-2 strings. If tryn were bigger than the number of length-1 strings, you would skip even more.
your problem is almost certainly here:
a=[x for x in alphabet]
for j in range(i):
a=[x+i for x in alphabet for i in a]
Perhaps you shouldn't assign the in-loop value to a, but instead use a different name? Otherwise, you are changing what you use every time through the loop....
Edit: More detail. So, technically user2357112's answer is more correct, but I'm amending mine. The initial answer was just from a quick reading, so the other answer is close to the original intent. But, the original version is inefficient (for more reasons than not using product :), since you are generating the inner loops more than once. So let's walk through why this is a bad idea, as an educational exercise:
Initial algorithm:
for i in range(n):
assign a to alphabet
for j in range(i):
i times, we rewrite a to be all combinations of the current set against the alphabet.
Note that for this algorithm, to generate the length(n) product, we have to generate all previous products length(n-1), length(n-2), ..., length(1). But you aren't saving those.
You'd be better off doing something like this:
sum_list = alphabet[:]
#get a copy
product_list = alphabet[:]
#Are we starting at 0, or 1? In any case, skip the first, since we preloaded it
for i in range(1, n):
# Your existing list comprehension was equivalent here, and could still be used
# it MIGHT be faster to do '%s%s'%(x,y) instead of x+y... but maybe not
# with these short strings
# This comprehension takes the result of the last iteration, and makes the next iteration
product_list = [x+y for x,y in product(product_list, alphabet)]
# So product list is JUST the list for range (n) - i.e. if we are on loop 2, this
# is aaa...zzz. But you want all lengths together. So, as you go, add these
# sublists to a main list.
sum_list.extend(product_list)
Overall, you are doing a lot less work.
Couple other things:
You're using i as a loop variable, then re-using it in the loop comprehension. This is conflicting, and probably not working the way you'd expect.
If this is to learn how to write save/restore type apps... it's not a good one. Note that the restore function is re-calculating every value to be able to get back where it left off - if you could rewrite this algorithm to write more information out to the file (such as the current value of product_list) and make it more generator-like, then it will actually work more like a real-world example.
Here is how I would suggest solving this problem in Python. I didn't implement the save state feature; this sequence is not a really long one and your computer should be able to produce this sequence pretty fast, so I don't think it is worth the effort to try to make it cleanly interruptable.
import itertools as it
def seq(alphabet, length):
for c in range(1, length+1):
for p in it.product(alphabet, repeat=c):
yield ''.join(p)
alphabet="abcdefghijklmnopqrstuvwxyz"
for x in seq(alphabet, 3):
print(x)
If you really wanted to, you could make a one-liner using itertools. I think this is too hard to read and understand; I prefer the above version. But this does work and will be somewhat faster, due to the use of itertools.chain and itertools.imap() rather than a Python for loops.
import itertools as it
def seq(alphabet, length):
return it.imap(''.join, it.chain.from_iterable(it.product(alphabet, repeat=c) for c in range(1, length+1)))
alphabet="abcdefghijklmnopqrstuvwxyz"
for x in seq(alphabet, 3):
print(x)
In Python 3.x you could just use map() rather than itertools.imap().

Why doesnt this sort function work for Python

Please tell me why this sort function for Python isnt working :)
def sort(list):
if len(list)==0:
return list
elif len(list)==1:
return list
else:
for b in range(1,len(list)):
if list[b-1]>list[b]:
print (list[b-1])
hold = list[b-1]
list[b-1]=list[b]
list[b] = hold
a = [1,2,13,131,1,3,4]
print (sort(a))
It looks like you're attempting to implement a neighbor-sort algorithm. You need to repeat the loop N times. Since you only loop through the array once, you end up with the largest element being in its place (i.e., in the last index), but the rest is left unsorted.
You could debug your algorithm on your own, using pdb.
Or, you could use python's built-in sorting.
Lets take a look at you code. Sort is a built in Python function (at least I believe it is the same for both 2.7 and 3.X) So when you are making your own functions try to stay away from name that function with inbuilt functions unless you are going to override them (Which is a whole different topic.) This idea also applies to the parameter that you used. list is a type in the python language AKA you will not be able to use that variable name. Now for some work on your code after you change all the variables and etc...
When you are going through your function you only will swap is the 2 selected elements are next to each other when needed. This will not work with all list combinations. You have to be able to check that the current i that you are at is in the correct place. So if the end element is the lowest in the List then you have to have it swap all the way to the front of the list. There are many ways of sorting (ie. Quick sort, MergeSort,Bubble Sort) and this isnt the best way... :) Here is some help:
def sortThis(L):
if (len(L) == 0 or len(L) == 1):
return list
else:
for i in range(len(L)):
value = L[i]
j = i - 1
while (j >= 0) and (L[j] > value):
L[j+1] = L[j]
j -= 1
L[j+1] = value
a = [1,2,13,131,1,3,4]
sortThis(a)
print a
Take a look at this for more sorting Fun: QuickSort MergeSort
If it works, it would be the best sorting algotithm in the world (O(n)). Your algorithm only puts the greatest element at the end of the list. you have to apply recursively your function to list[:-1].
You should not use python reserved words

Categories

Resources