lambda args: expr is a one-expression function. enumerate(it, start) pairs each item with a counter; zip pairs items and stops at the shortest input; map and filter are lazy, so wrap them in list(...) to see the values. sorted(it, key=f, reverse=True) sorts by f(item) and is stable (items with equal keys keep their original order). min and max accept a key and return the first of equally ranked items. sum(it, start) adds from start. any is true if some item is true; all is true if every item is true.
print(list(enumerate("ab", 1)), list(zip([1, 2, 3], "ab")))
print(list(map(lambda v: v * v, [1, 2, 3])), list(filter(lambda v: v % 2, range(6))))
print(sorted(["bb", "a", "ccc", "dd"], key=len), sorted([3, 1, 2], reverse=True))
print(max(["apple", "fig", "kiwi"], key=len), sum([1, 2, 3], 10), any([0, "", 3]), all([1, []]))
Output:
[(1, 'a'), (2, 'b')] [(1, 'a'), (2, 'b')]
[1, 4, 9] [1, 3, 5]
['a', 'bb', 'dd', 'ccc'] [3, 2, 1]
apple 16 True False
'bb' stays before 'dd' because both have length 2 and the sort is stable.
Generators. A function containing yield returns a generator: each next() runs the body up to the next yield and hands back that value. A generator can be consumed only once:
def gen(n):
for i in range(n):
yield i * i
gg = gen(3)
print(next(gg), list(gg), list(gg))
Output: 0 [1, 4] []. next took 0, the first list took the rest, and the second list found the generator exhausted.
Classes. self.name is first looked up on the instance, then on the class. A class attribute is shared by all instances. Mutating it through self (self.items.append(v)) changes the shared object, but assigning through self (self.count += 1) creates a separate instance attribute and leaves the class attribute alone:
class C:
items = []
count = 0
def __init__(self, v):
self.v = v
self.items.append(v)
self.count += 1
p = C(1)
q = C(2)
print(C.items, p.items is q.items, C.count, p.count, q.count)
Output: [1, 2] True 0 1 1.