Advice on map function using setitem

Viewed 90

I'm struggling to understand a script from "the Python Book". The script removes duplicates from a list, by putting every element into a dict as a key (for example with 'none' as value) and then returns the dict.keys(). The script is as follows:

from operator import setitem
def distinct(l):
    d = {}
    map(setitem, (d,)*len(l), l, [])
    return d.keys()

I have run the script, passing a list which contains duplicates to distinct():

li = [1, 5, 6, 1, 8]
x = distinct(li)
print(x)

However when I run the script and print the result I just get:

dict_keys([])

Can somebody give me an example of how I can run the distinct() function so I can get some meaningful output? Also please can someone explain how the map function works with the inner setitem function. I have read about setitem, but I'm not sure how it is working here. I'm confused by the first the argument to setitem:

(d,)*len(l)
1 Answers

As written, distinct cannot work in Python 3, as map is a class that, in this case, returns an iterable that will call setitem on demand as you iterate over it. As the return value is ignored, setitem will never be called.

In Python 2, map was a function that acted much like a list comprehension: it returned a list of the return values from calling setitem on the elements of the given iterables. But it has never been considered good practice to use map (or a list comprehension) simply for the side effect of the function.

If you don't care about the resulting sequence or list, use a proper for loop, instead of creating a map instance that is never iterated over.

def distinct(l):
    d = {}
    for k in l:
        d[k] = None
    return list(d)

Or use a dict comprehension:

def distinct(l):
    return list({k: None for k in l})

The purpose of (d,)*len(l) was to create sequence of references to d, so that every call to setitem modified the same dict.

Adapting my first example to use setitem would look like

def distinct(l):
    d = {}
    for k in l:
        setitem(d, k, None)
    return list(d)

Note that list(map(setitem, (d,)*len(l), l, [])) would never have done anything. [] was a third iterable to whose contents, not the list itself, would be used as the third argument to setitem. map only iterates until the shortest of the iterables is exhausted, and the empty list would be exhausted immediately.

Related