How does the following map function work? - python

>>>uneven = [['a','b','c'],['d','e'],['g','h','i']]
>>>map(None,*uneven)
O/P: [('a', 'd', 'g'), ('b', 'e', 'h'), ('c', None, 'i')]
The code above can be used for finding transpose of a matrix.
However iam unable to understand how it WORKS.

When using the * operator, the list is broken up into position arguments for the map. This is what you're actually running:
>>> map(None, ['a','b','c'], ['d','e'], ['g','h','i'])
When you pass multiple iterables to map, then the function (in this case None) is applied to every iterable in parallel. It processes 'a', 'd', 'g' first, and so on.
Edit:
As pointed out by Jon below, when you pass in None as the map function, it gets special cased to be the identity function, i.e. lambda id: id. This special casing of None's use in map has been removed in Python 3.

map(function, sequence[, sequence, ...]) -> list
from the documentation of map
If more than one sequence is given, the
function is called with an argument list consisting of the corresponding
item of each sequence, substituting None for missing values when not all
sequences have the same length.
If the function is None, return a list of the items of the sequence
Using sequence with * operator zip it according to the position of items in sequence.

Related

the process of function zip() python

I want to ask a question about zip() in python.
Consider the following simple code to demonstrate zip().
a = ['1', '2', '3']
b = ['a', 'b', 'c']
for key, value in zip(a, b):
print(key + value)
I know that the following output is produced:
1a
2b
3c
where each element in the corresponding lists are concatenated.
As a beginner in Python3, I understand the following about zip():
zip() creates a zip object, related to OOP that can be shown using list():
my_zip = zip(a, b)
print(my_zip)
print(list(my_zip))
>>> <zip object at 0xsomelocation>
>>>[('1', 'a'), ('2', 'b'), ('3', 'c')]
such that the zip object is a list of tuples.
My confusion is in this line from the original block of code, which I don't really understand:
for key, value in zip(a, b)
My interpretation is that as we are looping through our zip object, which has some innate __next__() method called on by our for loop, we loop through each tuple in turn.
For our first iteration of the loop, we get:
('1', 'a')
and python assigns '1' and 'a' to our variables key and value respectively. This is repeated until the end of the list dimensions i.e. 3 times.
Is this the correct interpretation of what is happening in our code?
such that the zip object is a list of tuples.
zip() doesn't return a list of tuples. It returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.
The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples. With no arguments, it returns an empty iterator.
and python assigns '1' and 'a' to our variables key and value respectively. This is repeated until the end of the list dimensions i.e. 3 times.
Yes. Rest of your interpretation is correct.
BONUS:
zip() should only be used with unequal length inputs when you don’t care about trailing, unmatched values from the longer iterables. If those values are important, use itertools.zip_longest() instead.

.get with tuples with dictionaries

Say I have a dictionary with tuples as the keys for example
dictionary = {('a','b'):1, ('c','d'):2}
Is it possible to return None if you try to find a value using a key not in the dictionary when using .get()?
I've tried
dictionary.get('a','c')
but this returns an integer and I've tried
dictionary.get(['a','c'])
and
dictionary.get([('a','c')])
but both return a type error.
To use ('a', 'c') as the key, you need to write like this:
dictionary.get(('a', 'c'))
Notice the doubled parentheses, it's necessary like that, to pass a tuple as the key parameter.
If you write dictionary.get('a', 'c'),
that means that 'a' is the key to get,
and 'c' is the default value to return in case the key doesn't exist.
And dictionary.get(['a','c']) cannot work,
because [...] is a list, and it's not hashable type.
And in any case ['a', 'c'] is not equal to ('a', 'c'),
so would not match anyway.

Python: from list to enumerated list to pass to lambda reduce function

I am trying to pass a list of hex char, into a lambda function, reduce to calculate a total decimal value. I am not sure what I am doing wrong but the python interpreter wouldn't recognize list(enumerate(reversed(numList)) as a list of tuples.
numList = ['3', '0', 'e', 'f', 'e', '1']
reduce(lambda sum,(up,x):sum+ int(x,16)*16**up,
enumerate(reversed(numList)))
when I print out
list(enumerate(reversed(numList))
It is a list of tuples.
[(0, '1'), (1, 'e'), (2, 'f'), (3, 'e'), (4, '0'), (5, '3')]
But it spit our error: can only concatenate tuple (not "int") to tuple
UPDATE:
The code is now working with a minor addition ",0" added to the lambda
reduce(lambda sum,(up,x):sum+ int(x,16)*16**up,
list(enumerate(reversed(numList))),0)
I don't understand what that means. Also I am not sure what is the best way to approach this.
that means you make sure, that it starts with 0 instead of the first Argument - in this case (0,'1') - because otherwise the types dont match? – am2 1 min ago
.
the third argument you add is initializer. without it, the sum in first iteration will be (0,'1'). so you were trying to evaluate (0,'1')+int(x,16)*16**up which is invalid. – ymonad 14 mins ago
UPDATE 2:
reduce(lambda sum,(up,x):sum+ int(x,16)*16**up,enumerate(reversed(numList)),0)
is just as good and enumerate() returns iter and list(enumerate...) is redundant.
Marked it as solved.
You don't need to use the generic reduce function when all you really need is to calculate the sum.
This works and is vastly simpler:
sum( int(x,16)*16**up for up,x in enumerate(reversed(numList)) )
Also, I'm going to guess you already know you can do the exact same thing like this:
int(''.join(numList), 16)

Converting simple tuple into dictionary

I have a even length tuple having elements like ('a','b','c','d','e','f') which I want to convert to dictionary having elements like ['a':'b', 'c':'d', 'e':'f'].
I tried using dict(tuple) but that wasn't helping. I have just started learning Python and any help will be highly appreciable.
It looks like you're trying to group the tuple into pairs, and then make a dict out of those pairs. There are two ways to do this.
The first is zipping slices:
zip(t[::2], t[1::2])
This is called an "extended slice", which is of the form start:stop:step. The first one is ::2, so it has the default start (the beginning of the tuple) and stop (the end of the tuple), and a step of 2, so it gets elements 0, 2, and 4. The second one is 1::2, so it's the same, but it starts at 1 instead of the default, so it gets elements 1, 3, and 5.
See the tutorial section on Lists for more details. (Of course you're using a tuple, not a list, but they both slice the same way.)
The second is zipping an iterator with itself:
i = iter(t)
zip(i, i)
Since the two references to i are both the same iterator, whenever you advance one, it advances both. So, the first one gets #0, then the second gets #1, then the first gets #2, the second #3, and so on.
See the tutorial section on Iterators for more details. Also see How grouper works, which explains a more general version of this idea (or at least tries to).
Either way, you get ('a', 'b'), then ('c', 'd'), then ('e', 'f'), so you can just pass that to dict:
dict(zip(t[::2], t[1::2]))
So, which one is better?
Slicing is probably easier to understand. It's also usually faster.
However, slicing doesn't work on arbitrary iterables, just sequences, it wastes memory on big inputs (you're essentially making a complete extra copy of the sequence), and it's a little harder to generalize.
You should learn how both of them work so you can choose appropriately.
You can use a dict comprehension:
t = ('a','b','c','d','e','f')
d = {t[i]:t[i+1] for i in range(0,len(t),2)}
Note that the part
range(0,len(t),2)
will generate a list of the form
[0, 2, 4]
Try this:
t = ('a','b','c','d','e','f')
dict(t[i:i+2] for i in xrange(0, len(t), 2))
=> {'a': 'b', 'c': 'd', 'e': 'f'}
>>> tup = ('a','b','c','d','e','f')
>>> dct = dict(zip(tup[::2], tup[1::2]))
{'a': 'b', 'c': 'd', 'e', 'f'}
This should do the trick
def tup2dict():
tup = ('a','b','c','d','e','f')
print ({i:j for (i,j) in zip(tup,tup[1:])[::2]})
Thanks to iterating-over-every-two-elements-in-a-list and python-dictionary-comprehensionn.

Python - input from list of tuples

I've declared a list of tuples that I would like to manipulate. I have a function that returns an option from the user. I would like to see if the user has entered any one of the keys 'A', 'W', 'K'. With a dictionary, I would say this: while option not in author.items() option = get_option(). How can I accomplish this with a list of tuples?
authors = [('A', "Aho"), ('W', "Weinberger"), ('K', "Kernighan")]
authors = [('A', "Aho"), ('W', "Weinberger"), ('K', "Kernighan")]
option = get_option()
while option not in (x[0] for x in authors):
option = get_option()
How this works :
(x[0] for x in authors) is an generator expression, this yield the [0]th element of each item one by one from authors list, and that element is then matched against the option. As soon as match is found it short-circuits and exits.
Generator expressions yield one item at a time, so are memory efficient.
How about something like
option in zip(*authors)[0]
We are using zip to essentially separate the letters from the words. Nevertheless, since we are dealing with a list of tuples, we must unpack it using *:
>>> zip(*authors)
[('A', 'W', 'K'), ('Aho', 'Weinberger', 'Kernighan')]
>>> zip(*authors)[0]
('A', 'W', 'K')
Then we simply use option in to test if option is contained in zip(*authors)[0].
There are good answers here that cover doing this operation with zip, but you don't have to do it like that - you can use an OrderedDict instead.
from collections import OrderedDict
authors = OrderedDict([('A', "Aho"), ('W', "Weinberger"), ('K', "Kernighan")])
Since it remembers its entry order, you can iterate over it without fear of getting odd or unusual orderings of your keys.

Categories

Resources