If a function is written like this
def validate(input):
for a, b in input:
......
What is the syntactic form of input? I've tried a tuple but the function says there are "too many values to unpack".
In this case:
input is an iterable type (a list, tuple, etc.)
Its items are also iterable types. However, each item has exactly 2 items of its own.
I think the best way to explain this is with a demonstration:
>>> def validate(input):
... for a,b in input:
... print a,b
...
>>> data = [(0,1), (2,3), (4,5)]
>>> validate(data)
0 1
2 3
4 5
>>>
In the above code, notice how data is an iterable type (a list) and each of its items are iterable types (tuples in this case) that contain exactly 2 items.
for a,b in input expects input to be an iterable in which each element is a sub-iterable with exactly two items, e.g., ((1,2), ("foo", "bar"), ([], None)) but not (1, 2) or ((0,1), (2,3,4)).
Related
I want to check if all lists in a list of lists are equal.
One example for which I succeeded is a lists of two lists l2 for which
all([a == b for a, b in zip(*l2)])
correctly returns True if l2 = [[1,2],[1,2]] and Falsewhen l2 = [[1,2],[1,666]].
I expected to be able to directly use this code in the case in which the list of lists l has more lists in it by using the same code, but it seems to not work.
For example, when
l=[[1,2],[1,2],[1,2]]
all([a == b for a, b in zip(*l)])
returns the following error:
ValueError: too many values to unpack (expected 2)
I do not understand why this is the case as the result of zip(*l) looks like it should work:
list(zip(*l))
>> [(1, 1, 1), (2, 2, 2)]
Using the observation a == b and a == c imply a == c. You should test the first list with the other lists.
def equalLists(lists):
return not lists or all(lists[0] == b for b in lists[1:])
>>> equalLists([])
True
>>> equalLists([1,2],[1,2])
True
>>> equalLists([1,2],[1,2],[1,2])
True
>>> equalLists([1,2],[1,2],[1,3])
False
You could create a set of tuples and check the size is 1, otherwise they are not all the same:
len(set(tuple(elem) for elem in l)) == 1
This will work for a list of lists of any length. It will also be more efficient than linear time comparisons.
(You have to convert to a tuple first because a list is not hashable and a set requires its members to be hashable.)
Your method (and the other answers here) don't consider that if the lists' lengths vary, zip will shorten them to the length of the shortest:
all(a == b for a,b in zip([1,2], [1,2,3]))
>>> True
Firstly note that it's not necessary to construct a list in all like all([...]) as this adds an extra iteration after list creation, whereas as I've done above uses a generator which evaluates as it goes along.
If each list has hashable elements, I'd exploit set to calculate the distinct elements and check there's only 1:
len(set(tuple(x) for x in l)) == 1
If the elements aren't hashable, but do have the equals method defined on them (unlike your examples, since int is hashable) I'd compare each list to the first, possibly using a generator if you want to avoid comparing the first to itself:
li = iter(l)
first = next(li)
all(x == first for x in li)
This still makes use of python's built-in list equals method and won't do more comparisons than any zip methods in the case that all the lists are equal.
The only case where the above is inefficient is if you have a list of long lists, where most but not all are equal. In that case it's possible a zip method would be quicker:
from itertools import zip_longest
all(len(set(x)) == 1 for x in zip_longest(*l))
Here I used zip_longest for the case the list lengths are unequal. If you knw the lengths are equal you can use zip. By default it fills values with None from the shorter lists once they 'run out' in the iterator, so only use this if your lists have no legitimate Nones! (In that case you can set zip_longest(..., fillvalue="<something not in the lists>").
Equivalent for non-hashable list elements (with equals method):
all(all(i == x[0] for i in x[1:]) for x in zip_longest(*l))
l=[[1,2],[1,2],[1,2]]
all([a == b == c for a, b, c in zip(*l)])
You're telling zip() there's 2 values to unpack but zip has 3 lists to unpack.
You have three item after zip (1,1,1)
Use
l=[[1,2],[1,2],[1,2]]
all([a == b for a, b, c in zip(*l)])
or
l=[[1,2],[1,2],[1,2]]
all([a == b for a, b, *_ in zip(*l)])
# OR all([a == b for a, b, _ in zip(*l)])
# OR all([i[0] == i[1] for i in zip(*l)])
Edit as per comment.
To test all sub elements are equal
all([len(set(i)) == 1 for i in zip(*l)])
I understand Tuple are immutable and most of the list methods don't work on Tuple. Is there a way to find the index of an element in a tuple?(Other than typecasting it into a list and checking the index)
I don't quite see the problem:
>>> t = (1,2,3)
>>> t.index(2)
1
you can use index method of tuple to find the index of tuple.
>>> t = (1,2,3,4)
>>> t.index(2)
1
if tuple contains repetative items then use start and stop check.
t.index: (value, [start, [stop]])
>>> t = (1,2,3,4,2)
>>> t.index(2, 2, len(t))
4
This question already has answers here:
How are tuples unpacked in for loops?
(8 answers)
Closed 9 years ago.
I'm new to python and trying to work my way through http://yuji.wordpress.com/2011/06/22/python-imaplib-imap-example-with-gmail/ which has the following line:
result, data = mail.uid('search', None, "ALL") # search and return uids instead
Could someone explain this line?
Thank you.
It means that the function you have called returns an iterable, and the index 0 of the iterable is assigned to x and the index 1 is assigned to y. This is called tuple unpacking.
Eg)
>>> def func(a,b):
... return b,a
...
>>> a = 5
>>> b = 7
>>> a,b = func(a,b)
>>> a
7
>>> b
5
>>> x = func(a,b)
>>> x
(5, 7)
Edit to show that returning multiple values, they are packed as tuple by default and then unpacked at the other end. Since there is only one variable x here, the tuple is assigned to x.
Simple function for swapping two variables(Just for an example) that answers your question
At least, as of python 2.7.x, the function will unpack a tuple of 2 arguments returned from a function. If it returns anything other than 2 arguments in the tuple, I believe it will throw an error if you try to unpack more than this. If it returns 3 arguments and you unpack 2, for example, you will get an exception.
For example:
def func(a):
return (a,a+1,a*2)
a,b,c = func(7)
print a,b
==> 7 8 # NOTE Values
a = func(3)
print a
==> (3, 4, 6) # NOTE: TUPLE
a,b = func(9)
print a,b
==> Exception - ValueError: too many values to unpack
This may be different in 3.0+.
The other answer, that "the function you have called returns an iterable" is a good one. That is what is happening in your specific example. This is what is called "unpacking" in python. The following are examples of unpacking and assignment related to your question:
>>> a,b = 1,2
>>> a
1
>>> b
2
>>> a,b,c = ['do', 're', 'mi']
>>> a
'do'
>>> b
're'
>>> c
'mi'
>>>
This is one of the pretty features of Python syntax. If I am not mistaken, it is also optimized - i.e. the fastest way to achieve the result.
One way (the fastest way?) to iterate over a pair of iterables a and b in sorted order is to chain them and sort the chained iterable:
for i in sorted(chain(a, b)):
print i
For instance, if the elements of each iterable are:
a: 4, 6, 1
b: 8, 3
then this construct would produce elements in the order
1, 3, 4, 6, 8
However, if the iterables iterate over objects, this sorts the objects by their memory address. Assuming each iterable iterates over the same type of object,
What is the fastest way to iterate over a particular
attribute of the objects, sorted by this attribute?
What if the attribute to be chosen differs between iterables? If iterables a and b both iterate over objects of type foo, which has attributes foo.x and foo.y of the same type, how could one iterate over elements of a sorted by x and b sorted by y?
For an example of #2, if
a: (x=4,y=3), (x=6,y=2), (x=1,y=7)
b: (x=2,y=8), (x=2,y=3)
then the elements should be produced in the order
1, 3, 4, 6, 8
as before. Note that only the x attributes from a and the y attributes from b enter into the sort and the result.
Tim Pietzcker has already answered for the case where you're using the same attribute for each iterable. If you're using different attributes of the same type, you can do it like this (using complex numbers as a ready-made class that has two attributes of the same type):
In Python 2:
>>> a = [1+4j, 7+0j, 3+6j, 9+2j, 5+8j]
>>> b = [2+5j, 8+1j, 4+7j, 0+3j, 6+9j]
>>> keyed_a = ((n.real, n) for n in a)
>>> keyed_b = ((n.imag, n) for n in b)
>>> from itertools import chain
>>> sorted_ab = zip(*sorted(chain(keyed_a, keyed_b), key=lambda t: t[0]))[1]
>>> sorted_ab
((1+4j), (8+1j), (3+6j), 3j, (5+8j), (2+5j), (7+0j), (4+7j), (9+2j), (6+9j))
Since in Python 3 zip() returns an iterator, we need to coerce it to a list before attempting to subscript it:
>>> # ... as before up to 'from itertools import chain'
>>> sorted_ab = list(zip(*sorted(chain(keyed_a, keyed_b), key=lambda t: t[0])))[1]
>>> sorted_ab
((1+4j), (8+1j), (3+6j), 3j, (5+8j), (2+5j), (7+0j), (4+7j), (9+2j), (6+9j))
Answer to question 1: You can provide a key attribute to sorted(). For example if you want to sort by the object's .name, then use
sorted(chain(a, b), key=lambda x: x.name)
As for question 2: I guess you'd need another attribute for each object (like foo.z, as suggested by Zero Piraeus) that can be accessed by sorted(), since that function has no way of telling where the object it's currently sorting used to come from. After all, it is receiving a new iterator from chain() which doesn't contain any information about whether the current element is from a or b.
Have a bit of a problem distinguishing between identical integers.
In the following (which is obviously a trivial case) a, b, c are integers. I wish to create a dicionary, diction, which will contain {a: 'foo', b: 'bar', c: 'baz'}
diction = {}
for i in (a, b, c):
j = ('foo', 'bar', 'baz')[(a, b, c).index(i)]
diction[i] = j
All runs very nicely until, for example, a and b are the same: the third line will give index 0 for both a and b, resulting in j = 'foo' for each case.
I know lists can be copied by
list_a = [1, 2, 3]
list_b = list(list_a)
or
list_b = list_a[:]
So, is there any way of maybe doing this with my identical integers?
(I tried making one a float, but the value remains the same , so that doesn't work.)
To create a dictionary from two different iterables, you can use the following code:
d = dict(zip((a, b, c), ('foo', 'bar', 'baz')))
where zip is used to combine both iterables in a list of tuples that can be passed to the dictionary constructor.
Note that if a==b, then the 'foo' will be overwritten with 'bar', since the values are added to the dictionary in the same order they are in the iterable as if you were using this code:
d[a] = 'foo'
d[b] = 'bar'
d[c] = 'baz'
This is just the standard behaviour of a dictionary, when a new value is assigned to a key that is already known, the value is overwritten.
If you prefer to keep all values in a list, then you can use a collections.defaultdict as follows:
from collections import defaultdict
d = defaultdict(list)
for key, value in zip((a, b, c), ('foo', 'bar', 'baz')):
d[key].append(value)
You can't distinguish between identical objects.
You can tell them apart if they do not fall between -5 and 256
See also "is" operator behaves unexpectedly with integers
http://docs.python.org/c-api/int.html
The current implementation keeps an array of integer objects for all
integers between -5 and 256, when you create an int in that range you
actually just get back a reference to the existing object. So it
should be possible to change the value of 1. I suspect the behaviour
of Python in this case is undefined. :-)
In [30]: a = 257
In [31]: a is 257
Out[31]: False
In [32]: a = 256
In [33]: a is 256
Out[33]: True
You may have to roll your own dictionary like object that implements this sort of behavior though... and it still wouldn't be able to do anything between -5 and 256. I'd need to do more digging to be sure though.
If a and b have the same value then you can't expect them to point to different positions in dictionary if used as keys. Key values in dictionaries must be unique.
Also if you have two sequences the simplest way to make a dictionary out of them is to zip them together:
tup = (a,b,c)
val = ('foo', 'bar', 'baz')
diction = dict(zip(tup, val))
All of the answers so far are correct - identical keys can't be re-used in a dictionary. If you absolutely have to try to do something like this, but can't ensure that a, b, and c have distinct values you could try something like this:
d = dict(zip((id(k) for k in (a,b,c)), ('foo', 'bar', 'baz')))
When you go to look up your values though, you'll have to remember to do so like this:
d[id(a)]
That might help, but I am not certain what you're actually after here.