Today: lambda extras, floating point, the image logic, bluescreen, Ghost demo
Here is some answers about lambda from last lecture. This just extra material, not required for the course.
A "def" introduces a name for a function - basically the function name points to the code. We can do the same thing manually, = assigning a lambda into a variable like this:
>>> f = lambda n: n * 2 >>> >>> f # what is f? <function <lambda> at 0x7fabfd4b6ee0>
The above is essentially what def does - set up some code and set a variable name to point to it. You can then call the lambda using parenthesis to hold the params, just like a regular function:
>>> f(2) 4 >>> f(100) 200
This is not normally how you call your function code, so we don't want to dwell on this example. However, the example does show how Python has a uniform idea of some code in memory, and a name pointing to that code.
By far the most common map/lambda form is a lambda of 1 parameter mapped over a linear collection of elements. That said, here is how you do it for multiple parameters:
Given a lambda of 2 params, map() will take 2 lists of the same length, drawing the first param from the first list, the second param from the second list:
>>> list(map(lambda a, b: a + b, [1, 2, 3], [4, 5, 6])) [5, 7, 9]
Map() calls the lambda until it reaches the end of the shortest list, then stops. This could be a source of subtle bugs if the input lists are not the same length - e.g. the 6 is silently dropped in this example:
>>> list(map(lambda a, b: a + b, [1, 2], [4, 5, 6])) [5, 7]
The output of map() is not a list. It is an "iterable" which is list-like, but does not have all the features of a list. The iterable is cheaper to construct than a list, which is why Python does this. The iterable can be fed into another map(), or used in a foreach, or fed into a function like sorted() or min() or max(). So if you are going to use the map() output immediately like that, you do not need to do anything extra, and it will run the quickest:
>>> iter = map(lambda n: n * 2, [1, 2, 3, 4, 5]) >>> for num in iter: ... print(num) ... 2 4 6 8 10
However, if you need to access an element with [] or do a slice, or evaluate len(), then the iterable will not work. In that case, use list() to convert the iterable to a list and then everything should work. Do not worry about creating a list() out of an iterable; it's not that costly.
>>> iter = map(lambda n: n * 2, [1, 2, 3, 4, 5]) >>> iter[0] # [0] does not work, it's not a list TypeError: 'map' object is not subscriptable >>> >>> nums = list(iter) # fix: make a list out of it >>> nums [2, 4, 6, 8, 10] >>> nums[0] 2
# int 3 100 -2 # float, has a "." 3.14 -26.2 6.022e23
>>> 1 + 1 + 1 3 >>> 1 + 1 + 1.0 # float promotion 3.0 >>> 3.14 * 2 6.28 >>> 3.0 * 3 9.0 >>> 3.14 * 2 + 1 7.28
>>> float('3.14') # str -> float
3.14
>>> int(3.14) # float -> int, truncation
3
>>> int('16')
16
>>> int('3.14')
ValueError: invalid literal for int() with base 10: '3.14'
>>> 0.1 0.1 >>> 0.1 + 0.1 0.2 >>> 0.1 + 0.1 + 0.1 # this is why we can't have nice things 0.30000000000000004 >>> >>> 0.1 + 0.1 + 0.1 + 0.1 0.4 >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 0.5 >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 0.6 >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 0.7 >>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 0.7999999999999999 # here the garbage is negative
Another example with 3.14
>>> 3.14 * 3 9.42 >>> 3.14 * 4 12.56 >>> 3.14 * 5 15.700000000000001 # d'oh
Summary: float math is slightly wrong
The short answer, is that with a fixed number of bytes to store a floating point number in memory, there are some unavoidable problems where numbers have these garbage digits on the far right. It is similar to the way the number 1/3 is not possible to write it out precisely as a decimal number.
>>> a = 3.14 * 5 >>> b = 3.14 * 6 - 3.14 >>> a == b # Observe == not working right False >>> b 15.7 >>> a 15.700000000000001
>>> abs(a-b) < 0.00001 True