Числовые и строковые функции
Функции работают с числовыми или строковыми аргументами. В следующей таблице даны описания этих функций.
abs(x) | Модуль числа x. Результат: |x|. |
divmod(x, y) | Частное и остаток от деления. Результат: (частное, остаток). |
pow(x, y[, m]) | Возведение x в степень y по модулю m. Результат: x**y % m. |
round(n[, z]) | Округление чисел до заданного знака после (или до) точки. |
ord(s) | Функция возвращает код (или Unicode) заданного ей символа в односимвольной строке. |
chr(n) | Возвращает строку с символом с заданным кодом. |
len(s) | Возвращает число элементов последовательности или отображения. |
oct(n), hex(n) | Функции возвращают строку с восьмеричным или шестнадцатеричным представлением целого числа n. |
cmp(x, y) | Сравнение двух значений. Результат: отрицательный, ноль или положительный, в зависимости от результата сравнения. |
unichr(n) | Возвращает односимвольную Unicode-строку с символом с кодом n. |
unicode(s, [, encoding[, errors]]) | Создает Unicode-объект, соответствующий строке s в заданной кодировке encoding. Ошибки кодирования обрабатываются в соответствии с errors, который может принимать значения: 'strict' (строгое преобразование), 'replace' (с заменой несуществующих символов) или 'ignore' (игнорировать несуществующие символы). По умолчанию: encoding='utf-8', errors='strict'. |
Следующий пример строит таблицу кодировки кириллических букв в Unicode:
print "Таблица Unicode (русские буквы)".center(18*4) i = 0 for c in "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"\ "абвгдежзийклмнопрстуфхцчшщъыьэюя": u = unicode(c, 'koi8-r') print "%3i: %1s %s" % (ord(u), c, `u`), i += 1 if i % 4 == 0: print
Функции для доступа к внутренним структурам
В современной реализации языка Python глобальные и локальные переменные доступны в виде словаря благодаря функциям globals() и locals(). Правда, записывать что-либо в эти словари не рекомендуется.
Функция vars() возвращает таблицу локальных имен некоторого объекта (если параметр не задан, она возвращает то же, что и locals()). Обычно используется в качестве словаря для операции форматирования:
a = 1 b = 2 c = 3 print "%(a)s + %(b)s = %(c)s" % vars()
Функции для работы с атрибутами
У объектов в языке Python могут быть атрибуты (в терминологии языка C++ - члены-данные и члены-функции). Следующие две программы эквивалентны:
# первая программа: class A: pass a = A() a.attr = 1 try: print a.attr except: print None del a.attr
# вторая программа: class A: pass a = A() setattr(a, 'attr', 1) if hasattr(a, 'attr'): print getattr(a, 'attr') else: print None delattr(a, 'attr')
Функции компиляции и исполнения
Функция reload() уже рассматривалась, а из остальных функций этой категории особого внимания заслуживает eval(). Как следует из названия, эта функция вычисляет переданное ей выражение. В примере ниже вычисляется выражение, которое строится динамически:
a = 2 b = 3 for op in "+-*/%": e = "a " + op + " b" print e, "->", eval(e)
У функции eval() кроме подлежащего вычислению выражения есть еще два параметра - с их помощью можно задать глобальное и локальное пространства имен, из которых будут разрешаться имена выражения. Пример выше, переписанный для использования с собственным словарем имен в качестве глобального пространства имен:
for op in "+-*/%": e = "a " + op + " b" print e, "->", eval(e, {'a': 2, 'b': 3})
Функцией eval() легко злоупотребить. Нужно стараться использовать ее только тогда, когда без нее не обойтись. Из соображений безопасности не следует применять eval() для аргумента, в котором присутствует непроверенный ввод от пользователя.
Функции обработки данных
Эти функции подробнее будут рассмотрены в лекции по функциональному программированию. Пример с функциями range() и enumerate():
>>> for i, c in enumerate("ABC"): ... print i, c ... 0 A 1 B 2 C >>> print range(4, 20, 2) [4, 6, 8, 10, 12, 14, 16, 18]
Функции определения свойств
Эти функции обеспечивают доступ к некоторым встроенным атрибутам объектов и другим свойствам. Следующий пример показывает некоторые из этих функций:
>>> s = "abcde" >>> s1 = "abcde" >>> s2 = "ab" + "cde" >>> print "hash:", hash(s), hash(s1), hash(s2) hash: -1332677140 -1332677140 -1332677140 >>> print "id:", id(s), id(s1), id(s2) id: 1076618592 1076618592 1076618656
Здесь, можно увидеть, что для одного и того же строкового литерала "abcde" получается один и тот же объект, тогда как для одинаковых по значению объектов вполне можно получить разные объекты.
Функции преобразования типов и классы
Функции и классы из этой категории служат для преобразования типов данных. В старых версиях Python для преобразования к нужному типу использовалась одноименная функция. В новых версиях Python роль таких функций играют имена встроенных классов (однако семантика не изменилась). Для понимания сути достаточно небольшого примера:
>>> int(23.5) 23 >>> float('12.345') 12.345000000000001 >>> dict([('a', 2), ('b', 3)]) {'a': 2, 'b': 3} >>> object <type 'object'> >>> class MyObject(object): ... pass ...
Функции-"украшатели" методов классов
Эти функции будут рассмотрены в лекции, посвященной ООП.
Функции ввода-вывода
Функции input() и raw_input() используются для ввода со стандартного ввода. В серьезных программах их лучше не применять. Функция open() служит для открытия файла по имени для чтения, записи или изменения. В следующем примере файл открывается для чтения:
f = open("file.txt", "r", 1) for line in f: . . . f.close()
Функция принимает три аргумента: имя файла (путь к файлу), режим открытия ("r" - чтение, "w" - запись, "a" - добавление или "w+", "a+", "r+" - изменение. Также может прибавляться "t", что обозначает текстовый файл. Это имеет значение только на платформе Windows). Третий аргумент указывает режим буферизации: 0 - без буферизации, 1 - построчная буферизация, больше 1 - буфер указанного размера в байтах.
В новых версиях Python функция open() является синонимом для file().
Графический интерфейс
Почти все современные приложения имеют графический интерфейс пользователя. Такие приложения можно создавать и на языке Python. В стандартной поставке имеется модуль Tkinter, который есть не что иное, как интерфейс к языку Tcl/Tk, на котором можно описывать графический интерфейс.
Следует отметить, что существуют и другие пакеты для программирования графического интерфейса: wxPython (основан на wxWindows), PyGTK и т.д. Среди этих пакетов в основном такие, которые работают на одной платформе (реже - на двух).
Помимо возможностей программного описания графического интерфейса, для Python есть несколько коммерческих и некоммерческих построителей графического интерфейса (GUI builders), однако в данном курсе они не рассматриваются.
Хранение данных. Архивация
К этой категории отнесены модули, которые работают с внешними хранилищами данных.
Многопоточные вычисления
Модули этой категории станут предметом рассмотрения отдельной лекции.
Модуль copy
Этот модуль содержит функции для копирования объектов. Вначале предлагается к рассмотрению "парадокс", который вводит в замешательство новичков в Python:
lst1 = [0, 0, 0] lst = [lst1] * 3 print lst lst[0][1] = 1 print lst
В результате получается, возможно, не то, что ожидалось:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]] [[0, 1, 0], [0, 1, 0], [0, 1, 0]]
Дело в том, что список lst содержит ссылки на один и тот же список! Для того чтобы действительно размножить список, необходимо применить функцию copy() из модуля copy:
from copy import copy lst1 = [0, 0, 0] lst = [copy(lst1) for i in range(3)] print lst lst[0][1] = 1 print lst
Теперь результат тот, который ожидался:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]] [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
В модуле copy есть еще и функция deepcopy() для глубокого копирования, при которой объекты копируются на всю возможную глубину, рекурсивно.
Модуль csv
Формат CSV (comma separated values - значения, разделенные запятыми) достаточно популярен для обмена данными между электронными таблицами и базами данных. Следующий ниже пример посвящен записи в CSV-файл и чтению из него:
mydata = [(1, 2, 3), (1, 3, 4)] import csv
# Запись в файл: f = file("my.csv", "w") writer = csv.writer(f) for row in mydata: writer.writerow(row) f.close()
# Чтение из файла: reader = csv.reader(file("my.csv")) for row in reader: print row
Модуль gettext
При интернационализации программы важно не только предусмотреть возможность использования нескольких культурных сред, но и перевод сообщений и меню программы на соответствующий язык. Модуль gettext позволяет упростить этот процесс достаточно стандартным способом. Основные сообщения программы пишутся на английском языке. А переводы строк, отмеченных в программе специальным образом, даются в виде отдельных файлов, по одному на каждый язык (или культурную среду). Уточнить нюансы использования gettext можно по документации к Python.
Модуль itertools
Этот модуль содержит набор функций для работы с итераторами. Итераторы позволяют работать с данными последовательно, как если бы они получались в цикле. Альтернативный подход - использование списков для хранения промежуточных результатов - требует подчас большого количества памяти, тогда как использование итераторов позволяет получать значения на момент, когда они действительно требуются для дальнейших вычислений. Итераторы будут рассмотрены более подробно в лекции по функциональному программированию.
Модуль locale
Модуль locale применяется для работы с культурной средой. В конкретной культурной среде могут использоваться свои правила для написания чисел, валют, времени и даты и т.п. Следующий пример выводит дату сначала в культурной среде "C", а затем на русском языке:
import time, locale locale.setlocale(locale.LC_ALL, None) print time.strftime("%d %B %Y", time.localtime (time.time())) locale.setlocale(locale.LC_ALL, "ru_RU.KOI8-R") print time.strftime("%d %B %Y", time.localtime (time.time()))
В результате:
18 November 2004 18 Ноября 2004
Модуль os
Разделители каталогов и другие связанные с этим обозначения доступны в виде констант.
os.curdir | Текущий каталог |
os.pardir | Родительский каталог |
os.sep | Разделитель элементов пути |
os.altsep | Другой разделитель элементов пути |
os.pathsep | Разделитель путей в списке путей |
os.defpath | Список путей по умолчанию |
os.linesep | Признак окончания строки |
Программа на Python работает в операционной системе в виде отдельного процесса. Функции модуля os дают доступ к различным значениям, относящимся к процессу и к среде, в которой он исполняется. Одним из важных объектов, доступных из модуля os, является словарь переменных окружения environ. Например, с помощью переменных окружения web-сервер передает некоторые параметры в CGI-сценарий. В следующем примере можно получить переменную окружения PATH:
import os PATH = os.environ['PATH']
Большая группа функций посвящена работе с файлами и каталогами. Ниже приводятся только те, которые доступны как в Unix, так и в Windows.
access(path, flags) | Проверка доступности файла или каталога с именем path. Режим запрашиваемого доступа указывается значением flags, составленных комбинацией (побитовым ИЛИ) флагов os.F_OK (файл существует), os.R_OK (из файла можно читать), os.W_OK (в файл можно писать) и os.X_OK (файл можно исполнять, каталог можно просматривать). |
chdir(path) | Делает path текущим рабочим каталогом. |
getcwd() | Текущий рабочий каталог. |
chmod(path, mode) | Устанавливает режим доступа к path в значение mode. Режим доступа можно получить, скомбинировав флаги (см. ниже). Следует заметить, что chmod() не дополняет действующий режим, а устанавливает его заново. |
listdir(dir) | Возвращает список файлов в каталоге dir. В список не входят специальные значения "." и "..". |
mkdir(path[, mode]) | Создает каталог path. По умолчанию режим mode равен 0777, то есть: S_IRWXU|S_IRWXG|S_IRWXO, если пользоваться константами модуля stat. |
makedirs(path[, mode]) | Аналог mkdir(), создающий все необходимые каталоги, если они не существуют. Возбуждает исключение, когда последний каталог уже существует. |
remove(path), unlink(path) | Удаляет файл path. Для удаления каталогов используются rmdir() и removedirs(). |
rmdir(path) | Удаляет пустой каталог path. |
removedirs(path) | Удаляет path до первого непустого каталога. В случае если самый последний вложенный подкаталог в указанном пути - не пустой, возбуждается исключение OSError. |
rename(src, dst) | Переименовывает файл или каталог src в dst. |
renames(src, dst) | Аналог rename(), создающий все необходимые каталоги для пути dst и удаляющий пустые каталоги пути src. |
stat(path) | Возвращает информацию о path в виде не менее чем десятиэлементного кортежа. Для доступа к элементам кортежа можно использовать константы из модуля stat, например stat.ST_MTIME (время последней модификации файла). |
utime(path, times) | Устанавливает значения времен последней модификации (mtime) и доступа к файлу (atime). Если times равен None, в качестве времен берется текущее время. В других случаях times рассматривается как двухэлементный кортеж (atime, mtime). Для получения atime и mtime некоторого файла можно использовать stat() совместно с константами модуля stat. |
Для работы с процессами модуль os предлагает следующие функции (здесь упомянуты только некоторые, доступные как в Unix, так и в Windows):
abort() | Вызывает для текущего процесса сигнал SIGABRT. |
system(cmd) | Выполняет командную строку cmd в отдельной оболочке, аналогично вызову system библиотеки языка C. Возвращаемое значение зависит от используемой платформы. |
times() | Возвращает кортеж из пяти элементов, содержащий время в секундах работы процесса, ОС (по обслуживанию процесса), дочерних процессов, ОС для дочерних процессов, а также время от фиксированного момента в прошлом (например, от момента запуска системы). |
getloadavg() | Возвращает кортеж из трех значений, соответствующих занятости процессора за последние 1, 5 и 15 минут. |
Модуль pdb
Модуль pdb предоставляет функции отладчика с интерфейсом - командной строкой. Сессия отладки вышеприведенного модуля могла бы быть такой:
>>> import pdb >>> pdb.runcall(Sieve.primes, 100) > /home/rnd/workup/intuit-python/examples/Sieve.py(15)primes() -> sieve = sets.Set(range(2, N)) (Pdb) l 10 import sets 11 import math 12 """Модуль для вычисления простых чисел от 2 до N """ 13 def primes(N): 14 """Возвращает все простые от 2 до N""" 15 -> sieve = sets.Set(range(2, N)) 16 for i in range(2, int(math.sqrt(N))): 17 if i in sieve: 18 sieve -= sets.Set(range(2*i, N, i)) 19 return sieve 20 (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(16)primes() -> for i in range(2, int(math.sqrt(N))): (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(17)primes() -> if i in sieve: (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(18)primes() -> sieve -= sets.Set(range(2*i, N, i)) (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(16)primes() -> for i in range(2, int(math.sqrt(N))): (Pdb) p sieve Set([2, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]) (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(17)primes() -> if i in sieve: (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(18)primes() -> sieve -= sets.Set(range(2*i, N, i)) (Pdb) n > /home/rnd/workup/intuit-python/examples/Sieve.py(16)primes() -> for i in range(2, int(math.sqrt(N))): (Pdb) p sieve Set([2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53, 55, 59, 61, 65, 67, 71, 73, 77, 79, 83, 85, 89, 91, 95, 97])
Модуль pickle
Процесс записи объекта в виде последовательности байтов называется сериализацией. Для того чтобы сохранить объект во внешней памяти или передать его по каналам связи, его нужно вначале сериализовать.
Модуль pickle позволяет сериализовывать объекты и сохранять их в строке или файле. Следующие объекты могут быть сериализованы:
встроенные типы: None, числа, строки (обычные и Unicode).списки, кортежи и словари, содержащие только сериализуемые объекты.функции, определенные на уровне модуля (сохраняется имя, но не реализация!).встроенные функции.классы, определенные на уровне модуля.объекты классов, __dict__ или __setstate__() которые являются сериализуемыми.
Типичный вариант использования модуля приведен ниже.
Сохранение:
import pickle, time mydata = ("abc", 12, [1, 2, 3]) output_file = open("mydata.dat", "w") p = pickle.Pickler(output_file) p.dump(mydata) output_file.close()
Восстановление:
import pickle input_file = open("mydata.dat", "r") mydata = pickle.load(input_file) print mydata input_file.close()
Модуль profile
С помощью профайлера разработчики программного обеспечения могут узнать, сколько времени занимает исполнение различных функций и методов.
Продолжая пример с решетом Эратосфена, стоит посмотреть, как тратится процессорное время при вызове функции primes():
>>> profile.run("Sieve.primes(100000)") 709 function calls in 1.320 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function) 1 0.010 0.010 1.320 1.320 <string>:1(?) 1 0.140 0.140 1.310 1.310 Sieve.py:13(primes) 1 0.000 0.000 1.320 1.320 profile:0(Sieve.primes(100000)) 0 0.000 0.000 profile:0(profiler) 65 0.000 0.000 0.000 0.000 sets.py:119(__iter__) 314 0.000 0.000 0.000 0.000 sets.py:292(__contains__) 65 0.000 0.000 0.000 0.000 sets.py:339(_binary_sanity_check) 66 0.630 0.010 0.630 0.010 sets.py:356(_update) 66 0.000 0.000 0.630 0.010 sets.py:425(__init__) 65 0.010 0.000 0.540 0.008 sets.py:489(__isub__) 65 0.530 0.008 0.530 0.008 sets.py:495(difference_update)
Здесь ncalls - количество вызовов функции или метода, tottime - полное время выполнения кода функции (без времени нахождения в вызываемых функциях), percall - тоже, в пересчете на один вызов, cumtime - аккумулированное время нахождения в функции, вместе со всеми вызываемыми функциями. В последнем столбце приведено имя файла, номер строки с функцией или методов и его имя.
Примечание: "Странные" имена, например, __iter__, __contains__ и __isub__ - имена методов, реализующих итерацию по элементам, проверку принадлежности элемента (in) и операцию -=. Метод __init__ - конструктор объекта (в данном случае - множества). |
Модуль pydoc
Успех проекта зависит не только от обеспечения эффективного и качественного кода, но и от качества документации. Утилита pydoc аналогична команде man в Unix:
$ pydoc Sieve Help on module Sieve:
NAME Sieve - Модуль для вычисления простых чисел от 2 до N
FILE Sieve.py
FUNCTIONS primes(N) Возвращает все простые от 2 до N
Эта страница помощи появилась благодаря тому, что были написаны строки документации - как ко всему модулю, так и к функции primes(N).
Стоит попробовать запустить pydoc следующей командой:
pydoc -p 8088
И направить браузер на URL http://127.0.0.1:8088/ - можно получить документацию по модулям Python в виде красивого web-сайта.
Узнать другие возможности pydoc можно, подав команду pydoc pydoc.
Модуль random
Этот модуль генерирует псевдослучайные числа для нескольких различных распределений. Наиболее используемые функции:
random() | Генерирует псевдослучайное число из полуоткрытого диапазона [0.0, 1.0). |
choice(s) | Выбирает случайный элемент из последовательности s. |
shuffle(s) | Размешивает элементы изменчивой последовательности s на месте. |
randrange([start,] stop[, step]) | Выдает случайное целое число из диапазона range(start, stop, step). Аналогично choice(range(start, stop, step)). |
normalvariate(mu, sigma) | Выдает число из последовательности нормально распределенных псевдослучайных чисел. Здесь mu - среднее, sigma - среднеквадратическое отклонение (sigma > 0) |
Остальные функции и их параметры можно уточнить по документации. Следует отметить, что в модуле есть функция seed(n), которая позволяет установить генератор случайных чисел в некоторое состояние. Например, если возникнет необходимость многократного использования одной и той же последовательности псевдослучайных чисел.
Модуль sets
Модуль реализует тип данных для множеств. Следующий пример показывает, как использовать этот модуль. Следует заметить, что в Python 2.4 и старше тип set стал встроенным, и вместо sets.Set можно использовать set:
import sets A = sets.Set([1, 2, 3]) B = sets.Set([2, 3, 4]) print A | B, A & B, A - B, A ^ B for i in A: if i in B: print i,
В результате будет выведено:
Set([1, 2, 3, 4]) Set([2, 3]) Set([1]) Set([1, 4]) 2 3
Модуль shelve
Для хранения объектов в родном для Python формате можно применять полку (shelve). По своему интерфейсу полка ничем не отличается от словаря. Следующий пример показывает, как использовать полку:
import shelve data = ("abc", 12) # - данные (объект) key = "key" # - ключ (строка) filename = "polka.dat" # - имя файла для хранения полки d = shelve.open(filename) # открытие полки d[key] = data # сохранить данные под ключом key # (удаляет старое значение, если оно было) data = d[key] # загрузить значение по ключу len(d) # получить количество объектов на полке d.sync() # запись изменений в БД на диске del d[key] # удалить ключ и значение flag = d.has_key(key) # проверка наличия ключа lst = d.keys() # список ключей d.close() # закрытие полки
Модуль stat
В этом модуле описаны константы, которые можно использовать как индексы к кортежам, применяемым функциями os.stat() и os.chmod() (а также некоторыми другими). Их можно уточнить в документации по Python.
Модуль sys
Модуль sys содержит информацию о среде выполнения программы, об интерпретаторе Python. Далее будут представлены наиболее популярные объекты из этого модуля: остальное можно изучить по документации.
exit([c]) | Выход из программы. Можно передать числовой код завершения: 0 в случае успешного завершения, другие числа при аварийном завершении программы. |
argv | Список аргументов командной строки. Обычно sys.argv[0] содержит имя запущенной программы, а остальные параметры передаются из командной строки. |
platform | Платформа, на которой работает интерпретатор. |
stdin, stdout, stderr | Стандартный ввод, вывод, вывод ошибок. Открытые файловые объекты. |
version | Версия интерпретатора. |
setrecursionlimit(limit) | Установка уровня максимальной вложенности рекурсивных вызовов. |
exc_info() | Информация об обрабатываемом исключении. |
Модуль tempfile
Программе иногда требуется создать временный файл, который после выполнения некоторых действий больше не нужен. Для этих целей можно использовать функцию TemporaryFile, которая возвращает файловый объект, готовый к записи и чтению.
В следующем примере создается временный файл, куда записываются данные и затем читаются:
import tempfile f = tempfile.TemporaryFile() f.write("0"*100) # записывается сто символов 0 f.seek(0) # уст. указатель на начало файла print len(f.read()) # читается до конца файла и вычисляется длина
Как и следовало ожидать, в результате будет выведено 100. Временный файл будет удален, как только будут удалены все ссылки на его объект.
Модуль time
Этот модуль дает функции для получения текущего времени и преобразования форматов времени.
Модуль unittest
При разработке программного обеспечения рекомендуется применять так называемые регрессионные испытания. Для каждого модуля составляется набор тестов, по возможности таким образом, чтобы проверялись не только типичные вычисления, но и "крайние", вырожденные случаи, чтобы испытания затронули каждую ветку алгоритма хотя бы один раз. Тест для данного модуля (написанный сразу после того, как определен интерфейс модуля) находится в файле test_Sieve.py:
# file: test_Sieve.py import Sieve, sets import unittest
class TestSieve(unittest.TestCase):
def setUp(self): pass
def testone(self): primes = Sieve.primes(1) self.assertEqual(primes, sets.Set())
def test100(self): primes = Sieve.primes(100) self.assert_(primes == sets.Set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]))
if __name__ == '__main__': unittest.main()
Тестовый модуль состоит из определения класса, унаследованного от класса unittest.TestCase, в котором описывается подготовка к испытаниям (метод setUp) и сами испытания -- методы, начинающиеся на test. В данном случае таких испытаний всего два: в первом испытывается случай N=1, а во втором -- N=100.
Запуск тестов производится выполнением функции unittest.main(). Вот как выглядят успешные испытания:
$ python test_Sieve.py .. ---------------------------------------------------------------------- Ran 2 tests in 0.002s
OK
В процессе разработки перед каждым выпуском все модули прогоняются через регрессионные испытания, чтобы обнаружить, не были ли внесены ошибки. Однако никакие тесты в общем случае не могут гарантировать безошибочности сложной программы. При дополнении модулей тесты также могут быть дополнены, чтобы отразить изменения в проекте.
Кстати, сам Python и его стандартная библиотека имеют тесты для каждого модуля - они находятся в каталоге test в месте, где развернуты файлы поставки Python, и являются частью пакета test.
Модули anydbm и gdbm
Для внешнего хранения данных можно использовать примитивные базы данных, содержащие пары ключ-значение. В Python имеется несколько модулей для работы с такими базами: bsddb, gdbm, dbhash и т.п. Модуль anydbm выбирает один из имеющихся хэшей, поэтому его можно применять для чтения ряда форматов (any - любой).
Доступ к хэшу из Python мало отличается от доступа к словарю. Разница лишь в том, что хэш еще нужно открыть для создания, чтения или записи, а затем закрыть. Кроме того, при записи хэш блокируется, чтобы не испортить данные.
Модули array и struct
Эти модули реализуют низкоуровневый массив и структуру данных. Основное их назначение - разбор двоичных форматов данных.
Модули math и cmath
В этих модулях собраны математические функции для действительных и комплексных аргументов. Это те же функции, что используются в языке C. В таблице ниже даны функции модуля math. Там, где аргумент обозначен буквой z, аналогичная функция определена и в модуле cmath.
acos(z) | арккосинус z |
asin(z) | арксинус z |
atan(z) | арктангенс z |
atan2(y,x) | atan(y/x) |
ceil(x) | наименьшее целое, большее или равное x |
cos(z) | косинус z |
cosh(x) | гиперболический косинус x |
e | константа e |
exp(z) | экспонента (то есть, e**z) |
fabs(x) | абсолютное значение x |
floor(x) | наибольшее целое, меньшее или равное x |
fmod(x,y) | остаток от деления x на y |
frexp(x) | возвращает мантиссу и порядок x как пару (m, i), где m - число с плавающей точкой, а i - целое, такое, что x = m * 2.**i. Если 0, возвращает (0,0), иначе 0.5 <= abs(m) < 1.0 |
hypot(x,y) | sqrt(x*x + y*y) |
ldexp(m,i) | m * (2**i) |
log(z) | натуральный логарифм z |
log10(z) | десятичный логарифм z |
modf(x) | возвращает пару (y,q) - целую и дробную часть x. Обе части имеют знак исходного числа |
pi | константа пи |
pow(x,y) | x**y |
sin(z) | синус z |
sinh(z) | гиперболический синус z |
sqrt(z) | корень квадратный от z |
tan(z) | тангенс z |
tanh(z) | гиперболический тангенс z |
Модули в Python
Модуль оформляется в виде отдельного файла с исходным кодом. Стандартные модули находятся в каталоге, где их может найти соответствующий интерпретатор языка. Пути к каталогам, в которых Python ищет модули, можно увидеть в значении переменной sys.path:
>>> sys.path ['', '/usr/local/lib/python23.zip', '/usr/local/lib/python2.3', '/usr/local/lib/python2.3/plat-linux2', '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload', '/usr/local/lib/python2.3/site-packages']
В последних версиях Python модули можно помещать и в zip-архивы для более компактного хранения (по аналогии с jar-архивами в Java).
При запуске программы поиск модулей также идет в текущем каталоге. (Нужно внимательно называть собственные модули, чтобы не было конфликта имен со стандартными или дополнительно установленными модулями.)
Подключение модуля к программе на Python осуществляется с помощью оператора import. У него есть две формы: import и from-import:
import os import pre as re from sys import argv, environ from string import *
С помощью первой формы с текущей областью видимости связывается только имя, ссылающееся на объект модуля, а при использовании второй - указанные имена (или все имена, если применена *) объектов модуля связываются с текущей областью видимости. При импорте можно изменить имя, с которым объект будет связан, с помощью as. В первом случае пространство имен модуля остается в отдельном имени и для доступа к конкретному имени из модуля нужно применять точку. Во втором случае имена используются так, как если бы они были определены в текущем модуле:
os.system("dir") digits = re.compile("\d+") print argv[0], environ
Повторный импорт модуля происходит гораздо быстрее, так как модули кэшируются интерпретатором. Загруженный модуль можно загрузить еще раз (например, если модуль изменился на диске) с помощью функции reload():
import mymodule . . . reload(mymodule)
Однако в этом случае все объекты, являющиеся экземплярами классов из старого варианта модуля, не изменят своего поведения.
При работе с модулями есть и другие тонкости. Например, сам процесс импорта модуля можно переопределить. Подробнее об этом можно узнать в оригинальной документации.
Обработка текстов
Модули этой категории будут подробно рассмотрены в отдельной лекции.
Обзор стандартной библиотеки
Модули стандартной библиотеки можно условно разбить на группы по тематике.
Сервисы периода выполнения. Модули: sys, atexit, copy, traceback, math, cmath, random, time, calendar, datetime, sets, array, struct, itertools, locale, gettext.Поддержка цикла разработки. Модули: pdb, hotshot, profile, unittest, pydoc. Пакеты docutils, distutils.Взаимодействие с ОС (файлы, процессы). Модули: os, os.path, getopt, glob, popen2, shutil, select, signal, stat, tempfile.Обработка текстов. Модули: string, re, StringIO, codecs, difflib, mmap, sgmllib, htmllib, htmlentitydefs. Пакет xml.Многопоточные вычисления. Модули: threading, thread, Queue.Хранение данных. Архивация. Модули: pickle, shelve, anydbm, gdbm, gzip, zlib, zipfile, bz2, csv, tarfile.Платформо-зависимые модули. Для UNIX: commands, pwd, grp, fcntl, resource, termios, readline, rlcompleter. Для Windows: msvcrt, _winreg, winsound.Поддержка сети. Протоколы Интернет. Модули: cgi, Cookie, urllib, urlparse, httplib, smtplib, poplib, telnetlib, socket, asyncore. Примеры серверов: SocketServer, BaseHTTPServer, xmlrpclib, asynchat.Поддержка Internet. Форматы данных. Модули: quopri, uu, base64, binhex, binascii, rfc822, mimetools, MimeWriter, multifile, mailbox. Пакет email.Python о себе. Модули: parser, symbol, token, keyword, inspect, tokenize, pyclbr, py_compile, compileall, dis, compiler.Графический интерфейс. Модуль Tkinter.
Примечание: Очень часто модули содержат один или несколько классов, с помощью которых создается объект нужного типа, а затем речь идет уже не об именах из модуля, а об атрибутах этого объекта. И наоборот, некоторые модули содержат лишь функции, достаточно общие для того, чтобы работать над произвольными объектами (либо достаточно большой категорией объектов). |
Пакет distutils
Данный пакет предоставляет стандартный путь для распространения собственных Python-пакетов. Достаточно написать небольшой конфигурационный файл setup.py, использующий distutils, и файл с перечислением файлов проекта MANIFEST.in, чтобы пользователи пакета смогли его установить командой
python setup.py install
Тонкости работы с distutils можно изучить по документации.
Пакет docutils
Этот пакет и набор утилит пока что не входит в стандартную поставку Python, однако о нем нужно знать тем, кто хочет быстро готовить документацию (руководства пользователя и т.п.) для своих модулей. Этот пакет использует специальный язык разметки (ReStructuredText), из которого потом легко получается документация в виде HTML, LaTeX и в других форматах. Текст в формате RST легко читать и в исходном виде. С этим инструментом можно познакомиться на http://docutils.sourceforge.net
Платформо-зависимые модули
Эта категория модулей имеет применение только для конкретных операционных систем и семейств операционных систем. Довольно большое число модулей в стандартной поставке Python посвящено трем платформам: Unix, Windows и Macintosh.
При создании переносимых приложений использовать платформо-зависимые модули можно только при условии реализации альтернативных веток алгоритма, либо с отказом от свойств, которые доступны не на всех платформах. Так, под Windows не работает достаточно обычная для Unix функция os.fork(), поэтому при создании переносимых приложений нужно использовать другие средства для распараллеленных вычислений, например, многопоточность.
В документации по языку обычно отмечено, для каких платформ доступен тот или иной модуль или даже отдельная функция.
Поддержка цикла разработки
Модули этого раздела помогают поддерживать документацию, производить регрессионное тестирование, отлаживать и профилировать программы на Python, а также обслуживают распространение готовых программ, создавая среду для конфигурирования и установки пакетов.
В качестве иллюстрации можно предположить, что создается модуль для вычисления простых чисел по алгоритму "решето Эратосфена". Модуль будет находиться в файле Sieve.py и состоять из одной функции primes(N), которая в результате своей работы дает все простые (не имеющие натуральных делителей кроме себя и единицы) числа от 2 до N:
import sets import math """Модуль для вычисления простых чисел от 2 до N """ def primes(N): """Возвращает все простые от 2 до N""" sieve = sets.Set(range(2, N)) for i in range(2, math.sqrt(N)): if i in sieve: sieve -= sets.Set(range(2*i, N, i)) return sieve
Поддержка Internet. Форматы данных
В стандартной библиотеке Python имеются разноуровневые модули для работы с различными форматами, применяющимися для кодирования данных в сети Интернет и тому подобных приложениях.
Сегодня наиболее мощным инструментом для обработки сообщений в формате RFC 2822 является пакет email. С его помощью можно как разбирать сообщения в удобном для программной обработки виде, так и формировать сообщение на основе данных о полях и основном содержимом (включая вложения).
Поддержка сети. Протоколы Интернет
Почти все модули из этой категории, обслуживающие клиентскую часть протокола, построены по одному и тому же принципу: из модуля необходим только класс, объект которого содержит информацию о соединении с сервером, а методы реализуют взаимодействие с сервером по соответствующему протоколу. Таким образом, чем сложнее протокол, тем больше методов и других деталей требуется для реализации клиента.
Примеры серверов используются по другому принципу. В модуле с реализацией сервера описан базовый класс, из которого пользователь модуля должен наследовать свой класс, реализующий требуемую функциональность. Правда, иногда замещать нужно всего один или два метода.
Этому вопросу будет посвящена отдельная лекция.
Понятие модуля
Перед тем как приступить к изучению модулей стандартной библиотеки, необходимо определить то, что в Python называется модулем.
В соответствии с модульным подходом к программированию большая задача разбивается на несколько более мелких, каждую из которых (в идеале) решает отдельный модуль. В разных методологиях даются различные ограничения на размер модулей, однако при построении модульной структуры программы важнее составить такую композицию модулей, которая позволила бы свести к минимуму связи между ними. Набор классов и функций, имеющий множество связей между своими элементами, было бы логично расположить в одном модуле. Есть и еще одно полезное замечание: модули должно быть легче использовать, чем написать заново. Это значит, что модуль должен иметь удобный интерфейс: набор функций, классов и констант, который он предлагает своим пользователям.
В языке Python набор модулей, посвященных одной проблеме, можно поместить в пакет. Хорошим примером такого пакета является пакет xml, в котором собраны модули для различных аспектов обработки XML.
В программе на Python модуль представлен объектом-модулем, атрибутами которого являются имена, определенные в модуле:
>>> import datetime >>> d1 = datetime.date(2004, 11, 20)
В данном примере импортируется модуль datetime. В результате работы оператора import в текущем пространстве имен появляется объект с именем datetime.
Модули для использования в программах на языке Python по своему происхождению делятся на обычные (написанные на Python) и модули расширения, написанные на другом языке программирования (как правило, на C). С точки зрения пользователя они могут отличаться разве что быстродействием. Бывает, что в стандартной библиотеке есть два варианта модуля: на Python и на C. Таковы, например, модули pickle и cPickle. Обычно модули на Python в чем-то гибче, чем модули расширения.
Python о себе
Язык Python является рефлективным языком, в котором можно "заглянуть" глубоко в собственные внутренние структуры кода и данных. Модули этой категории дают возможность прикоснуться к внутреннему устройству Python. Более подробно об этом рассказывается в отдельной лекции.
Встроенные функции
В среде Python без дополнительных операций импорта доступно более сотни встроенных объектов, в основном, функций и исключений. Для удобства функции условно разделены по категориям:
Функции преобразования типов и классы: coerce, str, repr, int, list, tuple, long, float, complex, dict, super, file, bool, objectЧисловые и строковые функции: abs, divmod, ord, pow, len, chr, unichr, hex, oct, cmp, round, unicodeФункции обработки данных: apply, map, filter, reduce, zip, range, xrange, max, min, iter, enumerate, sumФункции определения свойств: hash, id, callable, issubclass, isinstance, typeФункции для доступа к внутренним структурам: locals, globals, vars, intern, dirФункции компиляции и исполнения: eval, execfile, reload, __import__, compileФункции ввода-вывода: input, raw_input, openФункции для работы с атрибутами: getattr, setattr, delattr, hasattrФункции-"украшатели" методов классов: staticmethod, classmethod, propertyПрочие функции: buffer, slice
Совет: Уточнить назначение функции, ее аргументов и результата можно в интерактивной сессии интерпретатора Python: >>> help(len) Help on built-in function len: len(...) len(object) -> integer Return the number of items of a sequence or mapping. Или так: >>> print len.__doc__ len(object) -> integer Return the number of items of a sequence or mapping. |
Взаимодействие с операционной системой
Различные операционные системы имеют свои особенности. Здесь рассматривается основной модуль этой категории, функции которого работают на многих операционных системах.
о встроенных функциях языка Python
В этой лекции говорилось о встроенных функциях языка Python и модулях его стандартной библиотеки. Некоторые направления будут рассмотрены более подробно в следующих лекциях. Python имеет настолько обширную стандартную библиотеку, что в рамках одной лекции можно только сделать ее краткий обзор, подкрепив небольшими примерами наиболее типичные идиомы при использовании модулей.
Что такое функциональное программирование?
Функциональное программирование - это стиль программирования, использующий только композиции функций. Другими словами, это программирование в выражениях, а не в императивных командах.
Как отмечает Дэвид Мертц (David Mertz) в своей статье о функциональном программировании на Python, "функциональное программирование - программирование на функциональных языках (LISP, ML, OCAML, Haskell, ...)", основными атрибутами которых являются:
"Наличие функций первого класса (функции наравне с другими объектами можно передавать внутрь функций).Рекурсия является основной управляющей структурой в программе.Обработка списков (последовательностей).Запрещение побочных эффектов у функций, что в первую очередь означает отсутствие присваивания (в "чистых" функциональных языках)Запрещение операторов, основной упор делается на выражения. Вместо операторов вся программа в идеале - одно выражение с сопутствующими определениями.Ключевой вопрос: что нужно вычислить, а не как.Использование функций более высоких порядков (функции над функциями над функциями)".
Функции itertools.imap(), itertools.starmap() и itertools.ifilter()
Аналогами map() и filter() в модуле itertools являются imap() и ifilter(). Отличие imap() от map() в том, что вместо значения от преждевременно завершившихся итераторов объект None не подставляется. Пример:
for i in map(lambda x, y: (x,y), [1,2], [1,2,3]): print i,
(1, 1) (2, 2) (None, 3)
from itertools import imap for i in imap(lambda x, y: (x,y), [1,2], [1,2,3]): print i,
(1, 1) (2, 2)
Здесь следует заметить, что обычная функция map() нормально воспринимает итераторы в любом сочетании с итерабельными (поддающимися итерациям) объектами:
for i in map(lambda x, y: (x,y), iter([1,2]), [1,2,3]): print i,
(1, 1) (2, 2) (None, 3)
Функция itertools.starmap() подобна itertools.imap(), но имеет всего два аргумента. Второй аргумент - последовательность кортежей, каждый кортеж которой задает набор параметров для функции (первого аргумента):
>>> from itertools import starmap >>> for i in starmap(lambda x, y: str(x) + y, [(1,'a'), (2,'b')]): ... print i, ... 1a 2b
Функция ifilter() работает как filter(). Кроме того, в модуле itertools есть функция ifilterfalse(), которая как бы добавляет отрицание к значению функции:
for i in ifilterfalse(lambda x: x > 0, [1, -2, 3, -3]): print i,
-2 -3
Функции itertools.takewhile() и itertools.dropwhile()
Некоторую новизну вносит другой вид фильтра: takewhile() и его "отрицательный" аналог dropwhile(). Следующий пример поясняет их принцип действия:
for i in takewhile(lambda x: x > 0, [1, -2, 3, -3]): print i,
print for i in dropwhile(lambda x: x > 0, [1, -2, 3, -3]): print i,
1 -2 3 -3
Таким образом, takewhile() дает значения, пока условие истинно, а остальные значения даже не берет из итератора (именно не берет, а не высасывает все до конца!). И, наоборот, dropwhile() ничего не выдает, пока выполняется условие, зато потом выдает все без остатка.
Функции как параметры и результат
Как уже не раз говорилось, функции являются такими же объектами Python как числа, строки или списки. Это означает, что их можно передавать в качестве параметров функций или возвращать из функций.
Функции, принимающие в качестве аргументов или возвращающие другие функции в результате, называют функциями высшего порядка. В Python функции высшего порядка применяются программистами достаточно часто. В большинстве случаев таким образом строится механизм обратных вызовов (callbacks), но встречаются и другие варианты. Например, алгоритм поиска может вызывать переданную ему функцию для каждого найденного объекта.
Функции range() и xrange()
Функция range() уже упоминалась при рассмотрении цикла for. Эта функция принимает от одного до трех аргументов. Если аргумент всего один, она генерирует список чисел от 0 (включительно) до заданного числа (исключительно). Если аргументов два, то список начинается с числа, указанного первым аргументом. Если аргументов три - третий аргумент задает шаг
>>> print range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print range(1, 10) [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print range(1, 10, 3) [1, 4, 7]
Функция xrange() - аналог range(), более предпочтительный для использования при последовательном доступе, например, в цикле for или с итераторами. Она возвращает специальный xrange-объект, который ведет себя почти как список, порождаемый range(), но не хранит в памяти все выдаваемые элементы.
Функция apply()
Функция apply() применяет функцию, переданную в качестве первого аргумента, к параметрам, которые переданы вторым и третьим аргументом. Эта функция в Python устарела, так как вызвать функцию можно с помощью обычного синтаксиса вызова функции. Позиционные и именованные параметры можно передать с использованием звездочек:
>>> lst = [1, 2, 3] >>> dct = {'a': 4, 'b': 5} >>> apply(max, lst) 3 >>> max(*lst) 3 >>> apply(dict, [], dct) {'a': 4, 'b': 5} >>> dict(**dct) {'a': 4, 'b': 5}
Функция enumerate()
Эта функция создает итератор, нумерующий элементы другого итератора. Результирующий итератор выдает кортежи, в которых первый элемент - номер (начиная с нуля), а второй - элемент исходной последовательности:
>>> print [x for x in enumerate("abcd")] [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
Функция filter()
Другой часто встречающейся операцией является фильтрование исходной последовательности в соответствии с некоторым предикатом (условием). Функция filter(f, seq) принимает два аргумента: функцию с условием и последовательность, из которой берутся значения. В результирующую последовательность попадут только те значения из исходной, для которой f() возвратит истину. Если в качестве f задано значение None, результирующая последовательность будет состоять из тех значений исходной, которые имеют истинностное значение True.
Например, в следующем фрагменте кода можно избавится от символов, которые не являются буквами:
>>> filter(lambda x: x.isalpha(), 'Hi, there! I am eating an apple.') 'HithereIameatinganapple'
Функция iter()
Эта функция имеет два варианта использования. В первом она принимает всего один аргумент, который должен "уметь" предоставлять свой итератор. Во втором один из аргументов - функция без аргументов, другой - стоповое значение. Итератор вызывает указанную функцию до тех пор, пока та не возвратит стоповое значение. Второй вариант встречается много реже первого и обычно внутри метода класса, так как сложно порождать значения "на пустом месте":
it1 = iter([1, 2, 3, 4, 5])
def forit(mystate=[]): if len(mystate) < 3: mystate.append(" ") return " "
it2 = iter(forit, None)
print [x for x in it1] print [x for x in it2]
Примечание: Если функция не возвращает значения явно, она возвращает None, что и использовано в примере выше. |
Функция itertools.chain()
Функция chain() позволяет сделать итератор, состоящий из нескольких соединенных последовательно итераторов. Итераторы задаются в виде отдельных аргументов. Пример:
from itertools import chain it1 = iter([1,2,3]) it2 = iter([8,9,0]) for i in chain(it1, it2): print i,
даст в результате
1 2 3 8 9 0
Функция itertools.count()
Бесконечный итератор, дающий целые числа, начиная с заданного:
for i in itertools.count(1): print i, if i > 100: break
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
Функция itertools.cycle()
Можно бесконечно повторять и некоторую последовательность (или значения другого итератора) с помощью функции cycle():
tango = [1, 2, 3] for i in itertools.cycle(tango): print i,
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 . . .
Функция itertools.groupby()
Эта функция дебютировала в Python 2.4. Функция принимает два аргумента: итератор (обязательный) и необязательный аргумент - функцию, дающую значение ключа: groupby(iterable[, func]). Результатом является итератор, который возвращает двухэлементный кортеж: ключ и итератор по идущим подряд элементам с этим ключом. Если второй аргумент опущен, элемент итератора сам является ключом. В следующем примере группируются идущие подряд положительные и отрицательные элементы:
import itertools, math lst = map(lambda x: math.sin(x*.4), range(30)) for k, i in itertools.groupby(lst, lambda x: x > 0): print k, list(i)
Функция itertools.izip()
Функция izip() аналогична встроенной zip(), но не тратит много памяти на построение списка кортежей, так как итератор выдает их строго по требованию.
Функция itertools.repeat()
Функция repeat() строит итератор, повторяющий некоторый объект заданное количество раз:
for i in itertools.repeat(1, 4): print i,
1 1 1 1
Функция itertools.tee()
Эта функция тоже появилась в Python 2.4. Она позволяет клонировать итераторы. Первый аргумент - итератор, подлежащий клонированию. Второй (N) -- количество необходимых копий. Функция возвращает кортеж из N итераторов. По умолчанию N=2. Функция имеет смысл, только если итераторы задействованы более или менее параллельно. В противном случае выгоднее превратить исходный итератор в список.
Функция map()
Для применения некоторой функции ко всем элементам последовательности применяется функция map(f, *args). Первый параметр этой функции - функция, которая будет применяться ко всем элементам последовательности. Каждый следующий n+1-й параметр должен быть последовательностью, так как каждый его элемент будет использован в качестве n-го параметра при вызове функции f(). Результатом будет список, составленный из результатов выполнения этой функции.
В следующем примере складываются значения из двух списков:
>>> l1 = [2, 7, 5, 3] >>> l2 = [-2, 1, 0, 4] >>> print map(lambda x, y: x+y, l1, l2) [0, 8, 5, 7]
В этом примере применена безымянная функция для получения суммы двух операндов ко всем элементам l1 и l2. В случае если одна из последовательностей короче другой, вместо соответствующего операнда будет None, что, конечно, собьет операцию сложения. В зависимости от решаемой задачи, можно либо видоизменить функцию, либо считать разные по длине последовательности ошибкой, которую нужно обрабатывать как отдельную ветвь алгоритма.
Частный случай применения map() - использование None в качестве первого аргумента. В этом случае просто формируется список кортежей из элементов исходных последовательностей:
>>> l1 = [2, 7, 5, 3] >>> l2 = [-2, 1, 0, 4] >>> print map(None, l1, l2) [(2, -2), (7, 1), (5, 0), (3, 4)]
Функция: определение и вызов
Как уже говорилось, определить функцию в Python можно двумя способами: с помощью оператора def и lambda-выражения. Первый способ позволяет использовать операторы. При втором - определение функции может быть только выражением.
Забегая вперед, можно заметить, что методы классов определяются так же, как и функции. Отличие состоит в специальном смысле первого аргумента self (в нем передается экземпляр класса).
Лучше всего рассмотреть синтаксис определения функции на нескольких примерах. После определения соответствующей функции показан один или несколько вариантов ее вызова (некоторые примеры взяты из стандартной библиотеки).
Определение функции должно содержать список формальных параметров и тело определения функции. В случае с оператором def функции также задается некоторое имя. Формальные параметры являются локальными именами внутри тела определения функции, а при вызове функции они оказываются связанными с объектами, переданными как фактические параметры. Значения по умолчанию вычисляются в момент выполнения оператора def, и потому в них можно использовать видимые на момент определения имена.
Вызов функции синтаксически выглядит как объект-функция(фактические параметры). Обычно объект-функция - это просто имя функции, хотя это может быть и любое выражение, которое в результате вычисления дает исполняемый объект.
Функция одного аргумента:
def swapcase(s): return s.swapcase()
print swapcase("ABC")
Функция двух аргументов, один из которых необязателен и имеет значение по умолчанию:
def inc(n, delta=1): return n+delta
print inc(12) print inc(12, 2)
Функция с одним обязательным аргументом, с одним, имеющим значение по умолчанию и неопределенным числом именованных аргументов:
def wrap(text, width=70, **kwargs): from textwrap import TextWrapper # kwargs - словарь с именами и значениями аргументов w = TextWrapper(width=width, **kwargs) return w.wrap(text)
print wrap("my long text ...", width=4)
Функция произвольного числа аргументов:
def max_min(*args): # args - список аргументов в порядке их указания при вызове return max(args), min(args)
print max_min(1, 2, -1, 5, 3)
Функция с обычными (позиционными) и именованными аргументами:
def swiss_knife(arg1, *args, **kwargs): print arg1 print args print kwargs return None
print swiss_knife(1) print swiss_knife(1, 2, 3, 4, 5) print swiss_knife(1, 2, 3, a='abc', b='sdf') # print swiss_knife(1, a='abc', 3, 4) # !!! ошибка
lst = [2, 3, 4, 5] dct = {'a': 'abc', 'b': 'sdf'} print swiss_knife(1, *lst, **dct)
Пример определения функции с помощью lambda-выражения дан ниже:
func = lambda x, y: x + y
В результате lambda-выражения получается безымянный объект-функция, которая затем используется, например, для того, чтобы связать с ней некоторое имя. Однако, как правило, определяемые lambda-выражением функции, применяются в качестве параметров функций.
В языке Python функция может возвратить только одно значение, которое может быть кортежем. В следующем примере видно, как стандартная функция divmod() возвращает частное и остаток от деления двух чисел:
def bin(n): """Цифры двоичного представления натурального числа """ digits = [] while n > 0: n, d = divmod(n, 2) digits = [d] + digits return digits
print bin(69)
Примечание: Важно понять, что за именем функции стоит объект. Этот объект можно связать с другим именем: def add(x, y): return x + y addition = add # теперь addition и add - разные имена одного и того же объекта |
def mylist(val, lst=[]): lst.append(val) return lst
print mylist(1), print mylist(2)
Вместо ожидаемого [1] [2] получается [1] [1, 2], так как добавляются элементы к "значению по умолчанию".
Правильный вариант решения будет, например, таким:
def mylist(val, lst=None): lst = lst or [] lst.append(val) return lst
Конечно, приведенная выше форма может использоваться для хранения в функции некоторого состояния между ее вызовами, однако, практически всегда вместо функции с таким побочным эффектом лучше написать класс и использовать его экземпляр.
Функция reduce()
Для организации цепочечных вычислений (вычислений с накоплением результата) можно применять функцию reduce(), которая принимает три аргумента: функцию двух аргументов, последовательность и начальное значение. С помощью этой функции можно, в частности, реализовать функцию sum():
def sum(lst, start): return reduce(lambda x, y: x + y, lst, start)
Совет: Следует помнить, что в качестве передаваемого объекта может оказаться список, который позволит накапливать промежуточные результаты. Тем самым, reduce() может использоваться для генерации последовательностей. |
В следующем примере накапливаются промежуточные результаты суммирования:
lst = range(10) f = lambda x, y: (x[0] + y, x[1]+[x[0] + y]) print reduce(f, lst, (0, []))
В итоге получается:
(45, [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])
Функция sorted()
Эта функция, появившаяся в Python 2.4, позволяет создавать итератор, выполняющий сортировку:
>>> sorted('avdsdf') ['a', 'd', 'd', 'f', 's', 'v']
Далее рассматриваются функции модуля itertools.
Функция sum()
Получить сумму элементов можно с помощью функции sum():
>>> sum(range(10)) 45
Эта функция работает только для числовых типов, она не может конкатенировать строки. Для конкатенации списка строк следует использовать метод join().
Функция zip()
Эта функция возвращает список кортежей, в котором i-й кортеж содержит i-е элементы аргументов-последовательностей. Длина результирующей последовательности равна длине самой короткой из последовательностей-аргументов:
>>> print zip(range(5), "abcde") [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
Функциональная программа
В математике функция отображает объекты из одного множества (множества определения функции) в другое (множество значений функции). Математические функции (их называют чистыми) "механически", однозначно вычисляют результат по заданным аргументам. Чистые функции не должны хранить в себе какие-либо данные между двумя вызовами. Их можно представлять себе черными ящиками, о которых известно только то, что они делают, но совсем не важно, как.
Программы в функциональном стиле конструируются как композиция функций. При этом функции понимаются почти так же, как и в математике: они отображают одни объекты в другие. В программировании "чистые" функции - идеал, не всегда достижимый на практике. Практически полезные функции обычно имеют побочный эффект: сохраняют состояние между вызовами или меняют состояние других объектов. Например, без побочных эффектов невозможно представить себе функции ввода-вывода. Собственно, такие функции ради этих "эффектов" и используются. Кроме того, математические функции легко работают с объектами, требующими бесконечного объема информации (например, вещественные числа). В общем случае компьютерная программа может выполнить лишь приближенные вычисления.
Кстати, бинарные операции "+", "-", "*", "/", которые записываются в выражениях, являются "математическими" функциями над двумя аргументами -- операндами. Их используют настолько часто, что синтаксис языка программирования имеет для них более короткую запись. Модуль operator позволяет представлять эти операции в функциональном стиле:
>>> from operator import add, mul >>> print add(2, mul(3, 4)) 14
Генераторное выражение
В Python 2.4 по аналогии со списковым включением появилось генераторное выражение. По синтаксису оно аналогично списковому, но вместо квадратных скобок используются круглые. Списковое включение порождает список, а, значит, можно ненароком занять очень много памяти. Генератор же, получающийся в результате применения генераторного выражения, списка не создает, он вычисляет каждое следующее значение строго по требованию (при вызове метода next()).
В следующем примере можно прочесть из файла строки, в которых производятся некоторые замены:
for line in (l.replace("- ", " - ") for l in open("input.dat")): print line
Ничто не мешает использовать итераторы и для записи в файл:
open("output.dat", "w").writelines( l.replace(" - ", " - ") for l in open("input.dat"))
Здесь для генераторного выражения не потребовалось дополнительных скобок, так как оно расположено внутри скобок вызова функции.
Итераторы
Применять для обработки данных явные последовательности не всегда эффективно, так как на хранение временных данных может тратиться много оперативной памяти. Более эффективным решением представляется использование итераторов - специальных объектов, обеспечивающих последовательный доступ к данным контейнера. Если в выражении есть операции с итераторами вместо контейнеров, промежуточные данные не будут требовать много места для хранения - ведь они запрашиваются по мере необходимости для вычислений. При обработке данных с использованием итераторов память будет требоваться только для исходных данных и результата, да и то необязательно вся сразу - ведь данные могут читаться и записываться в файл на диске.
Итераторы можно применять вместо последовательности в операторе for. Более того, внутренне оператор for запрашивает от последовательности ее итератор. Объект файлового типа тоже (построчный) итератор, что позволяет обрабатывать большие файлы, не считывая их целиком в память.
Там, где требуется итератор, можно использовать последовательность.
Работа с итераторами рассматривается в разделе, посвященном функциональному программированию, так как итераторами удобно манипулировать именно в функциональном стиле.
Использовать итератор можно и "вручную". Любой объект, поддерживающий интерфейс итератора, имеет метод next(), который при каждом вызове выдает очередное значение итератора. Если больше значений нет, возбуждается исключение StopIteration. Для получения итератора по некоторому объекту необходимо прежде применить к этому объекту функцию iter() (цикл for делает это автоматически).
В Python имеется модуль itertools, который содержит набор функций, комбинируя которые, можно составлять достаточно сложные схемы обработки данных с помощью итераторов. Далее рассматриваются некоторые функции этого модуля.
Карринг
Библиотека Xoltar toolkit (автор Bryn Keller) включает модуль functional, который позволяет упростить использование возможностей функционального программирования. Модуль functional применяет "чистый" Python. Библиотеку можно найти по адресу: http://sourceforge.net/projects/xoltar-toolkit.
При карринге (частичном применении) функции создается новая функция, задавая некоторые аргументы исходной. Следующий пример иллюстрирует частичное применение вычитания:
from functional import curry def subtract(x, y): return x - y
print subtract(3, 2) subtract_from_3 = curry(subtract, 3) print subtract_from_3(2) print curry(subtract, 3)(2)
Во всех трех случаях будет выведено 1. В следующем примере получается новая функция, подставляя второй аргумент. Вместо другого аргумента вставляется специальное значение Blank:
from functional import curry, Blank def subtract(x, y): return x - y
print subtract(3, 2) subtract_2 = curry(subtract, Blank, 2) print subtract_2(3) print curry(subtract, Blank, 2)(3)
Обработка последовательностей
Многие алгоритмы сводятся к обработке массивов данных и получению новых массивов данных в результате. Среди встроенных функций Python есть несколько для работы с последовательностями.
Под последовательностью в Python понимается любой тип данных, который поддерживает интерфейс последовательности (это несколько специальных методов, реализующих операции над последовательностями, которые в данном курсе обсуждаться не будут).
Следует заметить, что тип, основной задачей которого является хранение, манипулирование и обеспечение доступа к самостоятельным данным называется контейнерным типом или просто контейнером. Примеры контейнеров в Python - списки, кортежи, словари.
Простые генераторы
Разработчики языка не остановились на итераторах. Как оказалось, в интерпретаторе Python достаточно просто реализовать простые генераторы. Под этим термином фактически понимается специальный объект, вычисления в котором продолжаются до выработки очередного значения, а затем приостанавливаются до возникновения необходимости в выдаче следующего значения. Простой генератор формируется функцией-генератором, которая синтаксически похожа на обычную функцию, но использует специальный оператор yield для выдачи следующего значения. При вызове такая функция ничего не вычисляет, а создает объект с интерфейсом итератора для получения значений. Другими словами, если функция должна возвращать последовательность, из нее довольно просто сделать генератор, который будет функционально эквивалентной "ленивой" реализацией. Ленивыми называются вычисления, которые откладываются до самого последнего момента, когда получаемое в результате значение сразу используется в другом вычислении.
Для примера с последовательностью Фибоначчи можно построить такой вот генератор:
def Fib(N): a, b = 0, 1 for i in xrange(N): yield a a, b = b, a + b
Использовать его не сложнее, чем любой другой итератор:
for i in Fib(100): print i,
Однако следует заметить, что программа в значительной степени упростилась.
Рекурсия
В некоторых случаях описание функции элегантнее всего выглядит с применением вызова этой же функции. Такой прием, когда функция вызывает саму себя, называется рекурсией. В функциональных языках рекурсия обычно используется много чаще, чем итерация (циклы).
В следующем примере переписывается функция bin() в рекурсивном варианте:
def bin(n): """Цифры двоичного представления натурального числа """ if n == 0: return [] n, d = divmod(n, 2) return bin(n) + [d]
print bin(69)
Здесь видно, что цикл while больше не используется, а вместо него появилось условие окончания рекурсии: условие, при выполнении которого функция не вызывает себя.
Конечно, в погоне за красивым рекурсивным решением не следует упускать из виду эффективность реализации. В частности, пример реализации функции для вычисления n-го числа Фибоначчи это демонстрирует:
def Fib(n): if n < 2: return n else: return Fib(n-1) + Fib(n-2)
В данном случае количество рекурсивных вызовов растет экспоненциально от числа n, что совсем не соответствует временной сложности решаемой задачи.
В качестве упражнения предлагается написать итеративный и рекурсивный варианты этой функции, которые бы требовали линейного времени для вычисления результата.
Предупреждение: При работе с рекурсивными функциями можно легко превысить глубину допустимой в Python рекурсии. Для настройки глубины рекурсии следует использовать функцию setrecursionlimit(N) из модуля sys, установив требуемое значение N. |
Собственный итератор
Для полноты описания здесь представлен пример итератора, определенного пользователем. Если пример не очень понятен, можно вернуться к нему после изучения объектно-ориентированного программирования:
class Fibonacci: """Итератор последовательности Фибоначчи до N"""
def __init__(self, N): self.n, self.a, self.b, self.max = 0, 0, 1, N
def __iter__(self): # сами себе итератор: в классе есть метод next() return self
def next(self): if self.n < self.max: a, self.n, self.a, self.b = self.a, self.n+1, self.b, self.a+self.b return a else: raise StopIteration
# Использование: for i in Fibonacci(100): print i,
Списковые включения
Для более естественной записи обработки списков в Python 2 была внесена новинка: списковые включения. Фактически это специальный сокращенный синтаксис для вложенных циклов for и условий if, на самом низком уровне которых определенное выражение добавляется к списку, например:
all_pairs = [] for i in range(5): for j in range(5): if i <= j: all_pairs.append((i, j))
Все это можно записать в виде спискового включения так:
all_pairs = [(i, j) for i in range(5) for j in range(5) if i <= j]
Как легко заметить, списковые включения позволяют заменить map() и filter() на более удобные для прочтения конструкции.
В следующей таблице приведены эквивалентные выражения в разных формах:
filter(f, lst) | [x for x in lst if f(x)] |
filter(None, lst) | [x for x in lst if x] |
map(f, lst) | [f(x) for x in lst] |
Ссылки по теме
Статья Д. Мертца http://www-106.ibm.com/developerworks/library/l-prog.html
Часто задаваемые вопросы в comp.lang.functional http://www.cs.nott.ac.uk/~gmh/faq.html
В этой лекции рассматривался принцип
В этой лекции рассматривался принцип построения функциональных программ. Кроме того, было показано, что в Python и его стандартных модулях имеются достаточно мощные выразительные средства для создания функциональных программ. В случае, когда требуются дополнительные возможности, например, карринг, их можно легко реализовать или взять готовую реализацию.
Следует отметить, что итераторы - это практичное продолжение функционального начала в языке Python. Итераторы по сути позволяют организовать так называемые ленивые вычисления (lazy computations), при которых значения вычисляются только когда они непосредственно требуются.