'Привет, Боб!'
Такого рода приемами иногда трудно уравновесить идею сделать ваш про-
граммный код достаточно четким и при этом остаться в рамках принципа
DRY
1
. Это всегда будет нелегким выбором. Если у вас есть возможность
получить другое мнение от коллеги, то я призываю вас — спрашивайте.
Ключевые выводы
В Python переменные
*args
и
**kwargs
позволяют писать функции
с неизвестным количеством аргументов.
1
DRY (от англ. Don’t Repeat Yourself, то есть «не повторяйся») — это принцип разработки
программного обеспечения, нацеленный на снижение повторения информации различ-
ного рода. См.
https://ru .wikipedia .org/wiki/Don’t_repeat_yourself
. — Примеч. пер.
96 Глава 3 • Эффективные функции
Переменная
*args
собирает дополнительные позиционные аргументы
в кортеж. Переменная
**kwargs
собирает дополнительные именован-
ные аргументы в словарь.
Фактическим синтаксисом является
*
и
**
. Названия
args
и
kwargs
—
это просто договоренность (которой следует придерживаться).
3 .5 . Распаковка аргументов функции
Действительно крутым, но немного загадочным функциональным сред-
ством языка является способность «распаковывать» аргументы функции
из последовательностей и словарей при помощи операторов
*
и
**
.
Давайте определим простую функцию для работы в качестве примера:
def print_vector(x, y, z):
print('<%s, %s, %s>' % (x, y, z))
Как вы видите, эта функция принимает три аргумента (
x
,
y
и
z
) и печатает
их в приятно отформатированном виде. Мы можем применить эту функ-
цию в нашей программе для структурной распечатки трехмерных векторов:
>>> print_vector(0, 1, 0)
<0, 1, 0>
Нужно сказать, что в зависимости от того, какую структуру данных мы
выбираем для представления трехмерных векторов, их распечатка нашей
функцией
print_vector
может восприниматься немного неуклюжей. На-
пример, если наши векторы представлены в виде кортежей или списков,
то во время их распечатки мы должны явным образом задавать индекс
каждого компонента:
>>> tuple_vec = (1, 0, 1)
>>> list_vec = [1, 0, 1]
>>> print_vector(tuple_vec[0],
tuple_vec[1],
tuple_vec[2])
<1, 0, 1>
3 .5 . Распаковка аргументов функции 97
Обычный вызов функции с отдельными аргументами кажется излиш-
не многословным и громоздким. Не лучше ли будет просто развернуть
векторный объект на три его компонента и передать их все одним разом
в функцию
print_vector
?
(Разумеется, вы могли бы просто переопределить функцию
print_vector
так, чтобы она принимала один-единственный параметр, представляющий
векторный объект, но ради того, чтобы иметь простой пример, мы этот
вариант пока проигнорируем.)
К счастью, в Python имеется более подходящий способ справиться с этой
ситуацией — при помощи распаковки аргументов функции с использова-
нием оператора
*
:
>>> print_vector(*tuple_vec)
<1, 0, 1>
>>> print_vector(*list_vec)
<1, 0, 1>
Размещение звездочки
*
перед итерируемым объектом в вызове функции
его распакует и передаст его элементы как отдельные позиционные аргу-
менты в вызванную функцию.
Этот прием работает для любого итерируемого объекта, включая выраже-
ния-генераторы. В результате использования оператора
*
с генератором
все поступающие из генератора элементы будут использованы и переданы
в функцию:
>>> genexpr = (x * x for x in range(3))
>>> print_vector(*genexpr)
Помимо оператора
*
для распаковки последовательностей, в частности
кортежей, списков и генераторов, в позиционные аргументы, также име-
ется оператор
**
для распаковки именованных аргументов, поступающих
из словарей. Предположим, что наш вектор был представлен в виде сле-
дующего объекта
dict
:
>>> dict_vec = {'y': 0, 'z': 1, 'x': 1}
Этот объект-словарь можно передать в функцию
print_vector
практиче-
ски таким же образом, использовав оператор
**
для распаковки:
98 Глава 3 • Эффективные функции
>>> print_vector(**dict_vec)
<1, 0, 1>
Поскольку словари не упорядочены, этот оператор соотносит значения
словаря и аргументы функции на основе ключей словаря: аргумент
x
получает значение, связанное в словаре с '
x
'.
Если для распаковки словаря использовать оператор одинарной звездоч-
ки (
*
), то вместо этого ключи будут переданы в функцию в произвольном
порядке:
>>> print_vector(*dict_vec)
Функциональное средство языка Python, связанное с распаковкой ар-
гументов функции, предоставляет вам большую гибкость за бесплатно.
Зачастую это означает, что вам не придется реализовывать класс для не-
обходимого вашей программе типа данных. При этом вполне достаточно
обойтись простыми встроенными структурами данных, подобными кор-
тежам или спискам, и, как результат, это поможет уменьшить сложность
вашего программного кода.
Ключевые выводы
Операторы
*
и
**
могут использоваться для «распаковки» аргументов
функции, поступающих из последовательностей и словарей.
Эффективное применение распаковки аргументов будет способство-
вать написанию более гибких интерфейсов для ваших модулей и функ-
ций.
3 .6 . Здесь нечего возвращать
В конец любой функции Python добавляет неявную инструкцию
return
None
. По этой причине, если в функции не указано возвращаемое значе-
ние, по умолчанию она возвращает
None
.
3 .6 . Здесь нечего возвращать 99
Это означает, что инструкции
return
None
можно заменять на пустые
инструкции
return
или даже пропускать их полностью и по-прежнему
получать тот же самый результат:
def foo1(value):
if value:
return value
else:
return None
def foo2(value):
Do'stlaringiz bilan baham: |