iterate over list of tuples in two notations - python

I'm iterating over a list of tuples, and was just wondering if there is a smaller notation to do the following:
for tuple in list:
(a,b,c,d,e) = tuple
or the equivalent
for (a,b,c,d,e) in list:
tuple = (a,b,c,d,e)
Both of these snippits allow me to access the tuple per item as well as as a whole. But is there a notation that somehow combines the two lines into the for-statement? It seems like such a Pythonesque feature that I figured it might exist in some shape or form.

The pythonic way is the first option you menioned:
for tup in list:
a,b,c,d,e = tup

This might be a hack that you could use. There might be a better way, but that's why it's a hack. Your examples are all fine and that's how I would certainly do it.
>>> list1 = [(1, 2, 3, 4, 5)]
>>> for (a, b, c, d, e), tup in zip(list1, list1):
print a, b, c, d, e
print tup
1 2 3 4 5
(1, 2, 3, 4, 5)
Also, please don't use tuple as a variable name.

There isn't anything really built into Python that lets you do this, because the vast majority of the time, you only need to access the tuple one way or the other: either as a tuple or as separate elements. In any case, something like
for t in the_list:
a,b,c,d,e = t
seems pretty clean, and I can't imagine there'd be any good reason to want it more condensed than that. That's what I do on the rare occasions that I need this sort of access.
If you just need to get at one or two elements of the tuple, say perhaps c and e only, and you don't need to use them repeatedly, you can access them as t[2] and t[4]. That reduces the number of variables in your code, which might make it a bit more readable.

Related

best way of combining the values of 2 different tuples

I want to add together the values of 2 tuples (of any size), and create an output tuple.
For example:
a = (1,4)
b = (2,3)
output: (3,7)
Is there a better way to do it than just:
output = (a[0] + b[0], a[1]+b[1])
How about using a generator expression?
output = tuple(a[i] + b[i] for i in range(len(a)))
If you don't know that the tuples are the same length, you could try using something more fancy like zip (which will stop at the length of the shorter tuple), or itertools.izip (which will allow you to control how to handle different length tuples).
tuple(x+y for (x,y) in zip(a,b))
If you want to stick with 2-tuples, what you have is fine (and probably best). You might consider using a different data structure, one where the + operator adds element-wise. For example:
complex numbers add like 2-vectors (using .real and .imag components)
numpy arrays
Write your own Point class, overriding the __add__ magic method
If you wan't to do it in a way that doesn't require you spell out all elements, go with something functional:
output = tuple(map(sum, zip(a,b)))
or, a list-comp which you, again, must supply to tuple:
output = tuple([i+j for i,j in zip(a,b)])
you could always substitute zip with zip_longest from itertools, using a fill value of 0, if the sizes might differ.
tuple(map(lambda x, y: x + y, a, b))
import operator
tuple(map(operator.add, a, b))

Differentiating a tuple from a tuple of tuples

I have a tuple, and a tuple of tuples.
import numpy as np
a = ("Control", "Group1")
b = (("Control", "Group1"), ("Control", "Group1", "Group2))
How can I tell that a is fundamentally different from b? Both
print(len(a))
print(np.shape(a))
print(len(np.shape(a)))
and
print(len(b))
print(np.shape(b))
print(len(np.shape(b)))
produce the same output:
2
(2,)
1
Thanks in advance again!
You cannot, because they are not fundamentally different.
What should happen for the following?
c = (("Foo", "bar"), "baz")
It’s also a tuple, and it contains both "bare" values as well as another tuple.
If you need to detect tuples which only consist of tuples, use:
if all(isinstance(element, tuple) for element in a)
If you need to detect tuples which only consist of non-tuples, use:
if not any(isinstance(element, tuple) for element in a)
Both of the above are have a time complexity of O(n) (with n being the number of elements in a), which may not be desirable depending from where your data is coming. It is however unavoidable, unless you are willing to take the risk not actually having tuples of tuples.
Depending on what you’re doing with your data, you might actually want to check for a sequence of sequences. In that case, you should use the Sequence ABC (Python 2):
import collections.abc
if all(isinstance(element, collections.abc.Sequence) for element in a)
Use the equality operator, ==:
>>> a = ("Control", "Group1")
>>> b = (("Control", "Group1"), ("Control", "Group1", "Group2"))
>>> a == b
False
If you just want a vague idea of the general structure, and the string elements won't contain parentheses, you can count the parentheses:
>>> str(a).count('(')
1
>>> str(b).count('(')
3

Python assigning copies of the object to variables

Not a technical question, just a matter of coding style.
To me it makes more sense for assigning the same value to variables syntax to be a, b = 0, rather than a, b = 0, 0, but it is what it is. At least you can go around it by doing a = b = 0 if object is a number or a string, but today I came in situation that I needed 10 identical lists. So I went like:
list1, list2... = big_list[:], big_list[:]....
So big and ugly. How would you do it more in accordance with do-not-repeat-yourself principle?
You could do the following:
list1, list2, list3 = (big_list[:] for _ in range(3))
Whether that's an improvement is debatable. If you need many parallel lists, perhaps you should keep them in a collection instead of separate variables?
Personally, I would use a dictionary comprehension like
listdict = { key: value for key, value in range(1, 11), [big_list[:] for x in range(10)] }
Which would give you a dictionary containing the lists for reference under using keys 1 through 10 (i.e. listdict[1], listdict[2] ...etc)

When to drop list Comprehension and the Pythonic way?

I created a line that appends an object to a list in the following manner
>>> foo = list()
>>> def sum(a, b):
... c = a+b; return c
...
>>> bar_list = [9,8,7,6,5,4,3,2,1,0]
>>> [foo.append(sum(i,x)) for i, x in enumerate(bar_list)]
[None, None, None, None, None, None, None, None, None, None]
>>> foo
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>>
The line
[foo.append(sum(i,x)) for i, x in enumerate(bar_list)]
would give a pylint W1060 Expression is assigned to nothing, but since I am already using the foo list to append the values I don't need to assing the List Comprehension line to something.
My questions is more of a matter of programming correctness
Should I drop list comprehension and just use a simple for expression?
>>> for i, x in enumerate(bar_list):
... foo.append(sum(i,x))
or is there a correct way to use both list comprehension an assign to nothing?
Answer
Thank you #user2387370, #kindall and #Martijn Pieters. For the rest of the comments I use append because I'm not using a list(), I'm not using i+x because this is just a simplified example.
I left it as the following:
histogramsCtr = hist_impl.HistogramsContainer()
for index, tupl in enumerate(local_ranges_per_histogram_list):
histogramsCtr.append(doSubHistogramData(index, tupl))
return histogramsCtr
Yes, this is bad style. A list comprehension is to build a list. You're building a list full of Nones and then throwing it away. Your actual desired result is a side effect of this effort.
Why not define foo using the list comprehension in the first place?
foo = [sum(i,x) for i, x in enumerate(bar_list)]
If it is not to be a list but some other container class, as you mentioned in a comment on another answer, write that class to accept an iterable in its constructor (or, if it's not your code, subclass it to do so), then pass it a generator expression:
foo = MyContainer(sum(i, x) for i, x in enumerate(bar_list))
If foo already has some value and you wish to append new items:
foo.extend(sum(i,x) for i, x in enumerate(bar_list))
If you really want to use append() and don't want to use a for loop for some reason then you can use this construction; the generator expression will at least avoid wasting memory and CPU cycles on a list you don't want:
any(foo.append(sum(i, x)) for i, x in enumerate(bar_list))
But this is a good deal less clear than a regular for loop, and there's still some extra work being done: any is testing the return value of foo.append() on each iteration. You can write a function to consume the iterator and eliminate that check; the fastest way uses a zero-length collections.deque:
from collections import deque
do = deque([], maxlen=0).extend
do(foo.append(sum(i, x)) for i, x in enumerate(bar_list))
This is actually fairly readable, but I believe it's not actually any faster than any() and requires an extra import. However, either do() or any() is a little faster than a for loop, if that is a concern.
I think it's generally frowned upon to use list comprehensions just for side-effects, so I would say a for loop is better in this case.
But in any case, couldn't you just do foo = [sum(i,x) for i, x in enumerate(bar_list)]?
You should definitely drop the list comprehension. End of.
You are confusing anyone reading your code. You are building a list for the side-effects.
You are paying CPU cycles and memory for building a list you are discarding again.
In your simplified case, you are overlooking the fact you could have used a list comprehension directly:
[sum(i,x) for i, x in enumerate(bar_list)]

taking intersection of N-many lists in python

what's the easiest way to take the intersection of N-many lists in python?
if I have two lists a and b, I know I can do:
a = set(a)
b = set(b)
intersect = a.intersection(b)
but I want to do something like a & b & c & d & ... for an arbitrary set of lists (ideally without converting to a set first, but if that's the easiest / most efficient way, I can deal with that.)
I.e. I want to write a function intersect(*args) that will do it for arbitrarily many sets efficiently. What's the easiest way to do that?
EDIT: My own solution is reduce(set.intersection, [a,b,c]) -- is that good?
thanks.
This works for 1 or more lists. The 0 lists case is not so easy, because it would have to return a set that contains all possible values.
def intersection(first, *others):
return set(first).intersection(*others)
This works with 1 or more lists and does not use multiple parameters:
>>> def intersection(*listas):
... return set(listas[0]).intersection(*listas[1:])
...
>>> intersection([1,2,3,4],[4,5,6],[2,4,5],[1,4,8])
set([4])
>>> intersection([1,2,3,4])
set([1, 2, 3, 4])
>>>
Not sure this is better than other answers, anyway.
lists = [[5,4,3], [4,2], [6,2,3,4]]
try:
# the following line makes one intersection too much, but I don't think
# this hurts performance noticably.
intersected = set(lists[0]).intersection(*lists)
except ValueError:
# no lists[0]
intersected = set()
print intersected # set([4])
Sets can be intersected with any iterable, there's no need to convert it into a set first.

Categories