Более управляемые инструменты потока
Помимо оператора while, только что представленного, Python использует обычные операторы управления потоком, известные из других языков, с некоторыми нюансами.
4.1. if Операторы
Возможно, наиболее известный тип оператора — оператор if. Например:
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More
Может быть ноль или более elif частей, и часть else является необязательной. Ключевое слово ‘elif’ является сокращением от ‘else if’ и полезно для предотвращения чрезмерного отступа. Последовательность if … elif … elif … служит заменой операторам switch или case, встречающимся в других языках.
4.2. for Операторы
Оператор for в Python немного отличается от того, к чему вы могли привыкнуть в C или Pascal. Вместо того, чтобы всегда итерироваться по арифметической прогрессии чисел (как в Pascal) или давать пользователю возможность определять шаг итерации и условие останова (как в C), оператор Python for итерируется по элементам любого последовательности (списка или строки) в порядке их появления в последовательности. Например (без игры слов):
>>> # Measure some strings: ... words = ['cat', 'window', 'defenestrate'] >>> for w in words: ... print(w, len(w)) ... cat 3 window 6 defenestrate 12
Код, изменяющий коллекцию во время итерации по этой же коллекции, может быть сложным для корректного написания. Вместо этого, обычно проще перебирать копию коллекции или создавать новую коллекцию:
# Strategy: Iterate over a copy
for user, status in users.copy().items():
if status == 'inactive':
del users[user]
# Strategy: Create a new collection
active_users = {}
for user, status in users.items():
if status == 'active':
active_users[user] = status
4.3. Функция range()
Если вам нужно перебрать последовательность чисел, встроенная функция range() пригодится. Она генерирует арифметические прогрессии:
>>> for i in range(5): ... print(i) ... 0 1 2 3 4
Указанная конечная точка никогда не является частью генерируемой последовательности; range(10) генерирует 10 значений, являющихся допустимыми индексами для элементов последовательности длиной 10. Можно начать диапазон с другого числа или указать другой шаг (даже отрицательный; иногда это называется «шагом»):
>>> list(range(5, 10)) [5, 6, 7, 8, 9] >>> list(range(0, 10, 3)) [0, 3, 6, 9] >>> list(range(-10, -100, -30)) [-10, -40, -70]
Чтобы перебрать индексы последовательности, можно комбинировать range() и len() следующим образом:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i in range(len(a)): ... print(i, a[i]) ... 0 Mary 1 had 2 a 3 little 4 lamb
Однако в большинстве таких случаев удобнее использовать функцию enumerate(), см. Методы циклов.
Если просто напечатать диапазон, произойдёт странное событие:
>>> range(10) range(0, 10)
Во многих отношениях объект, возвращаемый функцией range(), ведет себя так, как будто это список, но на самом деле это не так. Это объект, который возвращает последующие элементы желаемой последовательности при итерации по нему, но фактически не создаёт список, тем самым экономя место.
Мы говорим, что такой объект является итерируемым, то есть подходящим в качестве цели для функций и конструкций, которые ожидают получения последовательных элементов до тех пор, пока запас не иссякнет. Мы видели, что оператор for является такой конструкцией, а примером функции, которая принимает итерируемый объект, является sum():
>>> sum(range(4)) # 0 + 1 + 2 + 3 6
Позже мы увидим больше функций, возвращающих итерируемые объекты и принимающих итерируемые объекты в качестве аргументов. В главе Структуры данных мы более подробно обсудим list().
4.4. break и continue операторы, и else предложения в циклах
Оператор break, как и в C, прерывает выполнение ближайшего вложенного цикла for или while.
Операторы цикла могут иметь else предложение; оно выполняется, когда цикл завершается исчерпанием итерируемого объекта (с for) или когда условие становится ложным (с while), но не когда цикл прерывается оператором break. Это иллюстрирует следующий цикл, в котором ищутся простые числа:
>>> for n in range(2, 10): ... for x in range(2, n): ... if n % x == 0: ... print(n, 'equals', x, '*', n//x) ... break ... else: ... # loop fell through without finding a factor ... print(n, 'is a prime number') ... 2 is a prime number 3 is a prime number 4 equals 2 * 2 5 is a prime number 6 equals 2 * 3 7 is a prime number 8 equals 2 * 4 9 equals 3 * 3
(Да, это правильный код. Внимательно посмотрите: предложение else принадлежит оператору цикла for, а не оператору if.)
При использовании с циклом предложение else больше похоже на предложение else оператора try, чем на предложение оператора if: предложение try оператора try выполняется, когда не возникает исключения, а предложение else оператора цикла выполняется, когда не возникает break. Более подробную информацию об операторе try и исключениях см. в разделе Обработка исключений.
Оператор continue, заимствованный из C, переходит к следующей итерации цикла:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found an odd number", num)
...
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9
4.5. pass Операторы
Оператор pass ничего не делает. Он может использоваться, когда синтаксически требуется оператор, но программа не требует действий. Например:
>>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C) ...
Это обычно используется для создания минимальных классов:
>>> class MyEmptyClass: ... pass ...
Еще одно место, где можно использовать pass, — это заполнитель для тела функции или условного оператора при работе над новым кодом, позволяя сохранить более абстрактное мышление. pass молча игнорируется:
>>> def initlog(*args): ... pass # Remember to implement this! ...
4.6. Определение функций
Мы можем создать функцию, которая записывает ряд Фибоначчи в произвольную границу:
>>> def fib(n): # write Fibonacci series up to n ... """Print a Fibonacci series up to n.""" ... a, b = 0, 1 ... while a < n: ... print(a, end=' ') ... a, b = b, a+b ... print() ... >>> # Now call the function we just defined: ... fib(2000) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
Ключевое слово def вводит определение функции. За ним должно следовать имя функции и заключенный в скобки список формальных параметров. Определяющие функцию операторы начинаются со следующей строки и должны быть отступом.
Первый оператор тела функции может быть строковым литералом; эта строковая литерал — это строка документации функции, или строка документации. (Подробнее о строках документации см. раздел Строки документации.) Существуют инструменты, которые используют строки документации для автоматического создания онлайн-или печатной документации, или для того, чтобы пользователь мог интерактивно просматривать код; это хорошая практика включать строки документации в написанный вами код, поэтому привыкайте к этому.
Выполнение функции создает новую таблицу символов, используемую для локальных переменных функции. Более точно, все присваивания переменных в функции сохраняют значение в локальной таблице символов; в то время как ссылки на переменные сначала ищут в локальной таблице символов, затем в локальных таблицах символов окружающих функций, затем в глобальной таблице символов и, наконец, в таблице встроенных имен. Таким образом, глобальные переменные и переменные окружающих функций не могут быть непосредственно присвоены значение внутри функции (если не указаны для глобальных переменных в операторе global, или для переменных окружающих функций в операторе nonlocal), хотя к ним можно обратиться.
Фактические параметры (аргументы) вызова функции вводятся в локальную таблицу символов вызываемой функции при ее вызове; таким образом, аргументы передаются с помощью передачи по значению (где значение всегда ссылка на объект, а не значение объекта). 1 Когда функция вызывает другую функцию или вызывает себя рекурсивно, для этого вызова создается новая локальная таблица символов.
Определение функции связывает имя функции с объектом функции в текущей таблице символов. Интерпретатор распознает объект, на который указывает это имя, как пользовательскую функцию. Другие имена также могут указывать на этот же объект функции и также могут использоваться для доступа к функции:
>>> fib <function fib at 10042ed0> >>> f = fib >>> f(100) 0 1 1 2 3 5 8 13 21 34 55 89
Приходя из других языков, вы можете возразить, что fib не функция, а процедура, так как она не возвращает значение. На самом деле, даже функции без оператора return возвращают значение, хотя и довольно скучное. Это значение называется None (это встроенное имя). Запись значения None обычно подавляется интерпретатором, если это единственное записанное значение. Вы можете увидеть его, если вы действительно хотите использовать print():
>>> fib(0) >>> print(fib(0)) None
Очень просто написать функцию, которая возвращает список чисел ряда Фибоначчи, а не печатает его:
>>> def fib2(n): # return Fibonacci series up to n ... """Return a list containing the Fibonacci series up to n.""" ... result = [] ... a, b = 0, 1 ... while a < n: ... result.append(a) # see below ... a, b = b, a+b ... return result ... >>> f100 = fib2(100) # call it >>> f100 # write the result [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Этот пример, как обычно, демонстрирует некоторые новые возможности Python:
- Оператор
returnвозвращает значение из функции.returnбез аргумента выражения возвращаетNone. Выход за пределы функции также возвращаетNone. - Оператор
result.append(a)вызывает метод объекта спискаresult. Метод — это функция, которая «принадлежит» объекту и называетсяobj.methodname, гдеobj— какой-то объект (это может быть выражение), аmethodname— имя метода, определенного типом объекта. Разные типы определяют разные методы. Методы разных типов могут иметь одинаковое имя без неоднозначности. (Можно определить собственные типы объектов и методы, используя классы, см. Классы) Методappend()в примере определен для объектов списка; он добавляет новый элемент в конец списка. В этом примере он эквивалентенresult = result + [a], но более эффективен.
4.7. Дополнительная информация о определении функций
Также можно определить функции с переменным числом аргументов. Существует три формы, которые могут быть объединены.
4.7.1. Значения аргументов по умолчанию
Самая полезная форма — это задание значения по умолчанию для одного или нескольких аргументов. Это создаёт функцию, которую можно вызывать с меньшим числом аргументов, чем определено, для разрешения. Например:
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
Эту функцию можно вызвать несколькими способами:
- указав только обязательный аргумент:
ask_ok('Do you really want to quit?') - указав один из необязательных аргументов:
ask_ok('OK to overwrite the file?', 2) - или даже указав все аргументы:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
Этот пример также демонстрирует ключевое слово in. Это проверяет, содержится ли определённое значение в последовательности.
Значения по умолчанию вычисляются в момент определения функции в области видимости определения, поэтому
i = 5
def f(arg=i):
print(arg)
i = 6
f()
выведет 5.
Важно: значение по умолчанию вычисляется только один раз. Это имеет значение, когда значение по умолчанию — это изменяемый объект, такой как список, словарь или экземпляры большинства классов. Например, следующая функция накапливает аргументы, передаваемые ей при последующих вызовах:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
Это выведет
[1] [1, 2] [1, 2, 3]
Если вы не хотите, чтобы значение по умолчанию делилось между последующими вызовами, вы можете переписать функцию следующим образом:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.7.2. Аргументы по ключевому слову
Функции также можно вызывать с помощью аргументов по ключевому слову в формате kwarg=value. Например, следующая функция:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
принимает один обязательный аргумент (voltage) и три необязательных аргумента (state, action, и type). Эту функцию можно вызвать любым из следующих способов:
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
но все следующие вызовы будут недействительными:
parrot() # required argument missing parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument parrot(110, voltage=220) # duplicate value for the same argument parrot(actor='John Cleese') # unknown keyword argument
При вызове функции аргументы по ключевому слову должны следовать за позиционными аргументами. Все передаваемые аргументы по ключевому слову должны соответствовать одному из аргументов, принимаемых функцией (например, actor — недействительный аргумент для функции parrot), а их порядок не имеет значения. Это также относится к необязательным аргументам (например, parrot(voltage=1000) тоже допустим). Один и тот же аргумент нельзя передавать более одного раза. Вот пример, который терпит неудачу из-за этого ограничения:
>>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: function() got multiple values for argument 'a'
Если присутствует конечный формальный параметр в формате **name, он получает словарь (см. Типы отображений — dict), содержащий все аргументы по ключевому слову, кроме тех, которые соответствуют формальному параметру. Это можно комбинировать с формальным параметром в формате *name (описано в следующем подразделе), который получает кортеж (кортеж) содержащий позиционные аргументы за пределами списка формальных параметров. (*name должен стоять перед **name.) Например, если мы определим функцию следующим образом:
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
Её можно вызвать так:
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
и, конечно же, она выведет:
-- Do you have any Limburger ? -- I'm sorry, we're all out of Limburger It's very runny, sir. It's really very, VERY runny, sir. ---------------------------------------- shopkeeper : Michael Palin client : John Cleese sketch : Cheese Shop Sketch
Обратите внимание, что порядок вывода аргументов по ключевому слову гарантированно соответствует порядку их предоставления при вызове функции.
4.7.3. Специальные параметры
По умолчанию аргументы могут передаваться в Python-функцию либо по позиции, либо явно по ключевому слову. Для читабельности и производительности имеет смысл ограничить способ передачи аргументов, чтобы разработчик мог определить способ передачи элементов, посмотрев только на определение функции: по позиции, по позиции или ключевому слову или по ключевому слову.
Определение функции может выглядеть так:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
где / и * — необязательные параметры. Если они используются, эти символы указывают на тип параметра в зависимости от того, как аргументы могут быть переданы в функцию: только по позиции, по позиции или ключевому слову, и только по ключевому слову. Аргументы по ключевому слову также называются именованными аргументами.
4.7.3.1. Аргументы по позиции или по ключевому слову
Если / и * отсутствуют в определении функции, аргументы могут передаваться в функцию по позиции или по ключевому слову.
4.7.3.2. Только позиционные параметры
Рассмотрим это немного подробнее. Можно пометить некоторые параметры как только позиционные. Если только позиционные, порядок параметров имеет значение, и параметры не могут передаваться по ключевому слову. Только позиционные параметры размещаются перед / (слеш). / используется для логического разделения только позиционных параметров от остальных параметров. Если в определении функции нет /, то только позиционных параметров нет.
Параметры, следующие за /, могут быть по позиции или ключевому слову или только по ключевому слову.
4.7.3.3. Только аргументы по ключевому слову
Чтобы пометить параметры как только по ключевому слову, указывая, что параметры должны передаваться в качестве аргументов по ключевому слову, поместите * в список аргументов непосредственно перед первым параметром только по ключевому слову.
4.7.3.4. Примеры функций
Рассмотрим следующие примеры определений функций, обращая особое внимание на маркеры / и *:
>>> def standard_arg(arg): ... print(arg) ... >>> def pos_only_arg(arg, /): ... print(arg) ... >>> def kwd_only_arg(*, arg): ... print(arg) ... >>> def combined_example(pos_only, /, standard, *, kwd_only): ... print(pos_only, standard, kwd_only)
В первом определении функции, standard_arg, самой распространённой форме, нет ограничений на соглашение о вызове и аргументы могут передаваться по позиции или по ключевому слову:
>>> standard_arg(2) 2 >>> standard_arg(arg=2) 2
Во второй функции pos_only_arg разрешены только позиционные параметры, так как в определении функции есть /:
>>> pos_only_arg(1) 1 >>> pos_only_arg(arg=1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments: 'arg'
В третьей функции kwd_only_args разрешены только аргументы по ключевому слову, как указано * в определении функции:
>>> kwd_only_arg(3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given >>> kwd_only_arg(arg=3) 3
А последняя использует все три соглашения о вызове в одном определении функции:
>>> combined_example(1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: combined_example() takes 2 positional arguments but 3 were given >>> combined_example(1, 2, kwd_only=3) 1 2 3 >>> combined_example(1, standard=2, kwd_only=3) 1 2 3 >>> combined_example(pos_only=1, standard=2, kwd_only=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: combined_example() got some positional-only arguments passed as keyword arguments: 'pos_only'
Наконец, рассмотрим это определение функции, в котором есть потенциальное столкновение между позиционным аргументом name и **kwds, в котором name используется в качестве ключа:
def foo(name, **kwds):
return 'name' in kwds
Нет возможного вызова, который вернёт True, так как ключевое слово 'name' всегда будет привязано к первому параметру. Например:
>>> foo(1, **{'name': 2})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo() got multiple values for argument 'name'
>>>
Но использование / (только позиционные аргументы) возможно, так как оно позволяет name в качестве позиционного аргумента и 'name' в качестве ключа в аргументах по ключевому слову:
def foo(name, /, **kwds):
return 'name' in kwds
>>> foo(1, **{'name': 2})
True
Другими словами, имена только позиционных параметров могут использоваться в **kwds без неоднозначности.
4.7.3.5. Резюме
Случай использования определит, какие параметры использовать в определении функции:
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
В качестве руководства:
- Используйте только позиционные, если вы хотите, чтобы имя параметров не было доступно пользователю. Это полезно, когда имена параметров не имеют реального значения, если вы хотите принудительно задать порядок аргументов при вызове функции, или если вам нужно взять некоторые позиционные параметры и произвольные ключевые слова.
- Используйте только по ключевому слову, когда имена имеют смысл, и определение функции более понятно, будучи явным с именами, или если вы хотите предотвратить полагание пользователей на позицию передаваемого аргумента.
- Для API используйте только позиционные, чтобы предотвратить разрыв API-изменений, если имя параметра будет изменено в будущем.
4.7.4. Произвольные списки аргументов
Наконец, наименее часто используемый вариант — указать, что функция может вызываться с произвольным числом аргументов. Эти аргументы будут обернуты в кортеж (см. Кортежи и последовательности). Перед переменным числом аргументов может стоять ноль или более обычных аргументов.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
Обычно эти аргументы с переменным числом параметров будут последними в списке формальных параметров, потому что они охватывают все оставшиеся входные аргументы, передаваемые в функцию. Любые формальные параметры, следующие за параметром *args, являются аргументами только по ключевому слову, то есть они могут использоваться только как ключевые слова, а не как позиционные аргументы.
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
4.7.5. Распаковка списков аргументов
Обратная ситуация возникает, когда аргументы уже находятся в списке или кортеже, но нужно их распаковать для вызова функции, требующей отдельных позиционных аргументов. Например, встроенная функция range() ожидает отдельные аргументы start и stop. Если они недоступны по отдельности, напишите вызов функции с оператором *, чтобы распаковать аргументы из списка или кортежа:
>>> list(range(3, 6)) # normal call with separate arguments [3, 4, 5] >>> args = [3, 6] >>> list(range(*args)) # call with arguments unpacked from a list [3, 4, 5]
Аналогичным образом словари могут передавать аргументы по ключевому слову с помощью оператора **:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
4.7.6. Выражения lambda
Небольшие анонимные функции могут быть созданы с помощью ключевого слова lambda. Эта функция возвращает сумму своих двух аргументов: lambda a, b: a+b. Lambda-функции могут быть использованы везде, где требуются объекты функций. Они синтаксически ограничены одним выражением. Семантически они представляют собой всего лишь синтаксический сахар для обычного определения функции. Как и вложенные определения функций, lambda-функции могут ссылаться на переменные из содержащей области видимости:
>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 43
В приведенном выше примере используется выражение lambda для возврата функции. Другое использование — передача небольшой функции в качестве аргумента:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] >>> pairs.sort(key=lambda pair: pair[1]) >>> pairs [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
4.7.7. Строки документации
Вот некоторые соглашения о содержании и форматировании строк документации.
Первая строка всегда должна быть кратким и лаконичным описанием назначения объекта. Для краткости она не должна явно указывать имя или тип объекта, так как они доступны другими способами (за исключением случаев, когда имя оказывается глаголом, описывающим действие функции). Эта строка должна начинаться с заглавной буквы и заканчиваться точкой.
Если в строке документации есть еще строки, вторая строка должна быть пустой, визуально отделяя резюме от остального описания. Следующие строки должны представлять собой один или несколько абзацев, описывающих вызов объекта, его побочные эффекты и т. д.
Парсер Python не удаляет отступы из многострочных строковых литералов в Python, поэтому инструменты, обрабатывающие документацию, должны удалять отступы при необходимости. Это делается с помощью следующего соглашения. Первая непустая строка после первой строки строки определяет величину отступа для всей строки документации. (Мы не можем использовать первую строку, поскольку она обычно прилегает к открывающим кавычкам строки, поэтому ее отступ не виден в строковом литерале). Пробелы, «эквивалентные» этому отступу, затем удаляются из начала всех строк строки. Строки, отступ которых меньше, не должны встречаться, но если они встречаются, все их начальные пробелы должны быть удалены. Эквивалентность пробелов должна проверяться после расширения табуляции (обычно до 8 пробелов).
Вот пример многострочной строки документации:
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
4.7.8. Аннотации функций
Аннотации функций являются полностью необязательной метаданными о типах, используемых пользовательскими функциями (см. PEP 3107 и PEP 484 для получения дополнительной информации).
Аннотации хранятся в атрибуте __annotations__ функции в виде словаря и не оказывают никакого влияния на другую часть функции. Аннотации параметров определяются двоеточием после имени параметра, за которым следует выражение, вычисляющее значение аннотации. Аннотации возвращаемых значений определяются буквальным ->, за которым следует выражение, между списком параметров и двоеточием, обозначающим конец def оператора. В следующем примере есть обязательный аргумент, необязательный аргумент и аннотированное возвращаемое значение:
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
4.8. Интермедия: Стиль кодирования
Теперь, когда вы собираетесь писать более длинные и сложные фрагменты кода Python, самое время поговорить о стиле кодирования. Большинство языков можно написать (или, точнее, форматировать) в разных стилях; некоторые более читабельны, чем другие. Сделать код читаемым для других всегда хорошая идея, и принятие приятного стиля кодирования очень помогает в этом.
Для Python PEP 8 стал руководством по стилю, которому следуют большинство проектов; оно способствует очень удобочитаемому и приятному стилю кодирования. Каждый разработчик Python должен его прочитать в какой-то момент; вот самые важные моменты, извлеченные для вас:
-
Используйте 4 пробела для отступа и не используйте табуляцию.
4 пробела — хороший компромисс между небольшим отступом (позволяет большую глубину вложенности) и большим отступом (легче читать). Табуляции вводят путаницу, и лучше от них отказаться.
-
Обрезайте строки так, чтобы они не превышали 79 символов.
Это помогает пользователям с маленькими экранами и позволяет иметь несколько файлов кода рядом на больших экранах.
- Используйте пустые строки для разделения функций и классов, а также больших блоков кода внутри функций.
- По возможности помещайте комментарии на отдельной строке.
- Используйте строки документации.
- Используйте пробелы вокруг операторов и после запятых, но не непосредственно внутри скобочных конструкций:
a = f(1, 2) + g(3, 4). - Называйте классы и функции последовательно; соглашение состоит в использовании
UpperCamelCaseдля классов иlowercase_with_underscoresдля функций и методов. Всегда используйтеselfв качестве имени первого аргумента метода (см. Первый взгляд на классы для получения дополнительной информации о классах и методах). - Не используйте сложные кодировки, если ваш код предназначен для использования в международных средах. По умолчанию Python, UTF-8 или даже обычный ASCII работают лучше в любом случае.
- Аналогично, не используйте символы, отличные от ASCII, в идентификаторах, если есть малейшая вероятность того, что люди, говорящие на другом языке, будут читать или поддерживать код.
Примечания
-
1 -
На самом деле, вызов по ссылке на объект было бы лучшим описанием, поскольку, если передается изменяемый объект, вызывающая сторона увидит любые изменения, которые делает вызываемая функция (элементы, добавленные в список).
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/tutorial/controlflow.html