>>> def foo():
... return 42
...
>>> foo()
42
>>> def foo():
... 42
...
>>> print(foo())
None
>>> def foo():
... """I return 42.""
... return 42
...
>>> foo.__doc__
'I return 42.'
>>> help(foo) # или foo? в IPython.
>>> def min(x, y): # __o
... return x if x < y else y # _`\<,_
... # (_)/ (_)
>>> min(-5, 12) # http://ascii.bike
-5
>>> min(x=-5, y=12)
-5
>>> min(x=-5, z=12)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: min() got an unexpected keyword argument 'z'
>>> min(y=12, x =-5) # порядок не важен
-5
>>> xs = {-5, 12, 13}
>>> min(???)
-5
>>> min(-5, 12, 13)
-5
>>> bounded_min(-5, 12, 13, lo=0, hi=255)
12
>>> bounded_min = make_min(lo=0, hi=255)
>>> bounded_min(-5, 12, 13)
12
Вопрос
Как потребовать, чтобы в args был хотя бы один элемент?
>>> def min(*args): # type(args) == tuple.
... res = float("inf")
... for arg in args:
... if arg < res:
... res = arg
... return res
...
>>> min(-5, 12, 13)
-5
>>> min()
inf
Вопрос
Как применить функцию min к коллекции?
>>> def min(first, *args):
... res = first
... # ...
...
>>> min()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: min() missing 1 required [...] argument: 'first'
>>> xs = {-5, 12, 13}
>>> min(???)
>>> xs = {-5, 12, 13}
>>> min(*xs)
-5
>>> min(*[-5, 12, 13])
-5
>>> min(*(-5, 12, 13))
-5
>>> bounded_min(-5, 12, 13, lo=0, hi=255)
12
Вопрос
В какой момент происходит инициализация ключевых аргументов со значениями по умолчанию?
>>> def bounded_min(first, *args, lo=float("-inf"),
... hi=float("inf")):
... res = hi
... for arg in (first,) + args:
... if arg < res and lo < arg < hi:
... res = arg
... return max(res, lo)
...
>>> bounded_min(-5, 12, 13, lo=0, hi=255)
12
>>> def unique(iterable, seen=set()):
... acc = []
... for item in iterable:
... if item not in seen:
... seen.add(item)
... acc.append(item)
... return acc
...
>>> xs = [1, 1, 2, 3]
>>> unique(xs)
[1, 2, 3]
>>> unique(xs)
[]
>>> unique.__defaults__
({1, 2, 3},)
>>> def unique(iterable, seen=None)):
... seen = set(seen or []) # None -- falsy
... acc = []
... for item in iterable:
... if item not in seen:
... seen.add(item)
... acc.append(item)
... return acc
...
>>> xs = [1, 1, 2, 3]
>>> unique(xs)
[1, 2, 3]
>>> unique(xs)
[]
>>> def flatten(xs, depth=None):
... pass
...
>>> flatten([1, [2], 3], depth=1)
>>> flatten([1, [2], 3], 1)
>>> def flatten(xs, *, depth=None):
... pass
...
>>> flatten([1, [2], 3], 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: flatten() takes 1 positional argument [...]
>>> def runner(cmd, **kwargs):
... if kwargs.get("verbose". True):
... print("Logging enabled")
...
>>> runner("mysqld", limit=42)
Logging enabled
>>> runner("mysqld", **{"verbose": False})
>>> options = {"verbose": False}
>>> runner("mysqld", **options)
>>> acc = []
>>> seen = set()
>>> (acc, seen) = ([], set())
>>> x, y, z = [1, 2, 3]
>>> x, y, z = {1, 2, 3} # unordered!
>>> x, y, z = "xyz"
>>> rectangle = (0, 0), (4, 4)
>>> (x1, y1), (x2, y2) = rectangle
>>> first, *rest = range(1, 5)
>>> first, rest
(1, [2, 3, 4])
>>> first, *rest, last = range(1, 5)
>>> last
4
>>> first, *rest, last = [42]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 1 values to unpack
>>> *_, (first, *rest) = [range(1, 5)] * 5
>>> first
1
Синтаксис распаковки работает в цикле for, например:
>>> for a, *b in [range(4), range(2)]:
... print(b)
...
[1, 2, 3]
[1]
>>> def f(*args, **kwargs):
... print(args, kwargs)
...
>>> f(1, 2, *[3, 4], *[5],
... foo="bar", **{"baz": 42}, boo=24)
(1, 2, 3, 4, 5) {{'baz': 42, 'boo': 24, 'foo': 'bar'}}
>>> defaults = {"host": "0.0.0.0", "port": 8080}
>>> {**defaults, "port": 80}
{'host': '0.0.0.0', 'port': 80}
>>> [*range(5), 6] # аналогично для множества и кортежа
[0, 1, 2, 3, 4, 6]
>>> def f(*args, **kwargs):
... pass
...
>>> f(1, 2, 3, **{"foo": 42})
>>> first, *rest = range(4)
>>> for first, *rest in [range(4), range(2)]:
... pass
...
>>> def wrapper():
... def identify(x):
... return x
... return identity
...
>>> f = wrapper()
>>> f(42)
42
>>> def make_min(*, lo, hi):
... def min(first, *args):
... res = hi
... for arg in (first,) + args:
... if arg < res and lo < arg < hi:
... res = arg
... return max(res, lo)
... return min
...
>>> bounded_min = make_min(lo=0, hi=255)
>>> bounded_min(-5, 12, 13)
0
Правило LEGB
Поиск имени ведется не более, чем в четырех областях видимости: локальной, замет в объемлющей функции (если такая имеется, затем в глобальной и, наконец, во встроенной.
>>> min # builtin
<built-in function min>
>>> min = 42 # global
>>> def f(*args):
... min = 2 # enclosing
... def g():
... min = 4 # local
... print(min)
...
>>> min = 42 # ≡ globals()["min"] = 42
>>> globals()
{..., 'min': 42}
>>> def f():
... min = 2 # ≡ locals()["min"] = 2
... print(locals())
...
>>> f()
{'min': 2}
>>> def foo():
... print(i)
...
>>> for i in range(4):
... f()
...
0
1
2
3
>>> min = 42
>>> def f():
... min += 1
... return min
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'min' referenced [...]
>>> min = 42
>>> def f():
... global min
... min += 1
... return min
...
>>> f()
42
>>> f()
44
>>> def cell(value=None):
... def get():
... return value
... def set(update):
... nonlocal value
... value = update
... return get, set
...
>>> get, set = cell()
>>> set(42)
>>> get()
42
и эквивалентны по поведению
>>> lambda arguments: expression
>>> def <lambda>(arguments):
... return expression
>>> lambda foo, *args, bar=None, **kwargs: 42
<function <lambda> at 0x100fb9730>
>>> map(indentity, range(4))
<map object at 0x100fc4c88>
>>> list(map(indentity, range(4)))
[0, 1, 2, 3]
>>> set(map(lambda x: x % 7, [1, 9, 16, -1, 2, 5]))
{1, 2, 5, 6}
>>> map(lambda s: s.strip(), open("./HBA1.txt"))
<map object at 0x100fc4cc0>
>>> list(map(lambda x, n: x ** n,
... [2, 3], range(1, 8)))
[2, 9]
>>> filter(lambda x: x % 2 != 0, range(10))
<filter object at 0x1011edfd0>
>>> list(filter(lambda x: x % 2 != 0, range(10)))
[1, 3, 5, 7, 9]
>>> xs = [0, None, [], {}, set(), "", 42]
>>> list(filter(None, xs))
[42]
>>> list(zip("abc", range(3), [42j, 42j, 42j]))
[('a', 0, 42j), ('b', 1, 42j), ('c', 2, 42j)]
>>> list(zip("abc", range(10))
[('a', 0), ('b', 1), ('c', 2)]
>>> [x ** 2 for x in range(10) if x % 2 == 1]
[1, 9, 25, 49, 81]
>>> list(map(lambda x: x ** 2,
... filter(lambda x: x % 2 == 1,
... range(10))))
[1, 9, 25, 49, 81]
>>> nested = [range(5), range(8, 10)]
>>> [x for xs in nested for x in xs] $ flatten
[0, 1, 2, 3, 4, 8, 9]
>>> {x % 7 for x in [1, 9, 16, -1, 2, 5]}
{1, 2, 5, 6}
>>> date = {"year": 2014, "month": "September", "day": ""}
>>> {k: v for k, v in date.items() if v}
{'month': 'September', 'year': 2014}
>>> {x: x ** 2 for x in range(4)}
{0: 0, 1: 1, 2: 4, 3: 9}