Функции в Python
Синтаксис объявления функций
Базовый синтаксис
Имя функции в Python может содержать буквы, цифры и символ подчеркивания _, но не может начинаться с цифры:
def foo():
return 42
foo() # возвращает 42
Важно: оператор return не обязателен. Если его нет, функция возвращает None:
def foo():
42
print(foo()) # выводит None
Документирование функций
Для документирования используются строковые литералы (docstring):
def foo():
"""I return 42."""
return 42
Документация доступна через атрибут __doc__ или функцию help():
print(foo.__doc__) # 'I return 42.'
help(foo) # показывает документацию
Работа с аргументами
Позиционные и именованные аргументы
def min(x, y):
return x if x < y else y
min(-5, 12) # -5
min(x=-5, y=12) # -5
min(y=12, x=-5) # -5 (порядок не важен)
Упаковка позиционных аргументов
Для работы с произвольным количеством аргументов используется *args:
def min(*args):
res = float("inf")
for arg in args:
if arg < res:
res = arg
return res
min(-5, 12, 13) # -5
min() # inf
Обязательный первый аргумент:
def min(first, *args):
res = first
for arg in args:
if arg < res:
res = arg
return res
min() # TypeError: missing required argument 'first'
Распаковка аргументов
Синтаксис распаковки работает с любыми итерируемыми объектами:
xs = {-5, 12, 13}
min(*xs) # -5
min(*[-5, 12, 13]) # -5
min(*(-5, 12, 13)) # -5
Ключевые аргументы и значения по умолчанию
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) # [] 😱
Правильно:
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] ✅
Только ключевые аргументы
Можно требовать, чтобы некоторые аргументы передавались только по имени:
def flatten(xs, *, depth=None):
pass
flatten([1, [2], 3], depth=1) # ✅
flatten([1, [2], 3], 1) # TypeError
Упаковка ключевых аргументов
def runner(cmd, **kwargs):
if kwargs.get("verbose", True):
print("Logging enabled")
runner("mysqld", limit=42) # ✅
runner("mysqld", **{"verbose": False}) # ✅
options = {"verbose": False}
runner("mysqld", **options) # ✅
Распаковка и присваивание
Базовая распаковка
x, y, z = [1, 2, 3] # ✅
x, y, z = {1, 2, 3} # ✅ (но порядок не гарантирован!)
x, y, z = "xyz" # ✅
# Распаковка вложенных структур
rectangle = (0, 0), (4, 4)
(x1, y1), (x2, y2) = rectangle
Расширенная распаковка (Python 3.0+)
first, *rest = range(1, 5) # first=1, rest=[2, 3, 4]
first, *rest, last = range(1, 5) # first=1, rest=[2, 3], last=4
# Можно использовать в любом месте
*_, (first, *rest) = [range(1, 5)] * 5
Особенность: при недостатке значений возникает ошибка:
first, *rest, last = [42] # ValueError
Распаковка в цикле for
for a, *b in [range(4), range(2)]:
print(b)
# Вывод:
# [1, 2, 3]
# [1]
Области видимости (Scopes)
Функции внутри функций
Функции в Python — объекты первого класса:
def wrapper():
def identity(x):
return x
return identity
f = wrapper()
f(42) # 42
Правило LEGB
Поиск имен осуществляется в четырех областях видимости:
- Local (локальная)
- Enclosing (объемлющая)
- Global (глобальная)
- Built-in (встроенная)
min = 42 # global
def f(*args):
min = 2 # enclosing
def g():
min = 4 # local
print(min)
Замыкания и позднее связывание
Замыкания используют переменные из внешних областей видимости во время выполнения:
def f():
print(i)
for i in range(4):
f()
# Вывод:
# 0
# 1
# 2
# 3
Присваивание и области видимости
По умолчанию присваивание создает локальную переменную:
min = 42
def f():
min += 1 # UnboundLocalError!
return min
Оператор global
Позволяет изменять глобальные переменные:
min = 42
def f():
global min
min += 1
return min
f() # 43
f() # 44
Оператор nonlocal (Python 3+)
Позволяет изменять переменные из объемлющей области видимости:
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)
lambda arguments: expression
# Эквивалентно:
def <lambda>(arguments):
return expression
Примеры:
lambda x: x ** 2
lambda foo, *args, bar=None, **kwargs: 42
Функции map, filter, zip
map - применяет функцию к каждому элементу:
list(map(lambda x: x % 7, [1, 9, 16, -1, 2, 5])) # [1, 2, 2, 6, 2, 5]
filter - оставляет элементы, удовлетворяющие условию:
list(filter(lambda x: x % 2 != 0, range(10))) # [1, 3, 5, 7, 9]
# С None - оставляет только truthy значения
xs = [0, None, [], {}, set(), "", 42]
list(filter(None, xs)) # [42]
zip - объединяет элементы нескольких последовательностей:
list(zip("abc", range(3), [42j, 42j, 42j]))
# [('a', 0, 42j), ('b', 1, 42j), ('c', 2, 42j)]
Генераторы коллекций
Генераторы списков:
[x ** 2 for x in range(10) if x % 2 == 1] # [1, 9, 25, 49, 81]
# Эквивалент с map/filter:
list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 1, range(10))))
# Вложенные генераторы:
nested = [range(5), range(8, 10)]
[x for xs in nested for x in xs] # [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} # {'year': 2014, 'month': 'September'}
{x: x ** 2 for x in range(4)} # {0: 0, 1: 1, 2: 4, 3: 9}
PEP 8: стиль кода
Базовые рекомендации
- 4 пробела для отступов
- Максимум 79 символов в строке кода
lower_case_with_underscoresдля переменных и функцийUPPER_CASE_WITH_UNDERSCORESдля констант
Выражения и операторы
Правильно:
exp = -1.05
value = (item_value / item_count) * offset / exp
if bar:
x += 1
if method == 'md5':
pass
if key not in d:
pass
Неправильно:
if bar: x += 1
if 'md5' == method:
pass
if not key in d:
pass
Функции
def something_useful(arg, **options):
"""One-line summary.
Optional longer description.
"""
pass
Резюме
- Функции могут принимать произвольное количество позиционных (
*args) и ключевых (**kwargs) аргументов - Синтаксис распаковки работает в вызовах функций, присваивании и циклах
- Поиск имен осуществляется по правилу LEGB
- Присваивание создает локальные переменные (можно изменить через
global/nonlocal) - Python поддерживает элементы ФП: lambda, map, filter, zip, генераторы коллекций
- PEP 8 содержит стилистические рекомендации для написания читаемого кода