List comprehension with tuple assignment - python

I want to ask if something like this is possible in python:
a,b = [i,i+1 for i in range(5)]
I know this isn't possible because I have got an error, but I think you understand what I am trying to achieve. Let me clear it up, I can do :
a,b = 3+2,3
Edit ---> Or even better:
a,b = [0,1,2,3,4],[1,2,3,4,5]
I wan't a similar thing in my first code example. I am trying to assign variables 'a' and 'b' as list, with list comprehension, but using tuple as assignment, the point is I don't want to use this:
a = [i for in range(5)]
b = [i+1 for in range(5)]
I am aware that I can use this: t = [(i,i+1) for i in range(5)], but that's not the point.
By the way this is only a simple example => "i,i+1"
Edit ---> I would like to clarify my question. How to assign several variables (type list) in one line, using list comprehension?

When you run this:
a,b = [(i,i+1) for i in range(5)] # wrapped i, i+1 in parentheses (syntax error)
It makes a list of five two-item tuples, like this:
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
But you're trying to assign those five tuples to only two objects (a and b)
Using argument unpacking (*) in zip, you can "unzip" the output to the first and second elements of each tuple:
a,b = zip(*[(i,i+1) for i in range(5)])
Which is this:
[(0, 1, 2, 3, 4), (1, 2, 3, 4, 5)]
And can be assigned to a and b as you've written

Don't try to be clever. This is perfectly acceptable code:
>>> a = range(5)
>>> b = range(1,6)
>>> a, b
([0, 1, 2, 3, 4], [1, 2, 3, 4, 5])

Related

Understanding dictionary assignment with tuple and list

I am a python beginner and I recently learned about dictionary assignment.
Here's what I am attempting
my_list = [[1, 4], [2, 2],[5,1]]
lists = dict(my_list)
print(lists) # Prints {1:4,2:2,5:1}
my_list = [(1, 4), (2, 2),(5,1)]
lists = dict(my_list)
print(lists) # Prints {1:4,2:2,5:1}
my_list = [[1, 4], (2, 2),{5,1}]
lists = dict(my_list)
print(lists) # Prints {1:5,2:2}
I am unable to explain why we are getting this weird answer in example 3.
Kindly help and explain.
{5, 1} is a set and, as such, inherently unordered. It is, somewhat depending on your Python implementation, unpredictably iterated as either 1->5 or 5->1. If you catch the first case, your dict instantiation is equivalent to:
lists = dict([(1, 4), (2, 2), (1, 5)])
or, even more verbose and obvious
lists = {}
lists[1] = 4
lists[2] = 2
lists[1] = 5 # overrides first binding of 1
Since there can be no duplicate keys in a dict, the last key binding for a repeated key "wins".

Tuple() vs () for Tuple Comprehension & Tuple Creation

I understand I can create a tuple by these two methods:
tuple([1, 2, 3])
(1, 2, 3)
I'm trying to understand what exactly is happening here:
tuple( (x, x*x) for x in [1, 2, 3] )
the tuple constructor seems to want a list, but it doesn't seem like the following would produce a list
(x, x*x) for x in [1, 2, 3]
Also I'm wondering why this doesn't work:
( (x, x*x) for x in [1, 2, 3] )
With dictionaries it seems I can use either:
my_dict = dict()
my_dict = {}
but now I'm wondering if, like tuples, there's a difference. Is ():tuple() a different :: relationship than {}:dict()?
Let's break down what's happening here. You're not wrong that the tuple constructor seems to want a list, but it would be more precise to say that the tuple constructor seems to want a list-like object. (to be specific, any iterable works)
This is a philosophy called as Duck Typing.
The saying goes as follows:
If it walks like a duck, swims like a duck, and quacks like a duck,
then it probably is a duck.
So then, a list works
a = [1, 2, 3, 2]
tuple(a) #Output: (1, 2, 3, 2)
but so does a different iterable, such as a set
tuple(set(a)) #Output: (1, 2, 3)
So, tuple does not care whether the object it receives is a list, just that it should be able to iterate and get values from the object.
Now, The second part of the magic comes from something called list comprehension/generator expressions. They are iterables you can create which make it easy to write 1 liners that create a list or a generator expression respectively. More on the generator later, for now, it is sufficient to see how a list comprehension works.
A simple example of list comprehension
[a for a in range(4)] #Output: [0, 1, 2, 3]
[a*a for a in range(4)] #output: [0, 1, 4, 9]
We see that they produce lists. So, can we feed them to a tuple constructor? Why not!
tuple([a for a in range(4)]) #Output: (0, 1, 2, 3)
tuple([a*a for a in range(4)]) #output: (0, 1, 4, 9)
Now, what about using the same expression but wrapping it in curved brackets instead?
(a for a in range(4)) #Output: <generator object <genexpr> at 0x000000FA4FDBE728>
You just created a generator expression
They are essentially memory efficient on-demand iterables. (to be jargon specific, they have a yield and next , and only yield values as needed). Let's see it in action.
my_generator = (a for a in range(4)) #generator created.
next(my_generator) #Outputs 0
next(my_generator) #Outputs 1
next(my_generator) #outputs 2
next(my_generator) #outputs 3
next(my_generator) #Raises StopIteration Error. The generator is exhausted.
We can see that we receive the same values as with our list comprehension.
So, does a tuple accept something like a generator? Well, duck typing to the rescue! Absolutely!
tuple((a for a in range(4))) #Output: (0, 1, 2, 3)
Do i need the redundant parenthesis? Nope!
tuple(a for a in range(4)) #Output: (0, 1, 2, 3)
tuple(a*a for a in range(4)) #Output: (0, 1, 4, 9)
Now, what does this produce? (x, x*x) for x in [1, 2, 3]
Well, it is an expression, but let's get an idea of how it would look in a list comprehension instead
[(x, x*x) for x in [1, 2, 3]] #Output: [(1, 1), (2, 4), (3, 9)]
Ah, its a list of tuples? Can a generator do the same?
my_generator = ((x, x*x) for x in [1, 2, 3]) #<generator object <genexpr> at 0x000000FA4FD2DCA8>
next(my_generator) #Output: (1, 1)
next(my_generator) #Output: (2, 4)
next(my_generator) #Output: (3, 9)
next(my_generator) #Raises StopIteration
Yep, looks good. So its a generator, but its an iterable. Behaves like a duck anyways, doesn't it? So, the tuple constructor should work just fine!
tuple((x, x*x) for x in [1, 2, 3]) #Output: ((1, 1), (2, 4), (3, 9))
So, that wraps up everything. The parenthesis do not imply a tuple all the time, () are not reserved for tuples. We see here that they can be used for generator expressions as well! Similarly, the {} do not have to be tied to dictionaries always, something similar to list comprehension actually exists for dictionaries too! (Known as dict comprehension)
I highly recommend going through the links for a more thorough explanation of the individual pieces that are working together here. Hope this helps!

Compare i vs other items in a Python list

Good Day, I've googled this question and have found similar answers but not what I am looking for. I am not sure what the problem is called so I that doesn't help me and I am looking for an elegant solution.
How do I loop over a list, item at a time, and compare it to all other items in a list. For example, if I had a list
l = [1,2,3,4]
Each loop of the out would yield something like
1 vs [2,3,4]
2 vs [1,3,4]
3 vs [1,2,4]
4 vs [1,2,3]
One solution I've been playing with involves duplicating the list every iteration, finding the index of the item, deleting it from the duplicate list and compare the two. This route seems less ideal as you have to create a new list on every iteration.
You can use itertools.combiations to create all combinations of the length 3 from your list and then use set.defference method to get the difference element between the l and the combinations. but note that you need to convert your main list to a set object :
>>> from itertools import combinations
>>> l = {1,2,3,4}
>>> [(l.difference(i).pop(),i) for i in combinations(l,3)]
[(4, (1, 2, 3)), (3, (1, 2, 4)), (2, (1, 3, 4)), (1, (2, 3, 4))]
A simple approach would be to use two loops:
arr = [1,2,3,4]
for i in arr:
comp = []
for j in arr:
if i != j:
comp.append(j)
print(comp)
I guess you could use list comprehension. While still creating a new list every iteration, you don't need to delete an item each time:
l = [1,2,3,4]
for i in l:
temp = [item for item in l if item != i]
print temp
[2, 3, 4]
[1, 3, 4]
[1, 2, 4]
[1, 2, 3]

Python List indexing multiple ranges

Sorry if this has already been asked, I couldn't find it anywhere. Basically how do I get 2 separate ranges within a list in Python.
If I want the 1st, 2nd, 5th and 6th elements of a list I know I can do this,
l = range(0,15)
l[1:3]+l[5:7]
but this assumes that l is easy to write. However I am scrapping something from a webpage using BeautifulSoup4, so I'm using soup.find_all (which gives me a list), so I can't simply write out 2 lists, l and concatenate them.
I want an answer that is something like
l = range(0,15)
l[1:3,5:7]
(but of course without the error) :)
This might be what you want. itemgetter creates a function that retrieves the listed indices:
>>> import operator
>>> snip = operator.itemgetter(1,2,5,6)
>>> snip(range(15))
(1, 2, 5, 6)
>>> snip('abcdefg')
('b', 'c', 'f', 'g')
>>> snip([1,2,3,4,5,6,7,8])
(2, 3, 6, 7)
I would do this with a function:
def multi_range(l, *args):
output = []
for indices in args:
output += l[indices[0]:indices[1]]
return output
So the first argument would be the list, and the rest of the parameters are tuples with the indices you're looking to pull. It would work fine with a long list name:
long_list_name = range(0, 15)
print multi_range(long_list_name, (1, 3), (5, 7))
>>> [1, 2, 5, 6]
l = range(0, 15)
print([l[i] for i in [1,2, 5,6]])
Not sure why you think l[1:3]+l[5:7] is hard, find_all returns a normal python list like any other.
Or using map:
l = range(0, 15)
print(list(map(l.__getitem__,(1,2,5,6))))
Is this OK?
indices = [1, 2, 5, 6]
selected = [l[i] for i in indices]

How To Merge an Arbitrary Number of Tuples in Python?

I have a list of tuples:
l=[(1,2,3),(4,5,6)]
The list can be of arbitrary length, as can the tuples. I'd like to convert this into a list or tuple of the elements, in the order they appear:
f=[1,2,3,4,5,6] # or (1,2,3,4,5,6)
If I know the at development time how many tuples I'll get back, I could just add them:
m = l[0] + l[1] # (1,2,3,4,5,6)
But since I don't know until runtime how many tuples I'll have, I can't do that. I feel like there's a way to use map to do this, but I can't figure it out. I can iterate over the tuples and add them to an accumulator, but that would create lots of intermediate tuples that would never be used. I could also iterate over the tuples, then the elements of the tuples, and append them to a list. This seems very inefficient. Maybe there's an even easier way that I'm totally glossing over. Any thoughts?
Chain them (only creates a generator instead of reserving extra memory):
>>> from itertools import chain
>>> l = [(1,2,3),(4,5,6)]
>>> list(chain.from_iterable(l))
[1, 2, 3, 4, 5, 6]
l = [(1, 2), (3, 4), (5, 6)]
print sum(l, ()) # (1, 2, 3, 4, 5, 6)
reduce(tuple.__add__, [(1,2,3),(4,5,6)])
tuple(i for x in l for i in x) # (1, 2, 3, 4, 5, 6)
Use the pythonic generator style for all of the following:
b=[(1,2,3),(4,5,6)]
list = [ x for x in i for i in b ] #produces a list
gen = ( x for x in i for i in b ) #produces a generator
tup = tuple( x for x in i for i in b ) #produces a tuple
print list
>> [1, 2, 3, 4, 5, 6]
>>> from itertools import chain
>>> l = [(1,2,3),(4,5,6)]
>>> list(chain(*l))
[1, 2, 3, 4, 5, 6]
You can combine the values in a list using the .extend() function like this:
l = [(1,2,3), (4,5,6)]
m = []
for t in l:
m.extend(t)
or a shorter version using reduce:
l = [(1,2,3), (4,5,6)]
m = reduce(lambda x,y: x+list(y), l, [])

Categories