Key ideas in depth: lambdas and sorting
Lambdas
One purpose of lambdas and list comprehensions is to remove the potential for side effects with variables. A side effect is any unexpect use of a variable. In general, variables are
highly exposed to side effects as anything in their scope can alter or access them. This is true to the extent that some functional programmers talk about
any use of a variable as being a side effect. As we saw when we looked at
Here's some standard code creating a list of squares of numbers:
squares = []
for x in range(10):
squares.append(x**2)
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
The problem is that it uses the squares
variable which may or may not exist before, and will exist after this runs. To prevent this, we could use
either of the following, which don't create that variable:
squares = list(map(lambda x: x**2, range(10)))
squares = [x**2 for x in range(10)]
Neither of these create any variables outside the scope of their statements. It is worth noticing that lambdas are generally avoided by Python programmers, as (as can be seen above) list comprehensions were designed to replace them with more elegance. Nevertheless, they have their uses.
Sorting
Sorting may seem of limited use, as you can either sort as-is, or pass in a function that takes in a single value (of which there are a limited number). However, you can actually build some reasonably sophisticated sort routines. Let's walk through an example. Say we have:
s = ["a:bee","b:apple","c:zoo","d:coffee"]
and we want to sort the main words alphabetically. There's no function that will take in a word and return it minus the two left character. However, there is:
str.rpartition(sep)
which returns a tuple split at
sep
. The question is, how do we use this method, which takes a separator as whatever
self
it is called on, then get the second part of the tuple?
The operator library includes functions for invoking operations on variables:
itemgetter() # get an item
attrgetter() # get an internal variable
methodcaller() # invoke a method
So, we can sort using rpartition, and then sort again on the second item in the returned tuple. Here's the code:
import operator
s = ["a:bee","b:apple","c:zoo","d:coffee"]
s2 = sorted(
sorted(
s,key=operator.methodcaller("rpartition",":")
)
, key=operator.itemgetter(2)
)
print(s2)
Although this looks complicated, and indeed is, the message is firstly that with a little persistence most things can be achieved, and secondly it pays to read around the standard library and get used to it so you know roughly where to look when something stops you moving forward.