How does this Lambda Expression work? - python

I'm familiar with simple lambda expressions. However I have this lambda expression in a book.
dd_pair = defaultdict(lambda: [0,0])
dd_pair[2][1] = 1 #now dd_pair contains {2: [0,1]}
I didn't declare any input variables, which is not how I learned Lambdas.
The previous example from the book has
dd_dict = defaultdict(dict)
dd_dict["Joel"]["City"] = "Seattle"
#this produces {"Joel" : { "City": "Seattle"}}
This makes sense, The Key is Joel, the nested Key is City, and the value is Seattle.
My two part question is, how does a lambda work with no input variables? and should I assume that the dd_pair[2][1] is to create a key 2 and at index 1 of the list set the value =1?

lambda: [0, 0] is exactly the same as:
def zero_pair():
return [0, 0]
that is, it is a function that takes no arguments and returns a len 2 array with the two entries set to 0. defaultdict takes a callable that takes no arguments and returns the value for a missing key so your second example (dd_pair = defaultdict(lambda: [0,0])) is the same as:
dd_pair = defaultdict(zero_pair)
Now, a dictionary in Python can take any value for its key (as long as they are hashable).
{0: 'integer', '0': 'string'}[0] # 'integer'
So when we index into dd_pair with a two, since the dictionary doesn't have any key at 2 our lambda (which is equivalent to zero_pair) is called and returns a list with two zeros in it. We then set the second element in that list to 1, mutating the list in place.
So yes, you're pretty much spot on when you say:
should I assume that the dd_pair[2][1] is to create a key 2 and at index 1 of the list set the value = 1

Related

Python - how to take the max length from a value of dictionary? Without lambda

Let us say I have this function:
def frequent_base(self):
dict = {}
for i in range(len(self.items)):
if self.items[i].base not in dict:
dict[self.items[i].base] = [(self.items[i].value)]
else:
dict[self.items[i].base] += [(self.items[i].base)]
return max(len(dict[self.items]), key=len(d))
Now, I can make it complicated and build a function which returns me index and such..
but it is bad coding and bad habit and takes long time ( especially in a test ).
How do I take the length?
let us say I have:
key1 with length 3 of value ( key1 has 3 values )
key2 with length 4 of value ( key 2 has 4 values )
key3 with length 2 of value ( key 3 has 2 values )
How do I take, not the key itself, not the value itself, but the len of values of key? which is 4 in this case.
or how do I take the key itself and then say length of value of that key? But I want to use Max function, I need to understand how to use that function good, with the key.
I will write and make myself super clear:
dict[1] = [1,2,3]
dict[2] = [1,2,3,4,5]
dict[3] = [1,2,3,7,8,9,10]
dict = {1: [1,2,3], 2:[1,2,3,4,5], 3:[1,2,3,7,8,9,10]}
I wish to return not dict[3], not 3, not the list of dict[3] it self.
I wish to return the length of the dict[3], which is 7
def frequent_base(self):
dict = {}
for i in range(len(self.items)):
if self.items[i].base not in dict:
dict[self.items[i].base] = [(self.items[i].value)]
else:
dict[self.items[i].base] += [(self.items[i].base)]
def key_for_len(dictionary):
return dictionary[1]
return max(dict.items(), key= key_for_len)
I am received error
Only thing you seem to need is maximal length amongst the values of your dictionary. You can easily get all the values using d.values() (d.items() would give you (key, value) tuples, which are harder to compare). Now we can easily calculate lengths of each value with generator comprehension (very much like list comprehension, which would also work) - len(v) for v in d.values(). Now we have an iterable with all the lengths, so it's just a matter of finding the maximum -
max(len(v) for v in d.values())
Should you need to get key or value with maximum length, we'd have to take a slightly different approach - we can use max key = argument to specify how we decide which element in the iterable is maximal - it is obvious when we are trying to get a maximum from few numbers, but not when we try to decide if (1, 3) is bigger than (2, 2) - in such case, we need to create a function that maps our items to easily comparable things like a number. In this case, we'd have tuples of (key, value) and we are comparing length of value - thus our function would be
def lenOfValue(kv):
return len(kv[1]) # kv[1] - 2nd element of a (key, value) tuple
code(1)
Then we pass that to max:
print(max(d.items(), key = lenOfValue))
And we get (2, [3, 4, 5, 6])
Bonus: lambda
Lambdas can be used here, which are really just a shorthand that lets us skip defining whole another function that we will probably never use again.
This code would be functionally exactly the same.
print(max(d.items(), key = lambda kv: len(kv[1])))
code(2)
Lambdas are nothing very complicated, being just a notation for creating simple, one-liner functions without all the bother of a def block, return etc.
Because Python's functions are objects like nearly anything else, this piece of code:
lenOfValue = lambda kv: len(kv[1])
really is in no way different that our previously used more lengthy definition of:
def lenOfValue(kv):
return len(kv[1])
It saves us few words and shows us the middle step between code(1) and code(2).
a_dict = {'some_key':[67,30,10], 'another_key':[87]}
max({ (k,len(v)) for k,v in a_dict.items() })
('some_key', 3)

Adding things to the emptiest list? (python)

I am looking for a simple way to compare the content of multiple lists and find the one with the fewest variables.
Then I want to set the new variable to be added to that list.
Example:
list1 = [1,5,7,12,4,8]
list2 = [3,2,9,11,14,6,10]
list3 = [13,15,16]
In this I would want it to find list3 to be the shortest and append the new value to it.
Due to pythons min, max, and sort's key keyword argument this is fairly simple to do in python
min([list1, list2, list3..], key = len).append(item)
key corresponds to the function applied to each element and whose result is used for comparison in both min and max. in this case the function len (which retrieves the length of sequence objects like list and tuple and any class that defines __len__ ) is used.
from min.
The key argument specifies a one-argument ordering function like that used for list.sort()
from list.sort()
key specifies a function of one argument that is used to extract a comparison key from each list element (for example, key=str.lower).
example
>>> x = [11231]
>>> y = [1,2,3,4,5]
>>> z = [1,2,3,4,1,1]
>>> min([x,y,z], key = len)
[11231]
You could write a small function that checks the len of each list, then append to that list.
def add_to_shortest(lists, item):
min(lists, key = lambda i: len(i)).append(item)
For example
>>> add_to_shortest([list1, list2, list3], 5)
>>> list3
[13, 15, 16, 5]

Dictionary of pair in python

I am new to python and was trying to make a dict of pairs in python.
What I would have done in c++ is
dist[make_pair(a,b)]=1
I am not sure how I can do the same in python
Edit
What I basically want to do is to map a pair of two integers to some value-
for example-
(1,2) -> 1
(1,3) -> 2
(2,1) ->3
I want to map pairs to some integer value
You can use the data structure "tuple" as a key in the dictionary. If you want to define a function that returns a n-tuple given n inputs, you can do that also.
a = 4
b = 5
dict_example = dict()
dict_example[(a,b)] = 1
print dict_example[(a,b)]
This prints the value of key (a,b), which is 1
To create an element having the tuple (a,b) as key, and 1 as a value, you just have to do :
new_dict = {(a,b) : 1}
If such a dict already exist, and you want to add the pair a,b as a key and 1 as a value, you have to do :
existing_dict[(a,b)] = 1
You can also use existing_dict[a,b] = 1 as Duncan pointed out.
I guess you tried using an array as a dict key, like:
>>> d = {}
>>> pair = [0, 1]
>>> d[pair] = 'foo'
TypeError: unhashable type: 'list'
Ok, what is that? Python dict keys must not be mutable. They can be numbers, strings and other hashable types - you can't use mutable types like lists, dicts and other mutable collections.
There is a collection that is very like a list but is not mutable: the tuple.
>>> d[tuple(pair)] = 'foo'
>>> d
{(1, 2): 'foo'}
As you can see, the literal for a tuple is (v1, v2, ..., vN). In places where it is not ambiguous, you can even omit the parenthesis:
>>> pair = 0, 1
>>> pair
(0, 1)
More than once I was bit by this when I left a trailing comma while refactoring code:
>>> x = 1, # I meant x = 1
>>> x
(1,)
That is the literal for a tuple with just 1 element.

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)

Python dictionary creation error

I am trying to create a Python dictionary from a stored list. This first method works
>>> myList = []
>>> myList.append('Prop1')
>>> myList.append('Prop2')
>>> myDict = dict([myList])
However, the following method does not work
>>> myList2 = ['Prop1','Prop2','Prop3','Prop4']
>>> myDict2 = dict([myList2])
ValueError: dictionary update sequence element #0 has length 3; 2 is required
So I am wondering why the first method using append works but the second method doesn't work? Is there a difference between myList and myList2?
Edit
Checked again myList2 actually has more than two elements. Updated second example to reflect this.
You're doing it wrong.
The dict() constructor doesn't take a list of items (much less a list containing a single list of items), it takes an iterable of 2-element iterables. So if you changed your code to be:
myList = []
myList.append(["mykey1", "myvalue1"])
myList.append(["mykey2", "myvalue2"])
myDict = dict(myList)
Then you would get what you expect:
>>> myDict
{'mykey2': 'myvalue2', 'mykey1': 'myvalue1'}
The reason that this works:
myDict = dict([['prop1', 'prop2']])
{'prop1': 'prop2'}
Is because it's interpreting it as a list which contains one element which is a list which contains two elements.
Essentially, the dict constructor takes its first argument and executes code similar to this:
for key, value in myList:
print key, "=", value

Categories