Самые основы
Пресмыкающийся
калькулятор
Использование Python для простейших вычислений
Давайте опробуем несколько простых команд Python. Не хочется в первый же день вас перегружать – рассмотрим только базис, который потом попробуем использовать для написания первой программы. Запустите интерпретатор и дождитесь появления основного приглашения — >>>. (Это не должно занять много времени.)
Числа
Поведение интерпретатора сходно поведению калькулятора: вы вводите выражение, а в ответ он выводит значение. Синтаксис выражений прямолинеен: операторы +, -, * и / работают также как и в большинстве других языков (например, Паскале или C); для группировки можно использовать скобки. Например:
>>> 2+2
4
>>> # Это комментарий
... 2+2
4
>>> 2+2
# а вот комментарий на одной строке с кодом
4
>>> (50-5*6)/4
5
>>> # Деление целых чисел возвращает округленное к минимальному значение:
... 7/3
2
>>> 7/-3
-3
Знак равенства ('=') используется для присваивания переменной какого-либо значения. После этого действия в интерактивном режиме ничего не выводится:
>>> width = 20
>>> height = 5*9
>>> width * height
900
Значение может быть присвоено нескольким переменным одновременно:
>>> x = y = z = 0 # Нулевые x, y и z
>>> x
0
>>> y
0
>>> z
0
Переменные должны быть определены (defined) (должны иметь присвоенное значение) перед использованием, иначе будет сгенерирована ошибка:
>>> # попытка получить
доступ к неопределённой переменной
... n
Traceback (most recent call last):
File "<stdin>",
line
NameError: name 'n' is not defined
Присутствует полная поддержка операций с плавающей точкой; операторы с операндами смешанного типа конвертируют целочисленный операнд в число с плавающей точкой:
>>> 3 * 3.75 / 1.5
7.5
>>> 7.0 / 2
3.5
Также поддерживаются комплексные числа; к мнимым частям добавляется суффикс j или J. Комплексные числа с ненулевым вещественным компонентом записываются в виде (<вещественная_часть>+<мнимая_часть>j), или могут быть созданы с помощью функции complex(<вещественная_часть>, <мнимая_часть>).
>>> 1j * 1J
(-1+0j)
>>> 1j * complex(0, 1)
(-1+0j)
>>> 3+1j*3
(3+3j)
>>> (3+1j)*3
(9+3j)
>>> (1+2j)/(1+1j)
(1.5+0.5j)
Комплексные числа всегда представлены в виде двух чисел с плавающей точкой - вещественной и мнимой частями. Для получения этих частей из комплексного числа z используйте z.real и z.imag.
>>> a=1.5+0.5j
>>> a.real
1.5
>>> a.imag
0.5
Функции конвертации (приведения) к вещественным и целым числам (float(), int() и long()) не работают с комплексными числами — нет единственно правильного способа сконвертировать комплексное число в вещественное. Используйте функцию abs(z) чтобы получить модуль числа (в виде числа с плавающей точкой) или z.real чтобы получить его вещественную часть:
>>> a=3.0+4.0j
>>> float(a)
Traceback (most recent call last):
File "<stdin>",
line
TypeError: can`t convert complex to float; use abs(z)
>>> a.real
3.0
>>> a.imag
4.0
>>> abs(a) # sqrt(a.real**2 + a.imag**2)
5.0
>>>
В интерактивном режиме последнее выведенное выражение сохраняется в переменной _. Это значит, что если вы используете Python в качестве настольного калькулятора — всегда есть способ продолжить вычисления с меньшими усилиями, например:
>>> tax = 12.5 / 100
>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)
113.06
>>>
Эта переменная для пользователя должна иметь статус только для чтения. Не навязывайте ей значение собственноручно — вы создадите независимую переменную с таким же именем, скрывающую встроенную переменную вместе с её магическими свойствами.
Строки
Помимо чисел, Python может работать со строками, которые, в свою очередь, могут быть описаны различными способами. Строки могут быть заключены в одинарные или двойные кавычки:
>>> 'spam eggs'
'spam eggs'
>>> 'doesn\'t'
"doesn't"
>>> "doesn't"
"doesn't"
>>> '"Yes," he said.'
'"Yes," he said.'
>>> "\"Yes,\" he said."
'"Yes," he said.'
>>> '"Isn\'t," she said.'
'"Isn\'t," she said.'
Строковые литералы могут быть разнесены на несколько строк различными способами. Могут быть использованы продолжающие строки, с обратным слэшем в качестве последнего символа строки, сообщающим о том, что следующая строка есть продолжение текущей[8]:
hello = "This is a rather long string containing\n\
several lines of text just as you would do in C.\n\
Note that whitespace at the beginning of the line is\
significant."
print hello
Обратите внимание, что новые строки все ещё нужно подключать в строку
через \n; новая строка,
за которой следут обратный слэш
— не обрабатывается. Запуск примера выведет следующее:
This is a rather long string containing
several lines of text just as you would do in C.
Note that whitespace at the beginning of the line is significant.
Если мы объявим строковой литерал сырым (raw) — символы \n не будут конвертированы в новые
строки, но и обратный слэш в конце строки, и символ
новой строки в исходном коде — будут добавлены в строку в виде данных.
Следовательно, код из
примера:
hello = r"This is a rather long string containing\n\
several lines of text much as you would do in C."
print hello
выведет:
This is a rather long string containing\n\ several lines of text much as you would do in C.
Или, строки могут быть обрамлены совпадающей парой тройных кавычек: """ или '''. Окончания каждой строчки не нужно завершать тройными кавычками. В примере ниже строки, начиющиеся с дефиса также будут включены в строку.
print """
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
"""
выводит в результате следующее:
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
Интерпретатор выводит результаты операций над строками тем же способом, каким они были введены: в кавычках, а также в кавычках и с другими забавными символами, экранированными обратными слэшами — для того, чтобы показать точное значение. Строка заключается в двойные кавычки если строка содержит одинарную кавычку и ни одной двойной, иначе она заключается в одинарные кавычки. (Оператор print, описанный позже, может использоваться для вывода строк без кавычек или экранирования.)
Строки могут конкатенироваться (склеиваться вместе) оператором + и быть повторенными оператором *:
>>> word = 'Help' + 'A'
>>> word
'HelpA'
>>> '<' + word*5 + '>'
'<HelpAHelpAHelpAHelpAHelpA>'
Два строковых литерала, расположенные друг за другом, автоматически конкатенируются; первая строка в предыдущем примере также могла быть записана как word = 'Help' 'A'; это работает только с двумя литералами — не с произвольными выражениями, содержащими строки.
>>> 'str' 'ing' # <-
Так — верно
'string'
>>> 'str'.strip() + 'ing' # <-
Так — верно
'string'
>>> 'str'.strip() 'ing' # <-
Так — не верно
File
"<stdin>", line
'str'.strip() 'ing'
^
SyntaxError: invalid syntax
Строки могут быть проиндексированы; также как и в C, первый символ строки имеет индекс 0. Отсутствует отдельный тип для символов; символ является строкой с единичной длиной. Подобно языку Icon, подстроки могут определены через нотацию срезов (slice): два индекса, разделенных двоеточием.
>>> word = 'Help' + 'A'
>>> word[4]
'A'
>>> word[0:2]
'He'
>>> word[2:4]
'lp'
Индексы срезов имеют полезные значения по умолчанию; опущенный первый индекс заменяется нулём, опущенный второй индекс подменяется размером срезаемой строки.
>>> word[:2] # Первые два символа
'He'
>>> word[2:] # Всё, исключая первые два
символа
'lpA'
В отличие от строк в C, строки Python не могут быть изменены. Присваивание по позиции индекса строки вызывает ошибку:
>>> word[0] = 'x'
Traceback (most recent call last):
File
"<stdin>", line
TypeError: object doesn't support item assignment
>>> word[:1] = 'Splat'
Traceback (most recent call last):
File "<stdin>",
line
TypeError: object doesn't support slice assignment
Тем не менее, создание новой строки со смешанным содержимым — процесс легкий и очевидный:
>>> 'x' + word[1:]
'xelpA'
>>> 'Splat' + word[4]
'SplatA'
Полезный инвариант операции среза: s[:i] + s[i:] эквивалентно s.
>>> word[:2] + word[2:]
'HelpA'
>>> word[:3] + word[3:]
'HelpA'
Вырождения индексов срезов обрабатываются элегантно: чересчур большой индекс заменяется на размер строки, а верхняя граница меньшая нижней возвращает пустую строку.
>>> word[1:100]
'elpA'
>>> word[10:]
''
>>> word[2:1]
''
Индексы могут быть отрицательными числами, обозначая при этом отсчет справа налево. Например:
>>> word[-1] # Последний символ
'A'
>>> word[-2] # Предпоследний символ
'p'
>>> word[-2:] # Последние два символа
'pA'
>>> word[:-2] # Всё, кроме последних двух
символов
'Hel'
Но обратите внимание, что -0 действительно эквивалентен 0 - это не отсчет справа.
>>> word[-0] # (поскольку -0 равен 0)
'H'
Отрицательные индексы вне диапазона обрезаются, но не советуем делать это с одно-элементными индексами (не-срезами):
>>> word[-100:]
'HelpA'
>>> word[-10] # ошибка
Traceback (most recent call last):
File "<stdin>",
line
IndexError: string index out of range
Один из способов запомнить, как работают срезы - думать о них, как об указателях на места между символами, где левый край первого символа установлен в ноль, а правый край последнего символа строки из n символов имеет индекс n, например:
+---+---+---+---+---+
| H | e | l | p | A |
+---+---+---+---+---+
0
1 2 3
4 5
-5 -4 -3
-2 -1
Первый ряд чисел дает позицию индексов строки от 0 до 5; второй ряд описывает соответствующие отрицательные индексы. Срез от i до j состоит из всех символов между правым и левым краями, отмеченными, соответственно, через i и j.
Для всех не-негативных индексов длина среза - разность между индексами, при условии что оба индекса находятся в диапазоне. Например, длина word[1:3] — 2.
Встроенная функция len() возвращает длину строки:
>>> s = 'supercalifragilisticexpialidocious'
>>> len(s)
34
Строки, а также строки в Unicode, которые описаны далее — примеры перечисляемых типов и поддерживают привычные для этих типов операции.
Строки Unicode
Начиная с Python версии 2.0 программисту стал доступен новый тип данных для хранения текста: объект Unicode. Он может быть использован для хранения и управления данными в Unicode (см. http://www.unicode.org/) и хорошо интегрируется с существующими строковыми объектами, обеспечивая автоматическую конверсию в тех случаях, когда это необходимо.
Преимущество набора Unicode состоит в том, что он предоставляет порядковый номер для любого символа из любой письменности, использовавшейся в современных или древнейших текстах. До этих пор, для символов в сценарии было доступно лишь 256 номеров. Тексты обычно привязывались к кодовой странице, которая устанавливала в соответствие порядковые номера и символы сценария. Это приводило к серьезным путаницам, особенно в том, что касалось интернационализации программного продукта. Unicode решает эти проблемы определяя единую кодовую страницу для всех письменностей.
Создание в Python строк в Unicode является действием настолько же простым, насколько простым является и создание обычных строк:
>>> u'Hello World !'
u'Hello World !'
Строчная буква "u" перед кавычкой указывает на необходимость создания Unicode-строки. Если вы хотите включить в строку специальные символы — вы можете сделать это используя кодирование Unicode-Экранированием Python (Python Unicode-Escape encoding). Следующий пример поясняет как:
>>> u'Hello\u0020World !'
u'Hello World !'
Экранированная последовательность \u0020 указывает на позицию, куда требуется вставить символ Unicode с порядковым номером 0x0020 (символ пробела).
Другие символы интерпретируются с использованием соответствующих им порядковых значений тем же способом, что и порядковые номера Unicode. Если у вас есть буквенные строки в стандартной кодировке Latin-1, использующейся во многих западных странах — вы найдёте удобным то, что первые 256 символов кодировки Unicode полностью совпадают с 256 символами кодировки Latin-1.
Для экспертов присутствует также сырой режим — такой же, как и для обычных строк. Вам требуется поставить перед открывающей кавычкой префикс "ur", чтобы Python использовал кодирование Сырым-Unicode-Экранированием (Raw-Unicode-Escape encoding). Описанная выше конверсия \uXXXX будет применена только при условии наличия перед строчной "u" нечётного количества обратных слэшей.
>>>
u'Hello World !'
>>>
u'Hello\\\\u0020World !'
Сырой режим наиболее полезен, если вам необходимо ввести множество обратных слэшей — это может потребоваться в случае использования регулярных выражений.
Помимо стандартных способов кодирования, Python предоставляет целый набор различных способов создания Unicode-строк основываясь на известной кодировке.
Встроенная функция unicode() предоставляет доступ ко всем зарегистрированным кодекам (кодировщикам и декодировщикам) Unicode. Наиболее известными кодировками, которые могут использовать эти кодеки являются Latin-1, ASCII, UTF-8 и UTF-16. Последние две — кодировки с различными длинами элементов — они могут хранить каждый символ Unicode в одном или более байтах. Кодировкой по умолчанию установлена ASCII, принимающая все символы в диапазоне от 0 до 127 и отвергающая любые другие символы сообщением об ошибке. В случае, если Unicode-строка выводится на печать, пишется в файл или конвертируется функцией str() — конвертирование производится с использованием кодировки по умолчанию.
>>> u"abc"
u'abc'
>>> str(u"abc")
'abc'
>>> u"äöü"
u'\xe4\xf6\xfc'
>>> str(u"äöü")
Traceback (most recent call last):
File "<stdin>",
line
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
Для конвертирования Unicode-строки в 8-битовую строку с использованием желаемой кодировки, объекты Unicode предоставляют метод encode(), принимающий единственный аргумент — название кодировки. Предпочитаются названия записанные в нижнем регистре.
>>> u"äöü".encode('utf-8')
'\xc3\xa4\xc3\xb6\xc3\xbc'
Если у вас есть в наличии данные в некоторой кодировке, а вы хотите создать на их основе соответствующую строку в Unicode — вы можете использовать функцию unicode(), передав название кодировки вторым аргументом.
>>> unicode('\xc3\xa4\xc3\xb6\xc3\xbc', 'utf-8')
u'\xe4\xf6\xfc'
Списки
В языке Python доступно некоторое количество составных типов данных, использующихся для группировки прочих значений вместе. Наиболее гибкий из них — список (list); его можно выразить через разделённые запятыми значения (элементы), заключённые в квадратные скобки. Элементы списка могут быть разных типов.
>>> a = ['spam', 'eggs',
100, 1234]
>>> a
['spam',
'eggs', 100,
1234]
Подобно индексам в строках, индексы списков начинаются с нуля, списки могут быть срезаны, объединены (конкатенированы) и так далее:
>>> a[0]
'spam'
>>> a[3]
1234
>>> a[-2]
100
>>> a[1:-1]
['eggs', 100]
>>> a[:2] + ['bacon', 2*2]
['spam', 'eggs', 'bacon', 4]
>>> 3*a[:3] + ['Boo!']
['spam', 'eggs', 100, 'spam', 'eggs', 100, 'spam', 'eggs', 100, 'Boo!']
В отличие от строк, являющихся неизменяемыми, изменить индивидуальные элементы списка вполне возможно:
>>> a
['spam', 'eggs', 100, 1234]
>>> a[2] = a[2] + 23
>>> a
['spam',
'eggs', 123,
1234]
Присваивание срезу также возможно, и это действие может даже изменить размер списка или полностью его очистить:
>>> # Заменим некоторые элементы:
... a[0:2]
= [1, 12]
>>> a
[1, 12, 123, 1234]
>>> # Удалим немного:
... a[0:2]
= []
>>> a
[123, 1234]
>>> # Вставим пару:
... a[1:1] = ['bletch', 'xyzzy']
>>> a
[123, 'bletch', 'xyzzy', 1234]
>>> # Вставим (копию) самого себя в начало
>>> a[:0] = a
>>> a
[123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234]
>>> # Очистка списка: замена всех значений пустым списком
>>> a[:] = []
>>> a
[]
Встроенная функция len() также применима к спискам:
>>> a = ['a', 'b', 'c', 'd']
>>> len(a)
4
Вы можете встраивать списки (создавать списки, содержащие другие списки), например так:
>>> q = [2, 3]
>>> p = [1, q,
4]
>>> len(p)
3
>>> p[1]
[2, 3]
>>> p[1][0]
2
Вы можете добавить что-нибудь в конец списка.
>>> p[1].append('xtra') #
Смотрите раздел Подробнее о списках
>>> p
[1, [2, 3, 'xtra'], 4]
>>> q
[2, 3, 'xtra']
Обратите внимание, что в последнем примере p[1] и q на самом деле ссылаются на один и тот же объект! Мы вернёмся к семантике объектов позже.
Первые шаги к программированию
Безусловно, Python можно использовать для более сложных задач, чем сложение двух чисел. Например, мы можем вывести начальную последовательность чисел Фибоначчи таким образом:
>>> # Ряд Фибоначчи:
... # сумма двух элементов определяет следущий
элемент
... a, b = 0, 1
>>> while b < 10:
... print b
... a, b = b, a+b
...
1
1
2
3
5
8
Этот пример представляет нам некоторые новые возможности.
>>> i = 256*256
>>> print 'Значением i является', i
Значением i является 65536
Замыкающая строку запятая отменяет перевод строки после вывода:
>>> a, b = 0, 1
>>> while b < 1000:
... print b,
... a, b = b, a+b
...
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
Обратите внимание на то, что интерпретатор вставляет новую строку перед тем как выводит следующее приглашение, если последняя строка не была завершена.
Дополнительные средства управления порядком
выполнения
Помимо описанного выше оператора while, в Python доступны привычные операторы управления из других языков, но с некоторыми различиями.
Оператор if
Возможно наиболее широко известный тип выражения — оператор if. Пример:
>>> x = int(raw_input("Введите, пожалуйста, целое число: "))
Введите, пожалуйста, целое число: 42
>>> if x < 0:
... x = 0
... print 'Отрицательное значение изменено на ноль'
... elif x == 0:
... print 'Ноль'
... elif x == 1:
... print 'Единица'
... else:
... print 'Больше'
...
Больше
Блоков elif может быть ноль или больше, а блок else не обязателен. Ключевое слово „elif“ — краткая запись „else if“ — позволяет избавиться от чрезмерных отступов. Последовательность if ... elif ... elif ... — замена операторам switch или case, которые можно встретить в других языках.
Оператор for
Оператор for в Python немного отличается от того, какой вы, возможно, использовали в C или Pascal. Вместо неизменного прохождения по арифметической прогрессии из чисел (как в Pascal) или предоставления пользователю возможности указать шаг итерации и условие останова (как в С), оператор for в Python проходит по всем элементам любой последовательности (списка или строки) в том порядке, в котором они в ней располагаются. Например
>>> # Измерим несколько строк:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
... print x, len(x)
...
cat 3
window 6
defenestrate 12
Изменять содержимое последовательности, по которой проходит цикл, небезопасно (это можно сделать только для изменяемых типов последовательностей, таких как списки). Если вам нужно модифицировать список, использующийся для прохода по нему в цикле (например, для того чтобы продублировать выборочные элементы), вам нужно передать циклу его копию. Нотация срезов делает это практически безболезненным:
>>> for x in a[:]: # создать срез-копию всего списка
... if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']
Функция range()
Если вам нужно перебрать последовательность чисел, встроенная функция range() придёт на помощь. Она генерирует списки, содержащие арифметические прогрессии:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Указанная точка завершения никогда не оказывается частью сгенерированного списка; вызов range(10) генерирует список из десяти значений — потенциальные индексы для элементов последовательности длиной 10. Позволено установить начало списка на другое число или указать другую величину инкремента (даже негативную; иногда её называют шагом (step)).
>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> 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
3 little
4 lamb
Однако в большинстве таких случаев удобно использовать функцию enumerate(). Обратитесь к техникам проходов циклом.
Операторы break и continue, а также условие else в циклах
Оператор break, как и в C, прерывает выполнение ближайшего заключающего цикла for или while. Оператор continue, также заимствованный из C, продолжает выполнение цикла со следующей итерации.
Операторы циклов могут иметь ветвь else; она исполняется, когда цикл прекращает сквозной анализ списка (в случае for) или когда условие становится ложным (в случае while), но не в тех случаях, когда цикл прерывается по break. Это поведение иллюстрируется следующим примером, в котором производится поиск простых чисел:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print n, 'равно', x, '*', n//x
... break
... else:
... # циклу не удалось найти
множитель
... print n, '- простое число'
...
2 - простое число
3 - простое число
4 равно 2 * 2
5 - простое число
6 равно 2 * 3
7 - простое число
8 равно 2 * 4
9 равно 3 * 3
Оператор pass
Оператор pass не делает ничего. Он может использоваться тогда, когда синтаксически требуется присутствие оператора, но от программы не требуется действий. Например:
>>> while True:
... pass # Ожидание прерывания c клавиатуры (Ctrl+C) в режиме занятости
...
Этот оператор часто используется для создания минималистичных классов, к примеру исключений (exceptions), или для игнорирования нежелательных исключений:
>>> class ParserError(Exception):
... pass
...
>>> try:
... import audioop
... except ImportError:
... pass
...
Другой вариант: pass может применяться в качестве заглушки для тела функции или условия при создании нового кода, позволяя вам сохранить абстрактный взгяд на вещи. С другой стороны, оператор pass игнорируется без каких-либо сигналов, и лучшим выбором было бы породить исключение NotImplementedError:
>>> def initlog(*args):
... raise NotImplementedError # Открыть файл для логгинга, если он ещё не открыт
... if not logfp:
... raise NotImplementedError # Настроить заглушку для логгинга
... raise NotImplementedError('Обработчик инициализации лога вызовов')
...
Если бы здесь использовались операторы pass, а позже вы бы запускали тесты, они могли бы упасть без указания причины. Использование NotImplementedError принуждает этот код породить исключение, сообщая вам конкретное место, где присутствует незавершённый код. Обратите внимание на два способа порождения исключений. Первый способ — без сообщения, но сопровождаемый комментарием, позволяет вам оставить комментарий, когда вы будете подменять выброс исключения рабочим кодом, который, в свою очередь (в идеале) будет хорошим описанием блока кода, для которого исключение предназначалось заглушкой. Однако передача сообщения вместе с исключением, как в третьем примере, обуславливает более насыщенный информацией вывод при отслеживании ошибки.
Определение функций
Мы можем создать функцию, которая выводит числа Фибоначчи до некоторого предела:
>>> def fib(n): #
вывести числа Фибоначчи меньшие (вплоть до) n
... """Выводит ряд
Фибоначчи, ограниченный n."""
... a, b = 0, 1
... while b < n:
... print b,
... a, b = b, a+b
...
>>> # Теперь вызовем определенную нами функцию:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
1597
Зарезервированное слово def предваряет определение функции. За ним должны следовать имя функции и заключённый в скобки список формальных параметров. Выражения, формирующие тело функции, начинаются со следующей строки и должны иметь отступ.
Первым выражением в теле функции может быть строковой литерал — этот литерал является строкой документации функции, или док-строкой (docstring). (Больше информации о док-строках вы найдёте в разделе Строки документации) Существуют инструменты, которые используют док-строки для того, чтобы сгенерировать печатную или онлайн-документацию или чтобы позволить пользователю перемещаться по коду интерактивно; добавление строк документации в ваш код — это хорошая практика, постарайтесь к ней привыкнуть.
Исполнение функции приводит к созданию новой таблицы символов, использующейся для хранения локальных переменных функции. Если быть более точными, все присваивания переменных в функции сохраняют значение в локальной таблице символов; при обнаружении ссылки на переменную, в первую очередь просматривается локальная таблица символов, затем локальная таблица символов для окружающих функций, затем глобальная таблица символов и, наконец, таблица встроенных имён. Таким образом, глобальным переменным невозможно прямо присвоить значения внутри функций (до тех пор, пока они поименованы в глобальном выражении) несмотря на то, что ссылки на них могут использоваться.
Фактические параметры (аргументы) при вызове функции помещаются в локальную таблицу символов вызванной функции; в результате аргументы передаются через вызов по значению (call by value) (где значение — это всегда ссылка (reference) на объект, а не значение его самого)[17]. Если одна функция вызывает другую — то для этого вызова создается новая локальная таблица символов.
При определении функции её имя также помещается в текущую таблицу символов. Тип значения, связанного с именем функции, распознается интерпретатором как функция, определённая пользователем (user-defined function). Само значение может быть присвоено другому имени, которое затем может также использоваться в качестве функции. Эта система работает в виде основного механизма переименования:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
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): # вернуть числа Фибоначчи меньшие (вплоть до) n
... """Возвращает
список чисел ряда Фибоначчи, ограниченный n."""
... result = []
... a, b = 0, 1
... while b < n:
... result.append(b) # см. ниже
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # вызываем
>>> f100 # выводим
результат
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Этот пример, как уже привычно, демонстрирует некоторые новые возможности Python:
Подробнее об определении функций
Также есть возможность определять функции с переменным количеством аргументов. Для этого существует три формы, которые также можно использовать совместно.
Значения аргументов по умолчанию
Наиболее полезная форма — указать значение по умолчанию для одного или
более аргументов. Таким образом создаётся функция, которая может быть вызвана с
меньшим количеством аргументов чем в её определении, и корректно их принять. Например:
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = raw_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 IOError, 'refusenik user'
print complaint
Эта функция может быть вызвана,
например, так: ask_ok('Do you really want to quit?') или
так: ask_ok('OK to
overwrite the file?', 2).
Этот пример также знакомит вас с зарезервированным словом in. Посредством его можно проверить, содержит или нет последовательность определённое значение.
Значения по умолчанию вычисляются в точке определения функции — в определяющей области, поэтому код
i = 5
def f(arg=i):
print arg
i = 6
f()
выведет 5.
Важное предупреждение: Значение по умолчанию вычисляется лишь единожды. Это имеет вес когда значением по умолчанию является изменяемый объект, такой как список, словарь (dictionary) или экземляры большинства классов. Например, следующая функция накапливает переданные ей аргументы с последовательными вызовами:
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
Именованные аргументы
Функции также могут быть вызваны с использованием именованных аргументов
(keyword arguments)
в форме "имя = значение". Например, нижеприведённая функция:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print "-- This parrot wouldn't", action,
print "if you put", voltage, "volts through it."
print "-- Lovely plumage, the", type
print "-- It's", state, "!"
могла бы быть вызвана любым из следующих способов[21]:
parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')
а эти случаи оказались бы неверными[22]:
parrot() # пропущен требуемый
аргумент
parrot(voltage=5.0, 'dead') #
неименованный аргумент следом за именованным
parrot(110, voltage=220) #
повторное значение аргумента
parrot(actor='John Cleese') # неизвестное имя аргумента
(Любознательному читателю может оказаться интересным попытаться перевести этот пример, взятый из скетча Монти Пайтона, на русский язык).
В общем случае, список аргументов должен содержать любое количество позиционных (positional) аргументов, за которыми должно следовать любое количество именованных аргументов, и при этом имена аргументов выбираются из формальных параметров. Неважно, имеет формальный параметр значение по умолчанию или нет. Ни один из аргументов не может получать значение более чем один раз — имена формальных параметров, совпадающие с именами позиционных аргументов, не могут использоваться в качестве именующих в одном и том же вызове[23]. Вот пример, завершающийся неудачей по причине описанного ограничения:
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>",
line
TypeError: function() got multiple values for keyword argument 'a'
Если в определении функции присутствует завершающий параметр в виде **имя — он принимает словарь (подробнее в разделе Справочника — Маппинг типов — словари), содержащий все именованные аргументы, исключая те, которые соответствуют формальным параметрам. Можно совместить эту особенность с поддержкой формального параметра в формате *имя (описывается в следующем подразделе), который получает кортеж (tuple), содержащий все позиционные аргументы, следующие за списком формальных параметров. (параметр в формате *имя должен описываться перед параметром в формате **имя.) Например, если мы определим такую функцию:
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
keys = keywords.keys()
keys.sort()
for kw in keys: print kw, ":", keywords[kw]
то её можно будет вызвать так[25]:
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
и она, конечно же, выведет[26]:
-- 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.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
Обратите внимание, что метод sort() списка имён (ключей) поименованных аргументов (keys) вызывается перед выводом содержимого словаря keywords; если бы этого не было сделано, порядок вывода аргументов был бы неопределён.
Списки аргументов
произвольной длины
Наконец, наиболее редко используется возможность указания того, что функция может быть вызвана с произвольным числом аргументов. При этом сами аргументы будут обёрнуты в кортеж (незнакомое слово, можно пока пропустить мимо ушей). Переменное количество аргументов могут предварять ноль или более обычных аргументов.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
Распаковка списков
аргументов
Обратная ситуация возникает когда аргументы уже содержатся в списке или в кортеже, но должны быть распакованы для вызова функции, требующей отдельных позиционных аргументов. Например, встроенная функция range() ожидает отдельных аргументов start и stop соответственно. Если они не доступны раздельно, для распаковки аргументов из списка или кортежа в вызове функции используйте *-оператор:
>>> range(3, 6) # обычный вызов с отдельными аргументами
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)
# вызов с распакованными из списка аргументами
[3, 4, 5]
Схожим способом, словари могут получать именованные аргументы через **-оператор[27]:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print "-- This parrot wouldn't", action,
... print "if you put", voltage, "volts through it.",
... print "It'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. It's bleedin' demised !
Модель lambda
По частым просьбам, в Python были добавлены несколько возможностей, которые были привычны для функциональных языков программирования, таких как Lisp. Используя зарезервированное слово 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
Строки документации
Перечислим некоторые существующие соглашения по содержимому строк документации и их форматированию.
· Первая строка всегда должна быть сжатой, лаконичной сводкой о назначении объекта. Для краткости, в ней не обязательно присутствие имени типа или объекта, поскольку они доступны другими способами (исключая случай, когда имя функции оказывается глаголом, описывающим суть операции). Эта строка должна начинаться с прописной буквы и оканчиваться точкой.
· Если строке документации (литерале, объекте строки) требуется больше строк (физических), вторая строка должна быть пустой, визуально отделяя сводку от остального описания. Следующие строки могут быть одним или более абзацем, описывающим соглашения по вызову объекта, сторонние эффекты, и т.д.
Парсер Python не обрабатывает отступы в много-строковых литералах, поэтому инструментам, которые работают над документацией, предлагается, по желанию, делать это самим. Производится это по следующему соглашению. Первая непустая строка после первой строки литерала определяет величину отступа всего литерала документации. (Мы не можем использовать первую строку, поскольку она обычно выравнивается по открывающим кавычкам и её отступ в литерале не явен). Пробельный „эквивалент“ этого отступа затем отрезается от начала всех строк литерала. Строк с меньшим отступом не должно обнаруживаться, но если они встретились, весь их начальный отступ должен быть обрезан. Эквивалентность пробельных замен может быть протестирована развертыванием табуляции (обычно, к 8 пробелам).
Вот пример многострочной документации (док-строки):
>>> def my_function():
... """Не делаем
ничего, но документируем.
...
... Нет, правда, эта функция
ничего не делает.
... """
... pass
...
>>> print my_function.__doc__
Не делаем ничего, но документируем.
Нет, правда, эта функция ничего
не делает.
Практическое задание
1) Разобрать (набрать и проверить) хотя бы треть из приведённых здесь примеров. Если Питон – не первый ваш язык, то у вас не должно возникнуть трудностей.
2) Убедиться что интерпретатор Python действительно работает так как вам тут рассказывают.
3) Убедиться что Python – это удобно, быстро и просто здорово.
При необходимости можно возвращаться к этому уроку. И вы будете сюда возвращаться! Потому как на следующий день всё забудете.
Всё! Стакан кефира и спать. До завтра!