This question already has an answer here:
Why a generator object is obtained instead of a list
(1 answer)
Closed 3 years ago.
Not sure if this has been asked before but I couldn't find a proper, clear explanation.
I had a concern about something related to python syntax.
While practicing some python, I Intuitively assumed this would print all the elements of the list; list1.
But it doesn't seem to do so, why would that be?
I could obviously print it in many other ways; but I fail to understand the inherent python logic at play here.
list1 = [1,2,3,4]
print(list1[i] for i in range(len(list1)))
I expected the output to be '[1, 2, 3, 4]', but it instead prints a generator object.
You need to surround list1[i] for i in range(len(list)) with [] to indicate that it's a list. Although list1 is a list, you are trying to use a generator expression to print it out, which will return a generator object (type of iterable similar to a list.) Without specifying you want to convert the generator to a list, it won't print a list. (A generator expression converted to a list is called list comprehension.)
Even if you did do this, it would still print it [1, 2, 3, 4] rather than 1 2 3 4. You need to do [print(list1[i], end=" ") for i in range(len(list1)))] for that to work. There are far better ways of doing this: see donkopotamus's answer.
The expression (list1[i] for i in range(len(list))) defines a generator object. So that is what is printed.
If you wish to print a list, then make it a list comprehension rather than a generator, and print that:
print( [list1[i] for i in range(len(list1))] )
Alternatively, you could force evaluation of the generator into a tuple (or list or set), by passing the generator to the appropriate type using eg
print(tuple(list1[i] for i in range(len(list1))))
In order to get the specific output you intended (space separated) of 1 2 3 4 you could use str.join in the following way:
>>> list1 = [1, 2, 3, 4]
>>> print(" ".join(list1[i] for i in range(len(list1))))
1 2 3 4
or unpack the list into print (this will not work in python 2, as in python 2 print is not a function)
>>> print(*(list1[i] for i in range(len(list1))))
1 2 3 4
(list1[i] for i in range(len(list1)))
is indeed a generator object, equivalent to simply
(x for x in list1)
You're passing that generator to print as a single argument, so print simply prints it: it does not extract the elements from it.
Alternatively, you can unpack it as you pass it to print:
print(*(list1[i] for i in range(len(list1))))
This will pass each element of the generated sequence to print as a separate argument, so they should each get printed.
If you simply meant to print your list, any of the following would have worked:
print(list1)
print([list1[i] for i in range(len(list1))])
print([x for x in list1])
The use of square brackets makes a list comprehension rather than a generator expression.
There is something called list comprehension and generator expression in python. They are awesome tools, you can find more info by googling. Here is a link.
Basically, you can make a list on the fly in python.
list1 = [1,2,3,4]
squared_list = [i*i for i in list1]
would return a list with all the items squared. However, is we say
squared_gen_list = (i*i for i in list1)
this returns what is known as a generator object. That is what is happening in your case, as you can see from the syntax and so you are just printing that out. Hope that clears up the confusion.
Related
Playing around with python3 REPL and noticed the following:
Why are print( [zip([1,2,3], [3,1,4])] ) and print( list(zip([1,2,3], [3,1,4])) ) different?
The first returns [<zip object at 0xblah>] and the second returns [(1,3), (2,1), (3,4)].
Trying to understand why the list comprehension in the first statement doesn’t give me the result that the list() constructor gives - I think I'm confused about the difference between list comprehension and list() and would appreciate insight into what's happening under the hood.
Searching gives me this question on lists and tuples which doesn't answer my question.
Edit: A suggested question on The zip() function in Python 3 is very helpful background, but does not address the confusion in my question about the difference between a list comprehension and a list literal, so i prefer the submitted answer below as more complete.
The first statement is not a list comprehension, a list comprehension would give you the same result. It is just a list literal containing a zip object:
This would be a list comprehension:
[value for value in zip([1,2,3], [3,1,4])]
The above will print the same as list(zip([1, 2, 3], [3, 1, 4])).
In general, [something] means: A list with one element: something.
On the other hand, list(something) means: Iterate over the values in something, and make a list from the result. You can see the difference for example by putting primitive objects inside it, like a number:
>>> [2]
[2]
>>> list(2)
TypeError: 'int' object is not iterable
I am trying to create a generator function that loops over an iterable sequence while eliminating duplicates and then returns each result in order one at a time (not as a set or list), but I am having difficulty getting it to work. I have found similar questions here, but the responses pretty uniformly result in a list being produced.
I would like the output to be something like:
>>> next(i)
2
>>> next(i)
8
>>> next(i)
4....
I was able to write it as a regular function that produces a list:
def unique(series):
new_series = []
for i in series:
if i not in new_series:
new_series.append(i)
return new_series
series = ([2,8,4,5,5,6,6,6,2,1])
print(unique(series))
I then tried rewriting it as a generator function by eliminating the lines that create a blank list and that append to that list, and then using "yield" instead of "return"; but I’m not getting it to work:
def unique(series):
for i in series:
if i not in new_series:
yield new_series
I don't know if I'm leaving something out or putting too much in. Thank you for any assistance.
Well, to put it simply, you need something to "remember" the values you find. In your first function you were using the new list itself, but in the second one you don't have it, so it fails. You can use a set() for this purpose.
def unique(series):
seen = set()
for i in series:
if i not in seen:
seen.add(i)
yield i
Also, yield should "yield" a single value at once, not the entire new list.
To print out the elements, you'll have to iterate on the generator. Simply doing print(unique([1, 2, 3])) will print the resulting generator object.
>>> print(unique([1, 1, 2, 3]))
<generator object unique at 0x1023bda98>
>>> print(*unique([1, 1, 2, 3]))
1 2 3
>>> for x in unique([1, 1, 2, 3]):
print(x)
1
2
3
Note: * in the second example is the iterable unpack operator.
Try this:
def unique(series):
new_se = []
for i in series:
if i not in new_se:
new_se.append(i)
new_se = list(dict.fromkeys(new_se)) # this will remove duplicates
return new_se
series = [2,8,4,5,5,6,6,6,2,1]
print(unique(series))
This question already has answers here:
The zip() function in Python 3
(2 answers)
Closed last month.
Created two lists in python and zipped them using zip function and saved them to a variable. Tried to print it to the console and was able to print successfully and when i tried again,empty list is printed.screenshot
Same with tuples.screenshot
Why i am able to retrieve values only once? Am in doing something wrong?
Python 3.7.1
The zip function is actually just an iterator -- an iterator can only be traversed once. If you want to be able to iterate over it multiple times, convert it to a list first.
a = [1,2,3]
b = [1,2,3]
c = list(zip(a, b))
Why is this?
Because those are similar to generators, so you can print first time, but when you try to print it again, it starts from the end, and no values left, see an example (clearer than yours):
>>> l=[1,2,3,4]
>>> it=iter(l) # generator basically (as in your question, zip, is also considered as a geneartor here)
>>> list(it)
[1, 2, 3, 4]
>>> list(it)
[]
>>>
So how to solve this issue?
Simply just replace this line:
zipped_list = zip(list1,list2)
With:
zipped_list = list(zip(list1,list2))
Then everything would work fine.
I often see code similar to this:
return [(var, val) for val in self.domains[var]
if self.nconflicts(var, val, assignment) == 0]
and I'm like DAMN that's sexy. But then I try to drop it sometimes and I get syntax errors. Are there any particular rules for this nice form of code writing that reverses the typical placement of for and if statements?
They're called list comprehensions. The basic syntax is (I'm using parens to group my words, not as part of the syntax):
[(an expression involving x) for x in someList if (some condition)]
If the condition evaluates to true, the resulting list includes the (expression involving x). So, for example, the following list comprehension uses this to only include strings in the resulting list.
>>> myList = [1,"hello",5.4,"world"]
>>> [elem for elem in myList if type(elem)==str]
['hello', 'world']
Note that the if part is optional, and the expression involving x can be as simple as just x (often used when you are just filtering out elements from another list).
In fact, the expression involving x doesn't really have to have x in it at all. For example, if for some reason you wanted a list of 0's as long as your name you could do this:
>>> [0 for letter in "Matthew"]
[0, 0, 0, 0, 0, 0, 0]
For when you don't need the list to stick around after you make it, use generator expressions instead. (Generator expressions and list comprehensions have the same syntax.)
See list comprehensions in the Python tutorial documentation. There are quite a number of things you can do with this syntax, including creating lists, sets, and dictionaries.
Although your example code is indeed a list comprehension, you do also occasionally see the reverse if syntax for inline conditionals:
a if b else c
The concept is called list comprehension,
http://www.secnetix.de/olli/Python/list_comprehensions.hawk
Take a look at lambda functions too,
http://www.secnetix.de/olli/Python/lambda_functions.hawk
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Flatten (an irregular) list of lists in Python
I have a python list whose elements can be letters or lists of letters.I wanted to write a function to extract all elements as below
suppose
l=['a',['b',['c']] ]
The output need to be ['a','b','c']
I thought recursion would be the correct way to do this.
The base case may be that the list contains only one element.
I tried to code this..but the output is
['a', 'b', ['c']]
Can someone please tell me what went wrong here?
def get_all_elements(mylist):
if len(mylist)==1:
return mylist[0]
else:
output=[mylist[0]]
output+=get_all_elements(mylist[1:])
return output
This seems to work Ok:
def flatten(iterable):
out = []
for i in iterable:
if hasattr(i,'__iter__'):
out.extend(flatten(i))
else:
out.append(i)
return out
l=['a',['b',['c']] ]
print flatten(l)
Where you went wrong is that in your solution, mylist[0] can itself be a list (of length 1) which contains another list (of arbitrary length). In that case, you just returned it.
When you check to see if mylist is of length 1, you don't check for the case where its contents are a list. Here's an example that will highlight your problem.
get_all_elements([1, [2, [3, 4]]])
If you want a complete solution, Flattening a shallow list in Python and Comprehension for flattening a sequence of sequences? are good places to look.
this will work on up to 3 levels deep, if the depth is arbitrary i don't think you'll be able to use a list comprehension.
[grandchild for parent in l for child in parent for grandchild in child]