Syntax 1
def get_lectures_stream():
return "youtu.be/dQw4w9WgXcQ"
>>> get_lectures_stream()
"youtu.be/dQw4w9WgXcQ"
What if I remove return?
Syntax 2
- name: number can’t be the first symbol, but almost any unicode symbol can be used
- return: if none, then returns
None
def get_lectures_stream():
"youtu.be/dQw4w9WgXcQ"
>>> get_lectures_stream()
None
Syntax 3
def get_lectures_stream() -> str:
"""Returns the last lecture stream."""
return "youtu.be/dQw4w9WgXcQ"
Functions are first-class objects in Python.
What does it mean?
1️⃣ What is First-Class object
- Created at runtime
- Assigned to a variable or element in a data structure
- Passed as an argument to a function
- Returned as the result of a function
Integers, strings, dictionaries, and functions in Python
🚀 Function as an object
>>> get_lectures_stream.__doc__
'Returns the last lecture stream.'
🚲 Let’s create a function
Let’s create a function 1
def min(x, y):
return x if x < y else y
>>> min(-5, 0)
-5
Let’s create a function 2
def min(x, y):
return x if x < y else y
>>> min(x=-5, y=0)
-5
Let’s create a function 3
def min(x, y):
return x if x < y else y
>>> min(x=-5, z=0)
# ???
Let’s upgrade the function!
>>> min(1, 2, 3)
1
>>> min({1, 2, 3})
1
>>> bounded_min(-42, 0, 42, low=0, high=255)
0
📦 Packing and unpacking
We did it!
def min(*args): # what's type(args)?
res = float("inf")
for arg in args:
if arg < res:
res = arg
return res
>>> min(-5, 12, 13)
-5
>>> min() # how to require at least 1 arg?
inf
The solution
def min(first, *args):
res = first
# ...
🧐 How to apply the function to a collection? (list, tuple, set)
The answer: unpacking
>>> xs = {-5, 12, 13}
>>> min(*xs)
-5
>>> min(*[-5, 12, 13])
-5
>>> min(*(-5, 12, 13))
-5
Let’s get back…
>>> def bounded_min(first, *args, lo=float("-inf"), hi=float("inf")):
res = hi
for arg in (first, ) + args: # what's the algorithmic complexity?
if arg < res and lo < arg < hi:
res = arg
return max(res, lo)
...
>>> bounded_min(-5, 12, 13, lo=0, hi=255)
12
👀 What’s wrong?
>>> def unique(iterable, seen=set()):
acc = []
for item in iterable:
if item not in seen:
seen.add(item)
acc.append(item)
return acc
Hint: when do default values initialize?
>>> xs = [1, 1, 2, 3]
>>> unique(xs)
[1, 2, 3]
>>> unique(xs)
[]
>>> unique.__defaults__
({1, 2, 3},)
How to solve it?
The solution
>>> 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)
[1, 2, 3]
🤔 How-to: require key argument
>>> 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)
🚀 Assignment (unpacking)
>>> x, y, z = [1, 2, 3]
>>> x, y, z = {1, 2, 3} # ???
>>> x, y, z = "xyz"
>>> first, *rest = range(1, 5)
>>> first, rest
(1, [2, 3, 4])
>>> first, *rest, last = range(1, 5)
>>> last
4
>>> for a, *b in [range(4), range(2)]:
print(b)
...
[1, 2, 3]
[1]
Let’s look inside one time only
>>> import dis
>>> dis.dis("first, *rest, last = ('a', 'b', 'c')")
0 LOAD_CONST 4 (('a', 'b', 'c'))
3 UNPACK_EX 257
6 STORE_NAME 0 (first)
9 STORE_NAME 1 (rest)
12 STORE_NAME 2 (last)
15 LOAD_CONST 3 (None)
18 RETURN_VALUE
>>> first, *rest, last = ['a', 'b', 'c'] # ???
Functions wrap up
- basic syntax
- minimal example
- packing, unpacking
👁 Scopes
>>> def wrapper():
def identity(x):
return x
return identity
...
>>> f = wrapper()
>>> f(42)
42
LEGB Rule: Local → Enclosing → Global → Built-in
The search is done during execution, not definition.
>>> min # builtin
<built-in function min>
>>> min = 42 # global
>>> def f(*args):
min = 2
def g(): # enclosing
min = 4 # local
print(min)
...
🤔 Is Python a functional programming language?
Guido van Rossum
I have never considered Python to be heavily influenced by functional languages, no matter what people say or think. I was much more familiar with imperative languages such as C and Algol 68 and although I had made functions first-class objects, I didn’t view Python as a functional programming language.
We have anonymous functions!
>>> lambda arguments: expression
# which is ...
>>> def <lambda>(arguments):
return expression
>>> lambda foo, *args, bar=None, **kwargs: 42
<function <lambda> at 0x100fb9730>
map applies the given function to every iterable (sequence element)
>>> map(indentity, range(4))
<map object at 0x100fc4c88>
>>> list(map(identity, 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>
filter removes any element, which is not satisfying the given predicat
>>> 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]
zip builds tuple sequence from elements of multiple sequences
>>> list(zip("abc", range(3), [42j, 42j, 42j]))
[('a', 0, 42j), ('b', 1, 42j), ('c', 2, 42j)]
We have sequence (list, dict, …) generators
>>> [x ** 2 for x in range(10) if x % 2 == 1]
[1, 9, 25, 49, 81]
is better than
>>> list(map(lambda x: x ** 2,
filter(lambda x: x % 2 == 1,
range(10))))
[1, 9, 25, 49, 81]
Which can be nested
>>> nested = [range(5), range(8, 10)]
>>> [x for xs in nested for x in xs] # flatten
[0, 1, 2, 3, 4, 8, 9]
Sets and dictionaries generators
>>> {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}
🔥 Scopes and functional programming wrap up
- LEGB Rule: Local → Enclosing → Global → Built-in
- one-line lambdas
- map, filter, zip
- generators
❓ How to generate 2D array of zeros in Python?
� PEP-8
Why? To improve readability. Basic rules:
- 4 spaces for indentation
- 79 symbols for a line of code 🤔
- variables_and_functions, CONSTANTS_YEAH, JustClasses
- think how it can be read
To check equality…
- for objects:
!=
and==
- for singletons:
is
andis not
- for booleans: use the bool itself, e.g.
if foo:
Write like it’s English
if not key in d
🤢if key not in
🤗
And write docstrings!
Full set of rules: https://www.python.org/dev/peps/pep-0008/