Python semantics - Multiple parameters in declaration of for loop - python

Sorry to ask a dumb question, but could somebody tell me what the following means
for ctype, (codename, name) in searched_perms:
I don't understand what's going on the parenthesis. for ctype in serached_perms: would make sense.
I realise that the answer is in the python docs somewhere but since I don't know what I'm looking for, it's difficult to find the explaination.

This is practically equivalent to
for x in search_perms:
ctype, y = x
codename, name = y
or
for x in search_perms:
ctype = x[0]
codename = x[1][0]
name = x[1][1]
i.e., it unpacks the items from search_perms as pairs, then unpacks the second item of each pair as a pair as well.
Example:
>>> d = {"ham": "yes", "spam": "no", "eggs": "please"}
>>> for k, v in d.iteritems():
... print("%s? %s" % (k, v))
...
eggs? please
ham? yes
spam? no
>>> for i, (k, v) in enumerate(d.iteritems()):
... print("question %d: %s? %s" % (i, k, v))
...
question 0: eggs? please
question 1: ham? yes
question 2: spam? no
This works because enumerate(d.iteritems()) generates pairs where each second element is again a pair:
>>> list(enumerate(d.iteritems()))
[(0, ('eggs', 'please')), (1, ('ham', 'yes')), (2, ('spam', 'no'))]

ctype, (codename, name) is the same thing as (ctype, (codename, name)). Therefore, searched_perms needs to be a sequence of things of the form (a,(b,c)), and on each loop iteration the variables ctype, codename, name will be set to a,b,c.

Your list searchedparams should looks something like this:
In [1]: L = []
In [2]: L.append(('a', ('b', 'c')))
In [3]: L.append(('d', ('e', 'f')))
In [4]: L
Out[4]: [('a', ('b', 'c')), ('d', ('e', 'f'))]
In [6]: for ctype, (codename, name) in L:
print ctype, codename, name
...:
a b c
d e f
('a', ('b', 'c')) is a tuple of 2 values, where the right value is also a tuple of two values.

It means that searched_perms is an iterable that returns two elements during iteration: the first one is ctype and the second is a tuple, composed of another two elements: (codename, name). So searched_perms looks something like this:
[[ctype1, (code1, name1)], [ctype2, (code2, name2)], ...]
The syntax for ctype, (codename, name) in searched_perms allows the extraction of all the contents in searched_perms, element by element.

Related

Python: create a list of tuple with value and number of occurence from list [duplicate]

This question already has answers here:
How do I count the occurrences of a list item?
(29 answers)
Closed 1 year ago.
Based on a list, I need to create a list of tuple with each tuple containing (value, nbr_of_occurence_of_the_value_in_the_list).
My code is working but I feel it could be improve, does someone has an idea on how to make this code better ?
def get_tuple_count_list(_list):
tuple_count_list = []
for v in _list:
if v not in [v1 for (v1,count) in tuple_count_list]:
tuple_count_list.append((v,1))
continue
i = [v1 for (v1,count) in tuple_count_list].index(v)
tuple_count_list[i] = (v, tuple_count_list[i][1]+1)
return tuple_count_list
print(get_tuple_count_list(["a","b","b","d","e","a","a","a","c","b"]))
#result expected: [('a', 4), ('b', 3), ('d', 1), ('e', 1), ('c', 1)]
How about simply using Counter.most_common() which is a standard library util producing exactly your desired output:
from collections import Counter
def get_tuple_count_list(_list):
return Counter(_list).most_common()
>>> get_tuple_count_list(["a","b","b","d","e","a","a","a","c","b"])
[('a', 4), ('b', 3), ('d', 1), ('e', 1), ('c', 1)]
Also see the Counter docs.
Even with plainer means you should not operate on a list of tuples while taking the counts. The tuples' immutability and the list's linear search are big hinderances both in terms of code readability and performance. You should always use a constant time lookup structure (typically a dictionary like Counter):
def get_tuple_count_list(_list):
counts = {}
for x in _list:
counts[x] = counts.get(x, 0) + 1
return [*counts.items()]
# return sorted(counts.items(), key=lambda i: -i[1])
def get_tuple_count_list(_list):
output = []
for i in _list:
count_val = _list.count(i)
if (i,count_val) not in output:
output.append((i,count_val))
return output
def get_tuple_count_list(_list):
tuple_count_dict = {}
for v in _list:
if v not in tuple_count_dict:
tuple_count_dict[v] = 1
continue
tuple_count_dict[v] = tuple_count_dict[v] + 1
return list(tuple_count_dict.items())
Python tuple is immutable and the time complexity of searching item in list is O(n). Replacing list of tuple with dict can improve performance.

Generate combinations of elements from multiple lists

I'm making a function that takes a variable number of lists as input (i.e., an arbitrary argument list).
I need to compare each element from each list to each element of all other lists, but I couldn't find any way to approach this.
Depending on your goal, you can make use of some of the itertools utilities. For example, you can use itertools.product on *args:
from itertools import product
for comb in product(*args):
if len(set(comb)) < len(comb):
# there are equal values....
But currently it's not very clear from your question what you want to achieve. If I didn't understand you correctly, you can try to state the question in a more specific way.
I think #LevLeitsky's answer is the best way to do a loop over the items from your variable number of lists. However, if purpose the loop is just to find common elements between pairs of items from the lists, I'd do it a bit differently.
Here's an approach that finds the common elements between each pair of lists:
import itertools
def func(*args):
sets = [set(l) for l in args]
for a, b in itertools.combinations(sets, 2):
common = a & b # set intersection
# do stuff with the set of common elements...
I'm not sure what you need to do with the common elements, so I'll leave it there.
The itertools module provides a lot of useful tools just for such tasks. You can adapt the following example to your task by integrating it into your specific comparison logic.
Note that the following assumes a commutative function. That is, about half of the tuples are omitted for reasons of symmetry.
Example:
import itertools
def generate_pairs(*args):
# assuming function is commutative
for i, l in enumerate(args, 1):
for x, y in itertools.product(l, itertools.chain(*args[i:])):
yield (x, y)
# you can use lists instead of strings as well
for x, y in generate_pairs("ab", "cd", "ef"):
print (x, y)
# e.g., apply your comparison logic
print any(x == y for x, y in generate_pairs("ab", "cd", "ef"))
print all(x != y for x, y in generate_pairs("ab", "cd", "ef"))
Output:
$ python test.py
('a', 'c')
('a', 'd')
('a', 'e')
('a', 'f')
('b', 'c')
('b', 'd')
('b', 'e')
('b', 'f')
('c', 'e')
('c', 'f')
('d', 'e')
('d', 'f')
False
True
if you want the arguments as dictionary
def kw(**kwargs):
for key, value in kwargs.items():
print key, value
if you want all the arguments as list:
def arg(*args):
for item in args:
print item
you can use both
def using_both(*args, **kwargs) :
kw(kwargs)
arg(args)
call it like that:
using_both([1,2,3,4,5],a=32,b=55)

Implement lookahead iterator for strings in Python

I'm doing some parsing that requires one token of lookahead. What I'd like is a fast function (or class?) that would take an iterator and turn it into a list of tuples in the form (token, lookahead), such that:
>>> a = ['a', 'b', 'c', 'd']
>>> list(lookahead(a))
[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', None)]
basically, this would be handy for looking ahead in iterators like this:
for (token, lookahead_1) in lookahead(a):
pass
Though, I'm not sure if there's a name for this technique or function in itertools that already will do this. Any ideas?
Thanks!
There are easier ways if you are just using lists - see Sven's answer. Here is one way to do it for general iterators
>>> from itertools import tee, izip_longest
>>> a = ['a', 'b', 'c', 'd']
>>> it1, it2 = tee(iter(a))
>>> next(it2) # discard this first value
'a'
>>> [(x,y) for x,y in izip_longest(it1, it2)]
# or just list(izip_longest(it1, it2))
[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', None)]
Here's how to use it in a for loop like in your question.
>>> it1,it2 = tee(iter(a))
>>> next(it2)
'a'
>>> for (token, lookahead_1) in izip_longest(it1,it2):
... print token, lookahead_1
...
a b
b c
c d
d None
Finally, here's the function you are looking for
>>> def lookahead(it):
... it1, it2 = tee(iter(it))
... next(it2)
... return izip_longest(it1, it2)
...
>>> for (token, lookahead_1) in lookahead(a):
... print token, lookahead_1
...
a b
b c
c d
d None
I like both Sven's and gnibbler's answers, but for some reason, it pleases me to roll my own generator.
def lookahead(iterable, null_item=None):
iterator = iter(iterable) # in case a list is passed
prev = iterator.next()
for item in iterator:
yield prev, item
prev = item
yield prev, null_item
Tested:
>>> for i in lookahead(x for x in []):
... print i
...
>>> for i in lookahead(x for x in [0]):
... print i
...
(0, None)
>>> for i in lookahead(x for x in [0, 1, 2]):
... print i
...
(0, 1)
(1, 2)
(2, None)
Edit: Karl and ninjagecko raise an excellent point -- the sequence passed in may contain None, and so using None as the final lookahead value may lead to ambiguity. But there's no obvious alternative; a module-level constant is possibly the best approach in many cases, but may be overkill for a one-off function like this -- not to mention the fact that bool(object()) == True, which could lead to unexpected behavior. Instead, I've added a null_item parameter with a default of None -- that way users can pass in whatever makes sense for their needs, be it a simple object() sentinel, a constant of their own creation, or even a class instance with special behavior. Since most of the time None is the obvious and even possibly the expected behavior, I've left None as the default.
The usual way to do this for a list a is
from itertools import izip_longest
for token, lookahead in izip_longest(a, a[1:]):
pass
For the last token, you will get None as look-ahead token.
If you want to avoid the copy of the list introduced by a[1:], you can use islice(a, 1, None) instead. For a slight modification working for arbitrary iterables, see the answer by gnibbler. For a simple, easy to grasp generator function also working for arbitrary iterables, see the answer by senderle.
You might find the answer to your question here: Using lookahead with generators.
I consider all these answers incorrect, because they will cause unforeseen bugs if your list contains None. Here is my take:
SEQUENCE_END = object()
def lookahead(iterable):
iter = iter(iterable)
current = next(iter)
for ahead in iter:
yield current,ahead
current = ahead
yield current,SEQUENCE_END
Example:
>>> for x,ahead in lookahead(range(3)):
>>> print(x,ahead)
0, 1
1, 2
2, <object SEQUENCE_END>
Example of how this answer is better:
def containsDoubleElements(seq):
"""
Returns whether seq contains double elements, e.g. [1,2,2,3]
"""
return any(val==nextVal for val,nextVal in lookahead(seq))
>>> containsDoubleElements([None])
False # correct!
def containsDoubleElements_BAD(seq):
"""
Returns whether seq contains double elements, e.g. [1,2,2,3]
"""
return any(val==nextVal for val,nextVal in lookahead_OTHERANSWERS(seq))
>>> containsDoubleElements([None])
True # incorrect!

iterating through a list with an if statement

I have a list that I am looping through with a "for" loop and am running each value in the list through an if statement. My problem is that I am trying to only have the program do something if all the values in the list pass the if statement and if one doesn't pass, I want it to move along to the next value in the list. Currently it is returning a value if a single item in the list passes the if statement. Any ideas to get me pointed in the right direction?
Python gives you loads of options to deal with such a situation. If you have example code we could narrow that down for you.
One option you could look at is the all operator:
>>> all([1,2,3,4])
True
>>> all([1,2,3,False])
False
You could also check for the length of the filtered list:
>>> input = [1,2,3,4]
>>> tested = [i for i in input if i > 2]
>>> len(tested) == len(input)
False
If you are using a for construct you can exit the loop early if you come across negative test:
>>> def test(input):
... for i in input:
... if not i > 2:
... return False
... do_something_with_i(i)
... return True
The test function above will return False on the first value that's 2 or lower, for example, while it'll return True only if all values were larger than 2.
Maybe you could try with an for ... else statement.
for item in my_list:
if not my_condition(item):
break # one item didn't complete the condition, get out of this loop
else:
# here we are if all items respect the condition
do_the_stuff(my_list)
You need to loop through your whole list and check the condition before trying to do anything else with the data, so you need two loops (or use some built in that does the loop for you, like all()). From this codepad with nothing too fancy, http://codepad.org/pKfT4Gdc
def my_condition(v):
return v % 2 == 0
def do_if_pass(l):
list_okay = True
for v in l:
if not my_condition(v):
list_okay = False
if list_okay:
print 'everything in list is okay, including',
for v in l:
print v,
print
else:
print 'not okay'
do_if_pass([1,2,3])
do_if_pass([2,4,6])
You must always be careful if you're deleting items from your list while you're trying to iterate through it.
If you're not deleting then does this help:
>>> yourlist=list("abcdefg")
>>> value_position_pairs=zip(yourlist,range(len(yourlist)))
>>> value_position_pairs
[('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5), ('g', 6)]
>>> filterfunc=lambda x:x[0] in "adg"
>>> value_position_pairs=filter(filterfunc,value_position_pairs)
>>> value_position_pairs
[('a', 0), ('d', 3), ('g', 6)]
>>> yourlist[6]
'g'
now if value_position_pairs is empty you're done. If not you can increase i by one to go to the next value or iterate through the failed values using their position in the array.

Is there a 'multimap' implementation in Python?

I am new to Python, and I am familiar with implementations of Multimaps in other languages. Does Python have such a data structure built-in, or available in a commonly-used library?
To illustrate what I mean by "multimap":
a = multidict()
a[1] = 'a'
a[1] = 'b'
a[2] = 'c'
print(a[1]) # prints: ['a', 'b']
print(a[2]) # prints: ['c']
Such a thing is not present in the standard library. You can use a defaultdict though:
>>> from collections import defaultdict
>>> md = defaultdict(list)
>>> md[1].append('a')
>>> md[1].append('b')
>>> md[2].append('c')
>>> md[1]
['a', 'b']
>>> md[2]
['c']
(Instead of list you may want to use set, in which case you'd call .add instead of .append.)
As an aside: look at these two lines you wrote:
a[1] = 'a'
a[1] = 'b'
This seems to indicate that you want the expression a[1] to be equal to two distinct values. This is not possible with dictionaries because their keys are unique and each of them is associated with a single value. What you can do, however, is extract all values inside the list associated with a given key, one by one. You can use iter followed by successive calls to next for that. Or you can just use two loops:
>>> for k, v in md.items():
... for w in v:
... print("md[%d] = '%s'" % (k, w))
...
md[1] = 'a'
md[1] = 'b'
md[2] = 'c'
Just for future visitors. Currently there is a python implementation of Multimap. It's available via pypi
Stephan202 has the right answer, use defaultdict. But if you want something with the interface of C++ STL multimap and much worse performance, you can do this:
multimap = []
multimap.append( (3,'a') )
multimap.append( (2,'x') )
multimap.append( (3,'b') )
multimap.sort()
Now when you iterate through multimap, you'll get pairs like you would in a std::multimap. Unfortunately, that means your loop code will start to look as ugly as C++.
def multimap_iter(multimap,minkey,maxkey=None):
maxkey = minkey if (maxkey is None) else maxkey
for k,v in multimap:
if k<minkey: continue
if k>maxkey: break
yield k,v
# this will print 'a','b'
for k,v in multimap_iter(multimap,3,3):
print v
In summary, defaultdict is really cool and leverages the power of python and you should use it.
You can take list of tuples and than can sort them as if it was a multimap.
listAsMultimap=[]
Let's append some elements (tuples):
listAsMultimap.append((1,'a'))
listAsMultimap.append((2,'c'))
listAsMultimap.append((3,'d'))
listAsMultimap.append((2,'b'))
listAsMultimap.append((5,'e'))
listAsMultimap.append((4,'d'))
Now sort it.
listAsMultimap=sorted(listAsMultimap)
After printing it you will get:
[(1, 'a'), (2, 'b'), (2, 'c'), (3, 'd'), (4, 'd'), (5, 'e')]
That means it is working as a Multimap!
Please note that like multimap here values are also sorted in ascending order if the keys are the same (for key=2, 'b' comes before 'c' although we didn't append them in this order.)
If you want to get them in descending order just change the sorted() function like this:
listAsMultimap=sorted(listAsMultimap,reverse=True)
And after you will get output like this:
[(5, 'e'), (4, 'd'), (3, 'd'), (2, 'c'), (2, 'b'), (1, 'a')]
Similarly here values are in descending order if the keys are the same.
The standard way to write this in Python is with a dict whose elements are each a list or set. As stephan202 says, you can somewhat automate this with a defaultdict, but you don't have to.
In other words I would translate your code to
a = dict()
a[1] = ['a', 'b']
a[2] = ['c']
print(a[1]) # prints: ['a', 'b']
print(a[2]) # prints: ['c']
Or subclass dict:
class Multimap(dict):
def __setitem__(self, key, value):
if key not in self:
dict.__setitem__(self, key, [value]) # call super method to avoid recursion
else
self[key].append(value)
There is no multi-map in the Python standard libs currently.
WebOb has a MultiDict class used to represent HTML form values, and it is used by a few Python Web frameworks, so the implementation is battle tested.
Werkzeug also has a MultiDict class, and for the same reason.

Categories

Resources