Введение в язык Питон

Атрибуты

Мы видели, что объекты имеют атрибуты, а функция dir() возвращает список этих атрибутов. Иногда, однако, мы просто хотим определить наличие одного или более атрибутов. И если у объекта есть интересующий нас атрибут, мы часто хотим извлечь этот атрибут. Эти задачи решаются с помощью функций hasattr() и getattr(), как показано в следующем примере:


Листинг 31. Наличие атрибута; получение атрибута
>>> print hasattr.__doc__ hasattr(object, name) -> Boolean
Return whether the object has an attribute with the given name. (This is done by calling getattr(object, name) and catching exceptions.)
>>> print getattr.__doc__ getattr(object, name[, default]) -> value
Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case.
>>> hasattr(id, '__doc__') 1 >>> print getattr(id, '__doc__') id(object) -> integer
Return the identity of an object. This is guaranteed to be unique among simultaneously existing objects. (Hint: it's the object's memory address.)

Больше чем основы

Функции, включенные в itertools - залог хорошего начала. Как минимум, они стимулируют программистов на Python к использованию и комбинированию итераторов. В общем, широкое использование итераторов безусловно важно для будущего Python. Но, помимо уже включенных, имеются и другие функции, которые я бы рекомендовал для будущих редакций этого модуля. Ими можно воспользоваться немедленно - разумеется, если они затем будут включены, часть имен и интерфейсов может отличаться.
Одна категория, которая могла бы в общем показаться полезной- это функции, принимающие набор итерируемых аргументов, затем выдающие отдельные элементы из каждого итерируемого аргумента. В противоположность этому, izip() выдает кортежи элементов, а imap() выдает значения, вычисленные из основных элементов. Два очевидных решения, по моему мнению, это chain() и weave(). Первая функция, по существу, подобна конкатенации последовательностей (уместно отложенной). То есть где для простых последовательностей вы использовали бы, например:

for x in ('a','b','c') + (1, 2, 3): do_something(x)

для обычных итерируемых аргументов вы могли бы использовать:

for x in chain(iter1, iter2, iter3): do_something(x)

Питоновская реализация такова:


Листинг 3. Пример реализации chain()

for x in chain(iter1, iter2, iter3): do_something(x)

Вы также могли бы комбинировать итераторы, перемежая их. Встроенный синтаксис, чтобы сделать то же самое с последовательностями, отсутствует, однако сама weave() также прекрасно работает для конечных последовательностей. Возможная реализация приведена ниже (Магнус Лай Хетленд (Magnus Lie Hetland) привел похожую функцию на comp.lang.python):
Листинг 4. Пример реализации weave()

def weave(*iterables): "Intersperse several iterables, until all are exhausted" iterables = map(iter, iterables) while iterables: for i, it in enumerate(iterables): try: yield it.next() except StopIteration: del iterables[i]

Позвольте мне проиллюстрировать поведение weave(), поскольку оно может быть не сразу очевидно из этой реализации:



>>> for x in weave('abc', xrange(4), [10,11,12,13,14]): ... print x, ... a 0 10 b 1 11 c 2 12 13 3 14


Даже после того, как некоторые итераторы использованы, оставшиеся продолжают выдавать величины, пока в какой-то момент все доступное не будет выдано.

Я предложу еще одну возможную функцию itertools. Подход к проблеме в ней во многом позаимствован из функционального программирования. icompose() обладает определенной симметрией с рассмотренной выше ireduce(). Однако, если ireduce() передает в функцию (отложенную) последовательность величин и выдает каждый результат, icompose() применяет последовательность функций к возвращаемой величине каждой предшествующей функции. Вероятное использование ireduce() - передать последовательность событий в долгоживущий объект. icompose() может вместо этого последовательно передавать объект функциям-мутаторам, каждая из которых возвращает новый объект. Если первая - довольно традиционный для объектно-ориентированного программирования способ представления событий, вторая более характерна для подходов функционального программирования.

Ниже - возможная реализация icompose():

Листинг 5. Пример реализации icompose():



def icompose(functions, initval): currval = initval for f in functions: currval = f(currval) yield currval


Что такое функциональное программирование?

Лучше всего начать с труднейшего вопроса - а что, собственно, такое "функциональное программирование (FP)"? Один из возможных ответов - "это когда вы пишете на языке наподобие Lisp, Scheme, Haskell, ML, OCAML, Clean, Mercury или Erlang (или еще на некоторых других)". Этот ответ, безусловно, верен, но не сильно проясняет суть. К сожалению, получить четкое мнение о том, что же такое FP, оказывается очень трудно даже среди собственно функциональных программистов. Вспоминается притча о трех слепцах и слоне. Возможно также определить FP, противопоставив его "императивному программированию" (тому, что вы делаете на языках наподобие C, Pascal, C++, Java, Perl, Awk, TCL и на многих других - по крайнее мере, большей частью).

Хотя автор всеми силами приветствует советы со стороны тех, кто лучше него знает предмет, он мог бы приблизительно охарактеризовать функциональное программирование как обладающее как минимум несколькими из следующих свойств. В языках, называемых функциональными, хорошо поддерживаются нижеперечисленные подходы, а все прочие подходы поддерживаются плохо или не поддерживаются вовсе:
  • Функции - объекты первого класса. Т.е., все, что можно делать с "данными", можно делать и с функциями (вроде передачи функции другой функции в качестве параметра).

  • Использование рекурсии в качестве основной структуры контроля потока управления. В некоторых языках не существует иной конструкции цикла, кроме рекурсии.

  • Акцент на обработке списков (lists, отсюда название Lisp - LISt Processing). Списки с рекурсивным обходом подсписков часто используются в качестве замены циклов.

  • "Чистые" функциональные языки избегают побочных эффектов. Это исключает почти повсеместно распространенный в императивных языках подход, при котором одной и той же переменной последовательно присваиваются различные значения для отслеживания состояния программы.

  • FP не одобряет или совершенно запрещает утверждения (statements), используя вместо этого вычисление выражений (т.е. функций с аргументами). В предельном случае, одна программа есть одно выражение (плюс дополнительные определения).

  • FP акцентируется на том, что должно быть вычислено, а не как.

  • Большая часть FP использует функции "высокого порядка" (функции, оперирующие функциями, оперирующими функциями).

  • Защитники функционального программирования доказывают, что все эти характеристики приводят к более быстрой разработке более короткого и безошибочного кода. Более того, высокие теоретики от компьютерной науки, логики и математики находят, что процесс доказательства формальных свойств для функциональных языков и программ много проще, чем для императивных.

    Что такое интроспекция?

    В повседневной жизни, интроспекция - это проявление самоанализа. Интроспекция основывается на изучении собственных мыслей, ощущений, мотивов и поступков. Великий философ Сократ провел большую часть своей жизни, занимаясь самоанализом и призывая своих сограждан-афинян следовать своему примеру. Он даже утверждал, что для него самого "непроанализированная жизнь не стоит существования" (за более подробной информацией о Сократе см. ссылки, приведенные в ).
    В случае программирования интроспекция означает возможность изучать что-либо, чтобы определить, что это такое, что оно умеет и что может делать. Интроспекция предоставляет программистам огромные гибкость и контроль. Если вы хоть раз работали с языками программирования, которые поддерживают интроспекцию, вы, возможно, испытываете схожие чувства: "непроанализированный объект не стоит воплощения".
    В этой статье рассмотрены интроспективные возможности языка программирования Python. Питоновская поддержка интроспекции пронизывает язык вглубь и вширь. В действительности, Python трудно представить без его интроспективных возможностей. Прочитав эту статью, вы сможете с легкостью "проникнуть в самое сердце" своих объектов Python.
    Мы начнем наше изучение Питоновской интроспекции в самом общем виде, в каком только возможно, а затем окунемся в более сложные технологии. Возможно, кто-нибудь даже заявит, что характеристики, которые являются нашей отправной точкой, не заслуживают звания "интроспективных". Согласимся, что, попадают ли они или нет под определение интроспекции, остается спорным моментом. Цель этой статьи и наша единственная задача - найти ответы на интересующие вопросы.
    Давайте приступим к нашему исследованию, используя Python интерактивно. При запуске Python из командной строки мы входим в оболочку Python, где можем вводить код Python и получать немедленный ответ от интерпретатора Python. (Команды, перечисленные в этой статье, выполнятся надлежащим образом для Python 2.2.2.
    Возможно, вы получите другие результаты и ошибки, если используете более раннюю версию. Последнюю версию Python можно скачать с Web-сайта Python (см. ).)

    Листинг 1. Запускаем интерпретатор Python в интерактивном режиме

    $ python Python 2.2.2 (#1, Oct 28 2002, 17:22:19) [GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2 Type "help", "copyright", "credits" or "license" for more information.

    >>>

    Запустив Python и посмотрев на его приглашение (>>>), вы, возможно, зададитесь вопросом, какие слова распознает Python. В большинстве языков программирования существуют зарезервированные, или ключевые слова, которые имеют специальное назначение в данном языке, и Python не является исключением. Возможно, вы также обратили внимание, что Python предлагает напечатать help для получения более подробной информации. Наверное, мы можем запросить у Python какую-нибудь справку о ключевых словах.

    Что такое Python?

    Python - свободно распространяемый, очень высокоуровневый интерпретируемый язык, разработанный Гвидо ван Россумом (Guido van Rossum). Он сочетает прозрачный синтаксис с мощной (но необязательной) объектно-ориентированной семантикой. Python доступен почти на всех существующих ныне платформах и обладает очень высокой переносимостью между платформами.

    Документирование функций.

    Хорошим стилем является документация каждой функции. Для этого сразу после заголовка поместите краткое описание функции, заключённое в тройные кавычки. Всё содержимое внутри тройных кавычек выводится как есть, в “сыром” виде. Такой способ позволяет легко понять назначение функции, прочитав исходный текст или воспользовавшись специальным сервером документации Питона.


    Дополнительная информация о модулях.

    Модули могут содержать в себе кроме определений функций и некоторый исполняемый код. Этот код не входит ни в какую функцию и исполняется интерпретатором, когда данный модуль подключается ключевым словом import. В исполняемом коде модуля можно использовать любые переменные, которые внутри модуля объявляются глобальными. Но каждый модуль имеет собственную таблицу переменных, так что случайное изменение таких переменных вне модуля исключено. Такой механизм обеспечивает стабильность работы модуля и соответствует принципу сокрытия информации.
    Модули могут импортировать другие модули. Желательно, чтобы операторы import стояли вначале модуля, так как это помогает сразу понять, какие файлы необходимы для его правильной работы.
    Из модуля можно импортировать не все, а только необходимые в данной ситуации функции(или иные объекты), тогда оператор import будет выглядеть так:
    >>> from fibo import fib, fib2 >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377
    При этом стоит отметить, что к другим функциям модуля обратиться будет невозможно, так ка модуль fibo будет неопределён. Для импортирования из модуля всех функций, кроме тех, имена которых начинаются на _, можно использовать import следующим образом:
    >>> from fibo import * >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377

    Другие функции высшего порядка

    Помимо фундаментальной функции curry(), в модуль functional включает ряд интереснейших функций высшего порядка. Более того, не составляет большого труда самому написать свои собственные функции высшего порядка - с помощью или без этого модуля. Во всяком случае, можно воспользоваться идеями, представленными в нем.
    По большей части функции высшего порядка ведут себя как "усовершенствованные" версии стандартных функций map(), filter()
    и reduce(). В большинстве случаев они действуют согласно следующему правилу: "принять функцию или функции и некоторые списки в качестве параметров, затем применить функцию (функции) к списку параметров". Это правило предоставляет потрясающие возможности для программирования. Другой принцип "принять набор функций и создать функцию, комбинирующую их функциональность". И опять-таки, возможны многочисленные вариации. Давайте взглянем, что предоставляет functional.
    Функции sequential() и also() создают функцию, основанную на последовательность других функций. Функции-компоненты затем могут быть вызваны с одинаковым аргументом (аргументами). Главное различие между этими двумя функциями заключается в том, что в sequential() список функций принимается в качестве первого аргумента, а also() принимает список аргументов (каждый из которых должен быть функцией) переменной длины. В большинстве случаев их используют ради побочных эффектов составляющих функций, однако sequential() позволяет опционально задать, результат какой функции вернуть как комбинированное значение:

    #---- Sequential calls to functions (with same args) ----#

    >>> def a(x):

    ... print x,

    ... return "a"

    ...

    >>> def b(x):

    ... print x*2,

    ... return "b"

    ...

    >>> def c(x):

    ... print x*3,

    ... return "c"

    ...

    >>> r = also(a,b,c)

    >>> r



    >>> r(5)

    5 10 15

    'a'

    >>> sequential([a,b,c],main=c)('x')

    x xx xxx

    'c'


    Функции disjoin() и conjoin() схожи с sequential() и also() в том смысле, что они также создают новые функции, которые применяют параметр(ы) к нескольким составляющим функциям. Но disjoin()

    выясняет, возвращает ли хотя бы одна из составляющих функций "истину" (true), а conjoin() выясняет, возвращают ли все

    функции "истину". При этом, когда это возможно, логика "короткого замыкания", поэтому при их вызове часть побочных эффектов может не проявиться. joinfuncs()

    похожа на also(), но, в отличие от нее, возвращает кортеж результатов составляющих функций, а не выбирает одно значение.

    В то время как вышеуказанные функции вызывают много функций с одинаковыми параметрами, функции any(), all() и none_of()

    позволяют вызывать одну и ту же функцию для каждого значения из списка. В общем случае они подобны встроенным функциям map(), filter()

    и reduce(). Но, в отличие от последних, эти функции задают булевы (логические) вопросы касательно набора возвращаемых величин. Например,



    #--------- Ask about collections of return values -------#

    >>> from functional import *

    >>> isEven = lambda n: (n%2 == 0)

    >>> any([1,3,5,8], isEven)

    1

    >>> any([1,3,5,7], isEven)

    0

    >>> none_of([1,3,5,7], isEven)

    1

    >>> all([2,4,6,8], isEven)

    1

    >>> all([2,4,6,7], isEven)

    0


    Особый интерес для тех, кто неравнодушен к математике, представляет функция высшего порядка compose(). Композиция из нескольких функций формирует цепочку, направляя возврат одной функции на вход следующей. Программист, комбинирующий несколько функций, должен следить за тем, чтобы выход и вход соответствовали друг другу - но ведь это так в любом случае, если программист использует возвращаемое значение. Ниже приведен пример, поясняющий сказанное:



    #----------- Creating compositional functions -----------#

    >>> def minus7(n): return n-7

    ...

    >>> def times3(n): return n*3

    ...

    >>> minus7(10)

    3

    >>> minustimes = compose(times3,minus7)

    >>> minustimes(10)

    9

    >>> times3(minus7(10))

    9

    >>> timesminus = compose(minus7,times3)

    >>> timesminus(10)

    23

    >>> minus7(times3(10))

    23



    Другие способы решения проблем с помощью магии

    Существует область программирования, где классы зачастую более важны, чем экземпляры. Например, декларативные мини-языки (declarative mini-languages) - это библиотеки Python, программная логика которых выражена непосредственно в объявлении класса. Дэвид рассматривает их в своей статье "Создание декларативных мини-языков" (). В подобных случаях использование метаклассов для воздействия на процесс создания класса может быть весьма эффективным.
    Одной из декларативных библиотек, основанных на классах, является gnosis.xml.validity. В этой структуре вы объявляете ряд "классов допустимости" ("validity classes"), которые описывают набор ограничений для допустимых документов XML. Эти объявления очень близки к тем, что содержатся в описаниях типа документа (DTDs). Например, документ "диссертация" может быть сконфигурирован с помощью следующего кода:


    Листинг 10. Правила gnosis.xml.validity в simple_diss.py
    from gnosis.xml.validity import * class figure(EMPTY): pass class _mixedpara(Or): _disjoins = (PCDATA, figure) class paragraph(Some): _type = _mixedpara class title(PCDATA): pass class _paras(Some): _type = paragraph class chapter(Seq): _order = (title, _paras) class dissertation(Some): _type = chapter
    Если попытаться создать экземпляр dissertation без надлежащих подэлементов, возбуждается исключение, описывающее ситуацию; подобное имеет место для каждого подэлемента. Правильные подэлементы будут автоматически сгенерированы из более простых аргументов, если существует только один непротиворечивый способ "достроить" тип до корректного состояния.
    Хотя классы допустимости часто (неформально) базируются на предварительно существующем DTD, экземпляры этих классов печатаются как внеконтекстные (unadorned) фрагменты документа XML, например:


    Листинг 11. Создание документа с базовым классом допустимости
    >>> from simple_diss import * >>> ch = LiftSeq(chapter, ('It Starts','When it began')) >>> print ch It Starts When it began

    Используя метакласс для создания классов допустимости, мы можем генерировать DTD из самих объявлений класса (и при этом добавить дополнительный метод в эти классы):

    Листинг 12. Использование метаклассов во время импорта модуля

    >>> from gnosis.magic import DTDGenerator, \ ... import_with_metaclass, \ ... from_import >>> d = import_with_metaclass('simple_diss',DTDGenerator) >>> from_import(d,'**') >>> ch = LiftSeq(chapter, ('It Starts','When it began')) >>> print ch.with_internal_subset() ]> It Starts When it began

    Пакету gnosis.xml.validity ничего неизвестно о DTD и внутренних подмножествах. Эти концепции и возможности всецело представлены метаклассом DTDGenerator без внесения каких-либо изменений в gnosis.xml.validity или simple_diss.py. DTDGenerator не подставляет свой собственный метод .__str__() в классы, которые он создает - вы по-прежнему можете вывести внеконтекстный фрагмент XML - но метакласс мог бы легко модифицировать подобные магические методы.

    Еще о функциональном программировании на Python

    Автор: David Mertz, Ph.D., Applied Metaphysician, Gnosis Software, Inc

    Перевод: Intersoft Lab

    Эта статья продолжает серию статей о функциональном программирования (ФП) на Python. В ней демонстрируется несколько достаточно сложных концепций ФП. Читателю окажется полезным введение в различные подходы программного решения задач.

    Фабрики базовых итераторов

    Все функции в модуле itertools могут быть легко реализованы на чистом Python как генераторы. Основной смысл этого модуля в Python 2.3+ - обеспечить стандартное поведение и имена для некоторых полезных функций. Хотя программисты могли бы написать свои собственные версии, на практике каждый создал бы слегка несовместимые вариации. Помимо этого, еще одна причина - эффективная реализация итераторных комбинаторов на С. Использование функций itertools будет заметно быстрее, чем написание своих собственных комбинаторов. В стандартной документации показаны эквивалентные реализации на чистом Python для каждой функции itertools, так что нет необходимости повторять их в этой статье.
    Функции в модуле itertools настолько базовые - и достаточно четко поименованные - что, вероятно, имеет смысл импортировать все имена из этого модуля. Функция enumerate(), например, вполне могла бы существовать в itertools, но вместо этого является встроенной функцией в Python 2.3+. В частности, вы можете легко выразить enumerate() в терминах функций itertools:

    from itertools import * enumerate = lambda iterable: izip(count(), iterable)

    Давайте рассмотрим несколько функций itertools, которые не используют другие итераторы в качестве базиса, а просто создают итераторы "с нуля". times() возвращает итератор, который выдает идентичный объект множество раз; сама по себе эта возможность довольно удобна, но по-настоящему она хороша при избыточном использовании xrange() и индексной переменной для простого повторения действия. То есть вместо:

    for i in xrange(1000): do_something()

    Вы можете теперь использовать более нейтральное:

    for _ in times(1000): do_something()

    Если второй аргумент не передается в times(), она просто повторно выдает None. Функция repeat() подобна times(), но неограниченно возвращает тот же самый объект. Этот итератор удобен либо если у цикла имеется независимое условие break, либо в комбинаторах, как izip() и imap().
    Функция count() похожа на помесь repeat() и range(). count() неограниченно возвращает последовательно идущие целые (начиная с факультативного аргумента). Однако, с учетом того, что в настоящий момент count() корректно не поддерживает автоматическое преобразование к long при переполнении, вы могли бы с равным успехом по-прежнему использовать xrange(n,sys.maxint); она не является неограниченной буквально, но для большинства целей приводит к тому же. Как и repeat(), count() особенно удобна в других итераторных комбинаторах.

    Форматированный ввод/вывод.

    В Питоне вы можете считывать данные либо с клавиатуры, либо с файла и выводить на монитор или также в файл. Разницы для программиста между файлом и дисплеем нет никакой(они представлены в виде потока байт). В файле обычно нужно сохранять те значения, которые можно впоследствие вновь использовать.
    Форматированный ввод/вывод – это совокупность операций, обеспечивающая ввод/вывод высокого уровня переменных с применением определённого формата ввода/вывода.
    В Питоне имеется несколько способов форматированного ввода/вывода. Самый простой из них – оператор print, печатающий переменные и строковые константы, применяя формат по умолчанию. Другой простой способ вывода данных на дисплей – функция str(), которая выводит любую переменную, используя также формат по умолчанию. Есть также функция repr(), которая выводит данные в машинном(неформатированном) виде. Некоторые типы переменных, которые не могут должным образом отформатироваться функцией str(), обрабатываются схоже с repr(). Пример использования функций str() и repr():
    >>> s = 'Hello, world.' >>> str(s) 'Hello, world.'
    >>> `s`#Такое выражение значит то же, что и repr(s) "'Hello, world.'"#Машинный формат >>> str(0.1)#Переменная с точкой '0.1'
    >>> `0.1`# repr(0.1) '0.10000000000000001'#Ошибка округления чисел с точкой >>> x = 10 * 3.25 >>> y = 200 * 200 >>> s = 'Значение x равно' + `x` + ', а y равен ' + `y` + '...'#Использование операций склеивания
    ... #строк для форматирования >>> print s Значение x равно 32.5, а y равен 40000...
    >>> #Заключение переменных в кавычки работает для всех типов данных ... p = [x, y] >>> ps = repr(p) >>> ps '[32.5, 40000]'
    >>> # Окружение строки дополнительными кавычками ... hello = 'hello, world\n' >>> hellos = `hello` >>> print hellos 'hello, world\n'
    >>> # Можно заключать в кавычки и константные списки ... `x, y, ('Linux', 'BSD')` "(32.5, 40000, ('Linux', 'BSD'))"

    Для форматированного вывода удобно использовать выравнивание по краю. Для этой цели в Питоне предусмотрены следующие функции модуля string: string.rjust(), string.ljust() и string.center(). Эти функции возвращают выровненную строку по правому и левому краю, а также по центру до определённого количества символов. Они принимают два параметра: саму строку и количество символов, зарезервированных под строку(если строка длинее, то она выводится как есть, а для удаления лишних символов воспользуйтесь “string.ljust(x, n)[0:n]#”). На примере это выглядит так:

    >>> import string >>> for x in range(1, 11): ... print string.rjust(`x`, 2), string.rjust(`x*x`, 3), ... # В предыдущем операторе print была поставлена в конце запятая, перевода строки нет: ... print string.rjust(`x*x*x`, 4) ... 1 1 1

    2 4 8

    3 9 27

    4 16 64

    5 25 125

    6 36 216

    7 49 343

    8 64 512

    9 81 729

    10 100 1000

    Для этой же цели можно использовать оператор print со строкой формата, аналогичной Си:

    >>> for x in range(1,11): ... print '%2d %3d %4d' % (x, x*x, x*x*x)# %d означает целое число ...

    1 1 1

    2 4 8

    3 9 27

    4 16 64

    5 25 125

    6 36 216

    7 49 343

    8 64 512

    9 81 729

    10 100 1000

    Очевидно, что число после символа процента определяет количество символов для выравнивания. Спецификатор после процента обозначает тип числа:

    %d Целое число
    %f Число с точкой
    %s Строка
    %c Символ
    %e Число в научном формате
    Функция string.zfill() возвращает строку, дополненную нулями слева, если её длина меньше заданной. string.zfill(строка, заданная_ширина):

    >>> import string >>> string.zfill('12', 5) '00012'

    >>> string.zfill('-3.14', 7) '-003.14'

    >>> string.zfill('3.14159265359', 5) '3.14159265359'

    Если использовать оператор print, то можно для той же цели использовать конструкцию “%ширина_поля.число_знаков_после_запятой”:

    >>> import math >>> print 'Значение числа Пи приблизительно %5.3f.' % math.pi Значение числа Пи приблизительно 3.142.

    Форматировать можно не только строки, но и другие объекты, например, списки, константные списки, заметьте, что в качестве спецификаций форматирования необходимо использовать только константные списки:

    >>> table = {'Иван': 4127, 'Жека': 4098, 'Илья': 7678} >>> for name, phone in table.items(): ... print '%-10s ==> %10d' % (name, phone) ...

    Иван ==> 4098

    Жека ==> 7678

    Илья ==> 4127

    Для оператора print можно передавать аргументы не по порядку в строке формата, а по именам переменных, описанных в строке формата следующим образом “%(имя_переменной)тип_переменной”:

    >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678} >>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table Jack: 4098; Sjoerd: 4127; Dcab: 8637678

    Функции как объекты первого класса

    Приведенные примеры уже засвидетельствовали, хотя и неочевидным образом, статус функций как объектов первого класса в Python. Дело в том, что, создав объект функции оператором lambda, мы произвели чрезвычайно общее действие. Мы имели возможность привязать наш объект к именам pr и namenum в точности тем же способом, как могли бы привязать к этим именам число 23 или строку "spam". Но точно так же, как число 23 можно использовать, не привязывая ни к какому имени (например, как аргумент функции), мы можем использовать объект функции, созданный lambda, не привязывая ни к какому имени. Функция в Python - всего лишь еще одно значение, с которым можно что-то сделать.

    Главное, что мы делаем с нашими объектами первого класса - передаем их во встроенные функции map(), reduce() и filter(). Каждая из этих функций принимает объект функции в качестве первого аргумента. map() применяет переданную функцию к каждому элементу в переданном списке (списках) и возвращает список результатов. reduce() применяет переданную функцию к каждому значению в списке и ко внутреннему накопителю результата; например, reduce(lambda n,m:n*m, range(1,10)) означает 10! (факториал 10 - умножить каждый элемент на результат предыдущего умножения). filter() применяет переданную функцию к каждому элементу списка и возвращает список тех элементов исходного списка, для которых переданная функция вернула значение истинности. Мы также часто передаем функциональные объекты нашим собственным функциям, но чаще некоторым комбинациям вышеупомянутых встроенных функций.
    Комбинируя три этих встроенных FP-функции, можно реализовать неожиданно широкий диапазон операций потока управления, не прибегая к утверждениям (statements), а используя лишь выражения.

    Функции высшего порядка: частичное вычисление функций - карринг (currying)

    Три наиболее общих функций высшего порядка встроены в Python: map(), reduce()
    и filter(). Эти функции используют в качестве (некоторых) своих параметров другие функции - вот почему мы называем их функциями высшего порядка. Другие функции высшего порядка (но не эти три) возвращают объекты-функции (function objects).
    Python всегда предоставлял программистам возможность создавать свои собственные функции высшего порядка благодаря полноправному статусу функций как объектов. Ниже в качестве иллюстрации приведен простой пример:

    #----------- Trivial Python function factory ------------#

    >>> def foo_factory():

    ... def foo():

    ... print "Foo function from factory"

    ... return foo

    ...

    >>> f = foo_factory()

    >>> f()

    Foo function from factory

    Программа Xoltar Toolkit, о которой я упоминал в предыдущих статьях, содержит замечательный набор функций высшего порядка. Большинство этих функций, предоставляемых модулем functional, имеются во множестве традиционных функциональных языках программирования, и их полезность проверена многолетним использованием. Пожалуй, наиболее известная и важная функция высшего порядка традиционно называется curry(). Она названа в честь логика Хаскелла Карри (Haskell Curry), чьим именем назван уже упоминавшийся язык программирования. В основе карринга лежит допущение о том, что (почти) любую функцию можно рассматривать как частично вычисляемую функцию одного аргумента. Для того, чтобы эта идея работала, необходимо чтобы значение, возвращаемое функцией, само могло быть функцией, но возвращаемые функции должны быть уже или ближе к завершению .
    Этот механизм подобен замыканию, о котором я рассказывал в предыдущей статье - каждый вызов каррированой функции добавляет больше данных, необходимых для окончательного вычисления (данные прикрепляются к процедуре). Проиллюстрируем сначала карринг очень простым примером на Haskell, а затем повторим тот же пример на Python с помощью модуля functional:



    #------------- Currying a Haskell computation -----------#

    computation a b c d = (a + b^2+ c^3 + d^4)

    check = 1 + 2^2 + 3^3 + 5^4

    fillOne = computation 1 -- specify "a"

    fillTwo = fillOne 2 -- specify "b"

    fillThree = fillTwo 3 -- specify "c"

    answer = fillThree 5 -- specify "d"

    -- Result: check == answer == 657


    А теперь на Python:



    #------------- Currying a Python computation ------------#

    >>> from functional import curry

    >>> computation = lambda a,b,c,d: (a + b**2 + c**3 + d**4)

    >>> computation(1,2,3,5)

    657

    >>> fillZero = curry(computation)

    >>> fillOne = fillZero(1) # specify "a"

    >>> fillTwo = fillOne(2) # specify "b"

    >>> fillThree = fillTwo(3) # specify "c"

    >>> answer = fillThree(5) # specify "d"

    >>> answer

    657


    Приведем еще один пример, подтверждающий, что между каррингом и замыканием много общего. Для этого, используя curry(), перепишем простую программу расчета налога, код которой можно найти в предыдущей статье:



    #------------ Python curried tax calculations -----------#

    from functional import *

    taxcalc = lambda income,rate,deduct: (income-(deduct))*rate

    taxCurry = curry(taxcalc)

    taxCurry = taxCurry(50000)

    taxCurry = taxCurry(0.30)

    taxCurry = taxCurry(10000)

    print "Curried taxes due =",taxCurry

    print "Curried expression taxes due =", \

    curry(taxcalc)(50000)(0.30)(10000)


    В отличие от замыкания, при использовании curry( ) необходимо заполнять параметры в определенном порядке (слева направо). Но заметьте, в модуль functional также включен класс rcurry(), для которого отсчет начинается с другого конца (справа налево).

    Обратите внимание на второй оператор print

    в этом примере - с одной стороны, это всего лишь тривиальное синтаксическое изменение - можно было бы просто вызвать taxcalc(50000,0.30,10000). Но с другой стороны, благодаря этому становится понятным идея о том, что каждая функция может быть функцией всего одного аргумента - весьма неожиданная идея для тех, кто с эти незнаком.

    Функции.

    В современной теории информатики ключевым является понятие функции – небольшого модуля программного кода, выполняющего определённые действия и обособленного от основного кода программы. Основное достоинство использования функций – это возможность повторного использования программного кода, т.е вы можете вызывать функцию многократно не только в той программе, где она была написана, но, возможно, и в других программах, другими людьми и для других целей. Вы и до этого использовали много раз функции, функции написанные разработчиками Питона, которые очень универсальны и допускают использование в программах различного типа. При этом, вам совершеннно не нужно знать, как, например, работает функция range, вы просто используете её, не заботясь, кем и как она была написана. Этот принцип сокрытия информации позволяет легко использовать стандартные функции, не зная деталей их работы. В Питоне реализована исчерпывающая поддержка функций. В Питоне функция определяется ключевым словом def, имени функции, затем в скобках идут некие параметры, разделяемые запятой, передаваемые в функцию из программы. К этим параметрам можно обращаться внутри функции по их именам. Впрочем, функция может не принимать никаких параметров, тогда в скобках не нужно ничего писать.После определения функции к ней можно обращаться по имени из любого места программы, передавая ей регламентированные в определении параметры:
    >>> def fib(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
    Функции могут не только принимать параметры, но и возвращать результат своей работы.(Это похоже на отношения начальника и подчинённого: программа вызывает функцию, передавая в неё некие параметры, и считывает результат работы с этими параметрами).
    Возврат значения из функции в вызывающую программу осуществляется посредством оператора return.

    >>> def fib(n): # Функция, возвращающая числа Фибоначчи, не превосходящие n ... result = [1]#Этот список будет содержать числа Фибоначчи ... a, b = 0, 1 ... while b < n: ... print b, ... a, b = b, a+b ... result.append(b);#Вставление в результативный список очередного числа ... return result #Возвращение результата ... >>> # Теперь функцию можно вызвать ... fib(2000) Результат выполнения: [1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597]

    В определении функций есть некоторые нюансы. Рассмотрим, к примеру, функцию range(). Её можно вызвать в 3-х разных формах – с один параметром, с двумя и с тремя. Для организации такого поведения совсем необязательно описывать три различные функции, можно применить параметры по умолчанию:

    def ask_ok(prompt, retries=4, complaint='Yes or no, please!'): while 1: ok = raw_input(prompt) if ok in ('д', 'да', 'yes'): return 1 if ok in ('н', 'нет', 'no', 'nop'): return 0 retries = retries - 1 if retries < 0: raise IOError, 'refusenik user' print complaint или в таком виде: i = 5

    def f(arg=i): print arg

    i = 6 f()#Выведет не 6, а 5

    Механизм параметров по умолчанию действует так: если переменная задана при вызове функции, как её параметр, то в функцию передаётся именно это значение, иначе в функцию передаётся значение по умолчанию.

    Внимание: значение по умолчанию оценивается лишь один раз, это играет роль при задании значения по умолчанию спискам, например:

    def f(a, L=[]): L.append(a) return L

    print f(1) print f(2) print f(3)

    Результат работы программы: [1]

    [1, 2]

    [1, 2, 3]

    Если вам нужно, чтобы параметры по умолчанию передавались раздельно, то используйте следующую форму:

    def f(a, L=None): if L is None: L = [] L.append(a) return L

    Функция dir()

    Несмотря на то, что относительно легко найти и импортировать модуль, не так-то просто запомнить, что каждый модуль содержит. И вряд ли вам захочется всякий раз смотреть исходный код, чтобы это выяснить. К счастью, Python предоставляет способ определения содержимого модулей (и других объектов) с помощью встроенной функции dir().
    Функция dir(), вероятно, наиболее известная из всех интроспективных механизмов Python. Она возвращает отсортированный список имен атрибутов для любого переданного в нее объекта. Если ни один объект не указан, dir() возвращает имена в текущей области видимости. Давайте применим dir() к нашему модулю keyword и посмотрим, что она покажет:


    Листинг 16. Атрибуты модуля keyword
    >>> dir(keyword) ['__all__', '__builtins__', '__doc__', '__file__', '__name__', 'iskeyword', 'keyword', 'kwdict', 'kwlist', 'main']
    А как насчет модуля sys, который мы рассматривали выше?


    Листинг 17. Атрибуты модуля sys
    >>> dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdout__', '_getframe', 'argv', 'builtin_module_names', 'byteorder', 'copyright', 'displayhook', 'exc_info', 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'getdefaultencoding', 'getdlopenflags', 'getrecursionlimit', 'getrefcount', 'hexversion', 'last_traceback', 'last_type', 'last_value', 'maxint', 'maxunicode', 'modules', 'path', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version', 'version_info', 'warnoptions']
    Без указания аргумента dir() возвращает имена в текущей области видимости. Заметьте, что keyword и sys присутствуют в этом списке, поскольку мы импортировали их ранее. Импортирование модуля добавляет имя этого модуля в текущую область видимости:


    Листинг 18. Имена в текущей области
    >>> dir() ['__builtins__', '__doc__', '__name__', 'keyword', 'sys']
    Мы упоминали, что функция dir() является встроенной функцией, что означает, что нам не нужно импортировать модуль, чтобы использовать эту функцию.
    Python распознает встроенные функции без нашего участия. И теперь мы видим это имя, __builtins__, возращенное обращением к dir(). Возможно, здесь имеется какая-то связь. Давайте введем имя __builtins__ в приглашение Python и посмотрим, скажет ли нам Python о нем что-нибудь интересное:

    Листинг 19. Что такое __builtins__?

    >>> __builtins__

    Итак, __builtins__, похоже, является в текущей области именем, связанным с объектом модуля по имени __builtins__. (Поскольку модули - это не простые объекты с одиночными значениями, Python показывает информацию об этом модуле внутри угловых скобок.) Заметьте, что, если вы будете искать файл __builtin__.py на диске, то вы ничего не найдете. Этот особый объект модуля создается интерпретатором Python из ниоткуда, так как он содержит элементы, которые всегда доступны интерпретатору. И, несмотря на то, что физически файла не существует, мы все же можем применить нашу функцию dir() к этому объекту, чтобы увидеть все встроенные функции, объекты ошибок и несколько различных атрибутов, которые он содержит:

    Листинг 20. Атрибуты модуля __builtins__

    >>> dir(__builtins__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'IOError', 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeError', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', 'abs', 'apply', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr', 'slice', 'staticmethod', 'str', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']


    Функция dir() работает со всеми типами объектов, включая строки, целые числа, списки, кортежи, словари, функции, экземпляры и методы классов и классы, определенные пользователем. Давайте применим dir() к строковому объекту и посмотрим, что возвратит Python. Как вы можете видеть, даже простая строка Python имеет ряд атрибутов:

    Листинг 21. Атрибуты строки

    >>> dir('this is a string') ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__repr__', '__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

    Поэкспериментируйте со следующими примерами, чтобы выяснить, что они возвратят. Заметьте, что символ # обозначает начало комментария. Все от начала комментария и до конца строки игнорируется Python:

    Листинг 22. Применяем dir() к другим объектам

    dir(42) # Integer (and the meaning of life) dir([]) # List (an empty list, actually) dir(()) # Tuple (also empty) dir({}) # Dictionary (ditto) dir(dir) # Function (functions are also objects)

    Чтобы продемонстрировать динамическую сущность интроспективных возможностей Python, давайте рассмотрим некоторые примеры, применяя dir() к классу, определенному пользователем, и нескольким экземплярам класса. Определим наш собственный класс интерактивно, создадим несколько экземпляров этого класса, и, добавив уникальный атрибут только к одному из этих экземпляров, посмотрим, сможет ли Python со всем этим разобраться. Ниже приведены результаты:

    Листинг 23. Применяем dir() к классам, определенным пользователем, экземплярам класса и атрибутам


    >>> class Person(object): ... """Person class.""" ... def __init__(self, name, age): ... self.name = name ... self.age = age ... def intro(self): ... """Return an introduction.""" ... return "Hello, my name is %s and I'm %s." % (self.name, self.age) ... >>> bob = Person("Robert", 35) # Создать экземпляр Person >>> joe = Person("Joseph", 17) # Создать еще один экземпляр >>> joe.sport = "football" # Присвоить одному экземпляру новый атрибут >>> dir(Person) # Атрибуты класса Person ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__', 'intro'] >>> dir(bob) # Атрибуты bob ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__', 'age', 'intro', 'name'] >>> dir(joe) # Заметьте, что joe имеет дополнительный атрибут ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__', '__setattr__', '__str__', '__weakref__', 'age', 'intro', 'name', 'sport'] >>> bob.intro() # Вызов метода intro экземпляра bob "Hello, my name is Robert and I'm 35." >>> dir(bob.intro) # Атрибуты метода intro ['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__repr__', '__setattr__', '__str__', 'im_class', 'im_func', 'im_self']

    Функция type() помогает нам определить

    Как было указано выше, каждый объект имеет тождественность, тип и значение. Важно заметить, что на один и тот же объект может указывать более одной переменной; с другой стороны, переменные могут ссылаться на объекты, которые выглядят похожими (у них одинаковый тип и значение), но нетождественны. Понятие тождественности объекта приобретает особо важное значение при внесении изменений в объект, таких как добавление элемента в список, что показано в приведенном ниже примере, в котором переменные blist и clist указывают на один и тот же объект списка. Как вы можете видеть, функция id() возвращает уникальный идентификатор для любого заданного объекта:


    Листинг 30. "Рубеж"
    >>> print id.__doc__ id(object) -> integer
    Return the identity of an object. This is guaranteed to be unique among simultaneously existing objects. (Hint: it's the object's memory address.)
    >>> alist = [1, 2, 3] >>> blist = [1, 2, 3] >>> clist = blist >>> clist [1, 2, 3] >>> blist [1, 2, 3] >>> alist [1, 2, 3] >>> id(alist) 145381412 >>> id(blist) 140406428 >>> id(clist) 140406428 >>> alist is blist # Возвращает 1 если True, 0 если False 0 >>> blist is clist # Аналогично 1 >>> clist.append(4) # Добавить элемент в конец списка >>> clist [1, 2, 3, 4] >>> blist # То же самое, поскольку они обе указывают на один и тот же объект [1, 2, 3, 4] >>> alist # А этот исходно только выглядел таким же [1, 2, 3] [1, 2, 3]

    Функциональное программирование на языке Python

    Автор: David Mertz, Ph.D., Applied Metaphysician, Gnosis Software, Inc.

    Перевод: Яков Маркович, ведущий инженер-исследователь ""

    Хотя пользователи обычно думают о Python как о процедурном и объектно-ориентированном языке, он содержит все необходимое для поддержки полностью функционального подхода к программированию.

    В этой статье рассматриваются общие концепции функционального программирования и иллюстрируются способы реализации функционального подхода на Python.

    Функциональное программирование на Python стало отложенным

    В Python 2.2 были введены простые генераторы, а стандартные циклы перепродуманы в терминах итераторов. В Python 2.3 генераторы становятся стандартными (нет необходимости в _future_), а новый модуль itertools добавлен для гибкой работы с итераторами. Модуль itertools, по существу, это набор комбинаторных функций высшего порядка, которые, однако, работают с итераторами с отложенным вычислением, а не с конечными списками. В этой статье Дэвид рассматривает этот новый модуль, показывая выразительную силу, появившуюся с комбинаторными итераторами.

    Функциональные циклы в Python

    Замена циклов на выражения так же проста, как и замена условных блоков. 'for' может быть впрямую переведено в map(). Так же, как и с условным выполнением, нам понадобится упростить блок утверждений до одного вызова функции (мы близки к тому, чтобы научиться делать это в общем случае): #---------- Функциональный цикл 'for' в Python ----------#

    for e in lst: func(e) # цикл на утверждении 'for'

    map(func,lst) # цикл, основанный на map()

    Кстати, похожая техника применяется для реализации последовательного выполнения программы, используя функциональный подход. Т.е., императивное программирование по большей части состоит из утверждений, требующих "сделать это, затем сделать то, затем сделать что-то еще". 'map()' позволяет это выразить так:
    #----- Функциональное последовательное выполнение в Python ----------#

    # создадим вспомогательную функцию вызова функции

    do_it = lambda f: f()

    # Пусть f1, f2, f3 (etc) - функции, выполняющие полезные действия

    map(do_it, [f1,f2,f3]) # последовательное выполнение, реализованное на map()

    В общем случае, вся главная программа может быть вызовом 'map()' со списком функций, которые надо последовательно вызвать, чтобы выполнить программу. Еще одно удобное свойство функций как объектов - то, что вы можете поместить их в список.
    Перевести 'while' впрямую немного сложнее, но вполне получается :
    #-------- Функциональный цикл 'while' в Python ----------#

    # Обычный (основаный на утверждении 'while') цикл

    while :



    if :

    break

    else:



    # Рекурсивный цикл в функциональном стиле

    def while_block():



    if :

    return 1

    else:



    return 0

    while_FP = lambda: ( and while_block()) or while_FP()

    while_FP()

    Наш вариант 'while' все еще требует функцию while_block(), которая сама по себе может содержать не только выражения, но и утверждения (statements).
    Но мы могли бы продолжить дальнейшее исключение утверждений в этой функции (как, например, замену блока 'if/else' в вышеописанном шаблоне на короткозамкнутое выражение). К тому же, обычная проверка на месте (наподобие 'while myvar==7') вряд ли окажется полезной, поскольку тело цикла (в представленном виде) не может изменить какие-либо переменные (хотя глобальные переменные могут быть изменены в while_block()). Один из способов применить более полезное условие - заставить while_block() возвращать более осмысленное значение и сравнивать его с условием завершения. Стоит взглянуть на реальный пример исключения утверждений:

    #---------- Функциональный цикл 'echo' в Python ------------#

    # Императивная версия "echo()"

    def echo_IMP():

    while 1:

    x = raw_input("IMP -- ")

    if x == 'quit':

    break

    else

    print x

    echo_IMP()

    # Служебная функция, реализующая "тождество с побочным эффектом"

    def monadic_print(x):

    print x

    return x

    # FP версия "echo()"

    echo_FP = lambda: monadic_print(raw_input("FP -- "))=='quit' or echo_FP()

    echo_FP()


    Мы достигли того, что выразили небольшую программу, включающую ввод/вывод, циклы и условия в виде чистого выражения с рекурсией (фактически - в виде функционального объекта, который при необходимости может быть передан куда угодно). Мы все еще используем служебную функцию monadic_print(), но эта функция совершенно общая и может использоваться в любых функциональных выражениях , которые мы создадим позже (это однократные затраты). Заметьте, что любое выражение, содержащее monadic_print(x) вычисляется так же, как если бы оно содержало просто x.В FP (в частности, в Haskell) есть понятие "монады" для функции, которая "не делает ничего, и вызывает побочный эффект при выполнении".

    Функциональные инструменты программирования.

    Следующие встроенные функции могут использоваться для многих операций со списками:
    filter() map() reduce()
    Функция filter() может использоваться для выборки значений из списка, условием служит функция пользователя. Синтаксис функции filter(имя_функции, список). Функция возвращает только те элементы списка, для которых значение функции принимает ненулевое(истинное) значение:
    >>> def f(x): return x % 2 != 0 and x % 3 != 0#Выбор некоторых простых чисел ... >>> filter(f, range(2, 25)) [5, 7, 11, 13, 17, 19, 23]
    Функция map() имеет следующий синтаксис: map(имя_функции, список). Map() возвращает список, являющийся результатом работы функции для каждого элемента списка:
    >>> def cube(x): return x*x*x#Куб числа ... >>> map(cube, range(1, 11)) [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
    В функцию map можт быть передано несколько списков, необходимо только соблюдать соответствующее количество параметров в пользовательской функции, например:
    >>> seq = range(8) >>> def square(x): return x*x#Квадрат числа ... >>> map(None, seq, map(square, seq)) [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]
    Функция reduce() имеет следующий синтаксис: reduce(имя_функции, список). Она возвращает единственное значение, являющееся выполнением пользовательской функции над первыми двумя элементами списка, затем с результатом выполнения функции и следующим элементом списка, например прогармма считает сумму всех чисел от 1 до 10:
    >>> def add(x,y): return x+y ... >>> reduce(add, range(1, 11)) 55
    Если в списке только один элемент, возвращается его значение, если список пустой, возникает исключительная ситуация. Если вы хотите, чтобы в случае пустого списка возвращалось какое-либо значение по умолчанию, то передайте его в качестве третьего аргумента функции reduce.

    Функциональные возможности, присущие Python

    Python поддерживает большую часть характеристик функционального программирования, начиная с версии Python 1.0. Но, как большинство возможностей Python, они присутствуют в очень смешанном языке. Так же как и с объектно-ориентированными возможностями Python, вы можете использовать то, что вам нужно, и игнорировать все остальное (пока оно вам не понадобится). В Python 2.0 было добавлено очень удачное "синтаксическое украшение" - списочные встраивания (list comprehensions). Хотя и не добавляя принципиально новых возможностей, списочные встраивания делают использование многих старых возможностей значительно приятнее.

    Базовые элементы FP в Python - функции map(), reduce(), filter() и оператор lambda. В Python 1.x введена также функция apply(), удобная для прямого применения функции к списку, возвращаемому другой. Python 2.0 предоставляет для этого улучшенный синтаксис. Несколько неожиданно, но этих функций и всего нескольких базовых операторов почти достаточно для написания любой программы на Python; в частности, все управляющие утверждения ('if', 'elif', 'else', 'assert', 'try', 'except', 'finally', 'for', 'break', 'continue', 'while', 'def') можно представить в функциональном стиле, используя исключительно функции и операторы. Несмотря на то, что задача реального удаления всех команд управления потоком, возможно, полезна только для представления на конкурс "невразумительный Python" (с кодом, выглядящим как программа на Lisp'е), стоит уяснить, как FP выражает управляющие структуры через вызовы функций и рекурсию.

    Хвостовая рекурсия

    В этой статье мы еще немного вгрызлись в гранит функционального программирования. То, что осталось, меньше (и, возможно, проще) того, что сделано (название этого раздела - небольшая шутка; к сожалению, ее суть пока не разъяснена). Отличный способ дальнейшего освоения множества концепций ФП - просмотреть исходники модуля functional. Модуль прекрасно откомментирован и содержит примеры для большинства функций/классов. В этой статье не были рассмотрены некоторые мета-функции, упрощающие комбинацию и взаимодействие других функций. Они определенно стоят изучения для программиста на Python, стремящегося продолжить изучение подходов функционального программирования.

    И опять о функциональном программировании на Python

    David Mertz, Ph.D., Applied Metaphysician, Gnosis Software, Inc

    Перевод:

    Предыдущие статьи коснулись основных понятий функционального программирования (ФП). Эта статья продолжит обсуждение, иллюстрируя дополнительные возможности, главным образом реализованные в библиотеке Xoltar Toolkit: частичное вычисление функций (Currying,
    карринг), функции высшего порядка (higher-order functions) и другие концепции.

    Имя

    Не все объекты имеют имя, но у тех, у которых оно есть, имя хранится в их атрибуте __name__. Заметьте, что имя выводится из объекта, а не из переменной, которая указывает на этот объект. Следующий пример подчеркивает это различие:


    Листинг 27. Что скрыто в имени
    $ python Python 2.2.2 (#1, Oct 28 2002, 17:22:19) [GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> dir() # Функция dir() ['__builtins__', '__doc__', '__name__'] >>> directory = dir # Создать новую переменную >>> directory() # Работает просто как первоначальный объект ['__builtins__', '__doc__', '__name__', 'directory'] >>> dir.__name__ # Как тебя зовут? 'dir' >>> directory.__name__ # У меня такое же имя 'dir' >>> __name__ # А теперь о чем-нибудь совершенно другом '__main__'
    Модули имеют имена, а сам интерпретатор Python считается модулем верхнего уровня, или основным модулем. Когда вы запускаете Python интерактивно, локальной переменной __name__ присваивается значение '__main__'. Подобным образом, когда вы выполняете модуль Python из командной строки, а не импортируете его в другой модуль, его атрибуту __name__ присваивается значение '__main__', а не действительное имя этого модуля. Так модули могут взглянуть на свое значение __name__, чтобы определиться, используются ли они в качестве поддержки для другой программы или как основное приложение, выполняемое из командной строки. Следующая идиома весьма распространена в модулях Python:


    Листинг 28. Определяем: выполнение или импорт
    if __name__ == '__main__': # Сделать здесь что-нибудь уместное, наподобие вызова # функции main(), определенной где-то в этом модуле. main() else: # Ничего не делать. Этот модуль был импортирован другим # модулем, который хочет воспользоваться этой функцией, # классом или другими полезными битами, которые он определил.

    Информация о поиске модулей.

    Когда вы импортируете модуль, то Питон ищет файл с таким именем не где-нибудь, а в определённых каталогах. Эти каталоги определены в переменной окружения PYTHONPATH вашей операционной системы. Эта переменная имеет структуру, схожую с переменной PATH и так же содержит в себе каталоги, где Питон будет искать модули. При отсутствии этой переменной, Питон будет искать модули в папке, куда были установлены его исполняемые файлы, а так как этот каталог зависит от инсталляции и частенько никаких модулей в нём нет, то удалять или изменять без особой необходимости PYTHONPATH не следует. Доступ к списку каталогов поиска можно получить также из списка sys.path модуля sys(import sys). Этот список можно изменять программно, путём стандартных операций со списками. Ни в коем случае не называйте свои модули так же, как названы стандартные модули Питона, так как это повлечёт за собой труднообнаружимую ошибку. Если подлежащий импорту скрипт находится в том же каталоге, что и вызывающая его программа, то нет необходимости обращаться к sys.path, так как Питон ищет модули также и в текущей директории.

    Интерактивная справочная утилита Python

    Как предлагалось выше, давайте напечатаем help и посмотрим, получим ли мы какую-нибудь информацию о ключевых словах:


    Листинг 2. Запрашиваем у Python справочную информацию
    >>> help Type help() for interactive help, or help(object) for help about object.
    Поскольку мы не знаем, какой объект может содержать ключевые слова, давайте попробуем ввести help(), не указывая какой-то особый объект:


    Листинг 3. Запускаем справочную утилиту
    >>> help()
    Welcome to Python 2.2! This is the online help utility.
    If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://www.python.org/doc/tut/.
    Enter the name of any module, keyword, or topic to get help on writing Python programs and using Python modules. To quit this help utility and return to the interpreter, just type "quit".
    To get a list of available modules, keywords, or topics, type "modules", "keywords", or "topics". Each module also comes with a one-line summary of what it does; to list the modules whose summaries contain a given word such as "spam", type "modules spam".
    help>
    Похоже, что мы немного продвинулись. Давайте введем keywords в приглашении справки:


    Листинг 4. Запрашиваем справку о keywords
    help> keywords
    Here is a list of the Python keywords. Enter any keyword to get more help.
    and elif global or assert else if pass break except import print class exec in raise continue finally is return def for lambda try del from not while
    help> quit
    You are now leaving help and returning to the Python interpreter. If you want to ask for help on a particular object directly from the interpreter, you can type "help(object)". Executing "help('string')" has the same effect as typing a particular string at the help> prompt.
    >>>
    Когда мы напечатали help(), мы увидели приветствие и некоторые указания, а затем приглашение справки. В приглашении мы ввели keywords и получили список ключевых слов Python.
    Получив ответ на свой вопрос, мы вышли из справочной утилиты, увидев короткое прощальное сообщение, и вернулись к приглашению Python.

    Как видно из этого примера, интерактивная справочная утилита Python отображает информацию на различные темы или об отдельном объекте. Эта справочная утилита довольно удобна и действительно использует интроспективные возможности Python. Однако буквальное использование справки не показывает, как она получает информацию. А поскольку задача этой статьи - раскрытие всех интроспективных секретов Python, нам необходимо незамедлительно выйти за рамки справочной утилиты.

    Прежде чем выйти из справки, давайте воспользуемся ею, чтобы получить список доступных модулей. Модули - это просто текстовые файлы, которые содержат код Python и имена которых заканчиваются на .py. Если мы напечатаем в приглашении Python help('modules') или введем modules в приглашении справки, мы получим длинный список доступных модулей, который похож на неполный список, приведенный ниже. Попытайтесь сами установить, какие модули доступны на вашей системе, и понять, почему считается, что Python поставляется "вместе с батарейками".

    Листинг 5. Получаем неполный список доступных модулей

    >>> help('modules')

    Please wait a moment while I gather a list of all available modules...

    BaseHTTPServer cgitb marshal sndhdr Bastion chunk math socket CDROM cmath md5 sre CGIHTTPServer cmd mhlib sre_compile Canvas code mimetools sre_constants <...> bisect macpath signal xreadlines cPickle macurl2path site xxsubtype cStringIO mailbox slgc (package) zipfile calendar mailcap smtpd cgi markupbase smtplib

    Enter any module name to get more help. Or, type "modules spam" to search for modules whose descriptions contain the word "spam".

    >>>

    Исключение команд управления потоком

    Первое, о чем стоит вспомнить в нашем упражнении - то, что Python "замыкает накоротко" вычисление логических выражений. Оказывается, это предоставляет эквивалент блока 'if'/'elif'/'else' в виде выражения. Итак: #------ "Короткозамкнутые" условные вызовы в Python -----#

    # Обычные управляющие конструкции

    if : func1()

    elif : func2()

    else: func3()

    # Эквивалентное "накоротко замкнутое" выражение

    ( and func1()) or ( and func2()) or (func3())

    # Пример "накоротко замкнутого" выражения

    >>> x = 3

    >>> def pr(s): return s

    >>> (x==1 and pr('one')) or (x==2 and pr('two')) or (pr('other'))

    'other'

    >>> x = 2

    >>> (x==1 and pr('one')) or (x==2 and pr('two')) or (pr('other'))

    'two'

    Казалось бы, наша версия условных вызовов с помощью выражений - не более, чем салонный фокус; однако все становится гораздо интересней, если учесть, что оператор lambda может содержать только выражения! Раз, как мы только что показали, выражения могут содержать условные блоки, используя короткое замыкание, выражение lambda позволяет в общей форме представить условные возвращаемые значения. Базируясь на предыдущем примере:
    #--------- Lambda с короткозамкнутыми условными выражениями в Python -------#

    >>> pr = lambda s:s

    >>> namenum = lambda x: (x==1 and pr("one")) \

    ... or (x==2 and pr("two")) \

    ... or (pr("other"))

    >>> namenum(1)

    'one'

    >>> namenum(2)

    'two'

    >>> namenum(3)

    'other'


    Исключение побочных эффектов

    После всей проделанной работы по избавлению от совершенно осмысленных конструкций и замене их на невразумительные вложенные выражения, возникает естественный вопрос - "Зачем?!". Перечитывая мои описания характеристик FP, мы можем видеть, что все они достигнуты в Python. Но важнейшая (и, скорее всего, в наибольшей степени реально используемая) характеристика - исключение побочных эффектов или, по крайней мере, ограничение их применения специальными областями наподобие монад. Огромный процент программных ошибок и главная проблема, требующая применения отладчиков, случается из-за того, что переменные получают неверные значения в процессе выполнения программы. Функциональное программирование обходит эту проблему, просто вовсе не присваивая значения переменным.

    Взглянем на совершенно обычный участок императивного кода. Его цель - распечатать список пар чисел, чье произведение больше 25. Числа, составляющие пары, сами берутся из двух других списков. Все это весьма напоминает то, что программисты реально делают во многих участках своих программ. Императивный подход к этой задаче мог бы выглядеть так:
    #--- Императивный код для "печати произведений" ----#

    # Процедурный стиль - поиск больших произведений с помощью вложенных циклов

    xs = (1,2,3,4)

    ys = (10,15,3,22)

    bigmuls = []

    #...прочий код...

    for x in xs:

    for y in ys:

    #...прочий код...

    if x*y > 25:

    bigmuls.append((x,y))

    #...прочий код...

    #...прочий код...

    print bigmuls

    Этот проект слишком мал для того, чтобы что-нибудь пошло не так. Но, возможно, он встроен в код, предназначенный для достижения множества других целей в то же самое время. Секции, комментированные как "#...прочий код..." - места, где побочные эффекты с наибольшей вероятностью могут привести к ошибкам.
    В любой из этих точек переменные xs, ys, bigmuls, x, y могут приобрести неожиданные значения в гипотетическом коде. Далее, после завершения этого куска кода все переменные могут иметь значения, которые могут ожидаются, а могут и не ожидаться посдедующим кодом. Очевидно, что инкапсуляция в функциях/объектах и тщательное управление областью видимости могут использоваться, чтобы защититься от этого рода проблем. Вы также можете всегда удалять ('del') ваши переменные после использования. Но, на практике, указанный тип ошибок весьма обычен.

    Функциональный подход к нашей задаче полностью исключает ошибки, связанные с побочными эффектами. Возможное решение могло бы быть таким:

    #--- Функциональный код для поиска/печати больших произведений на Python ----#

    bigmuls = lambda xs,ys: filter(lambda (x,y):x*y > 25, combine(xs,ys))

    combine = lambda xs,ys: map(None, xs*len(ys), dupelms(ys,len(xs)))

    dupelms = lambda lst,n: reduce(lambda s,t:s+t, map(lambda l,n=n: [l]*n, lst))

    print bigmuls((1,2,3,4),(10,15,3,22))


    Мы связываем в примере анонимные ('lambda') функции с именами, но это не необходимо. Вместо этого мы могли просто вложить определения. Мы использовали имена как ради большей читаемости, так и потому, что combine() - в любом случае отличная служебная функция (генерирует список всех возможных пар элементов из двух списков). В свою очередь, dupelms() в основном лишь вспомогательная часть combine(). Хотя этот функциональный пример более многословен, чем императивный, при повторном использовании служебных функций код в собственно bigmuls() окажется, вероятно, более лаконичным, чем в императивном варианте.

    Реальное преимущество этого функционального примера в том, что в нем абсолютно ни одна переменная не меняет своего значения. Какое-либо неожиданное побочное влияние на последующий код (или со стороны предыдущего кода) просто невозможно. Конечно, само по себе отсутствие побочных эффектов не гарантирует безошибочность кода, но в любом случае это преимущество.Однако заметьте, что Python, в отличие от многих функциональных языков, не предотвращает повторное привязывание имен bigmuls, combine и dupelms. Если дальше в процессе выполнения программы combine() начнет значить что-нибудь другое - увы! Можно было бы разработать класс-одиночку (Singleton) для поддержки однократного связывания такого типа (напр. 's.bigmuls', etc.), но это выходит за рамки настоящей статьи.

    Еще стоит отметить, что задача, которую мы только что решили, скроена в точности под новые возможности Python 2.0. Вместо вышеприведенных примеров - императивного или функционального - наилучшая (и функциональная) техника выглядит следующим образом:

    #----- Код Python для "bigmuls" с использованием списочных встраиваний (list comprehensions) -----#

    print [(x,y) for x in (1,2,3,4) for y in (10,15,3,22) if x*y > 25]


    Исключения и классы.

    Все исключения являются классами. Чтобы определить новый тип исключения, мы фактически создаём новый класс, наследующий базовому классу Exception:
    >>> class MyError(Exception): ... def __init__(self, value): ... self.value = value ... def __str__(self): ... return `self.value` ... >>> try: ... raise MyError(2*2) ... except MyError, e: ... print 'My exception occurred, value:', e.value ...
    My exception occurred, value: 4
    >>> raise MyError, 'oops!'
    Traceback (most recent call last):
    File "", line 1, in ?
    __main__.MyError: 'oops!'
    Обычно в классах-исключениях делают небольшие конструкторы __init__, которые инициализируют поля класса, чтобы к ним впоследствие можно было бы обратиться:
    class Error(Exception): """Базовый класс для исключений в модуле.""" pass
    class InputError(Error): """Ошибка ввода данных.
    Поля: expression – выражение, где произошла ошибка message – объяснение ошибки """
    def __init__(self, expression, message): self.expression = expression self.message = message
    class TransitionError(Error): """Возникает при неверной операции
    Поля: previous – состояние до начала плохой операции next – состояние после операции message – объяснение, почему такая операция недопустима """
    def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message
    |


    Исключительные ситуации.

    Возникают либо при ошибке работы пользователя, либо при ошибке в логике программы. Исключительных ситуаций существует много: деление на нуль, использование несуществующего модуля или переменной, многие стандартные функции также генерируют исключения в случае неверной работы. Приведём примеры таких ситуаций:
    >>> 10 * (1/0) Traceback (most recent call last):
    File "", line 1, in ?
    ZeroDivisionError: integer division or modulo #Деление на нуль
    >>> 4 + spam*3 Traceback (most recent call last):
    File "", line 1, in ?
    NameError: spam
    #Нет такого имени переменной >>> '2' + 2 Traceback (most recent call last):
    File "", line 1, in ?
    TypeError: illegal argument type for built-in operation#Смешение разных типов
    Конечно, всё было бы отлично, если пользователя удовлетворяли подобные сообщения, я лично в этом сильно сомневаюсь. Кроме этого, исключения могут генерировать ся в любой момент программы, что может вызвать потерю данных. По всем этим причинам желательно предусматривать возможность перехватывать исключения. Для этого используется блок try, который исполняет операторы внутри блока, но если возбуждается исключение, то оператор try ищет обработчик исключения(прерывая исполнение блока кода внутри try), обозначаемый except имя_исключения . Если обработчик не найден, то он ищется в других блоках try, если он не найден, то возникает непредвиденное(unhandled) исключение, которое отображается, как в предыдущем примере. Блок try выполняется до конца при отсутствии исключительных ситуаций, блоки except при этом пропускаются:
    >>> while 1: ... try: ... x = int(raw_input("Введите число ")) #Здесь может возникнуть исключение ... break #Если всё правильно, то выходим из бесконечного while
    ... except ValueError: #А вот здесь обрабатывается исключение неверного формата числа ... print ''Ой-ой. Неправильное число. Попробуйте снова..."
    Оператор except может принимать несколько имён исключений, оформленных в скобках через запятую:

    ... except (RuntimeError, TypeError, NameError): ... pass

    Обработчики исключений могут принимать некоторые параметры, причём их число и последовательность специфична для каждого типа исключения. Также есть возможность создавать обработчик исключений по умолчанию, который обрабатывает все исключения, для которых не был определён конкретный обработчик:

    import string, sys

    try: f = open('myfile.txt') s = f.readline() i = int(string.strip(s)) except IOError, (errno, strerror): print "I/O ошибка(%s): %s" % (errno, strerror) except ValueError: print "Не могу преобразовать это в целое." except: print "Неожиданная ошибка:", sys.exc_info()[0] #Имя последнего исключения raise #Возбуждение данного исключения ещё раз(см. далее)

    В блоке try имеется дополнительный оператор else, который выполняется, при отсутствии исключений в блоке try:

    for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'не могу открыть', arg else: print arg, 'имеет длину в ', len(f.readlines()), ' строк' f.close()

    Обработчики исключений способны обрабатывать исключения не только непосредственно в блоке try, но и в функциях, вызываемых из этого блока, например:

    >>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() #Функция, вызывающая исключительную ситуацию ... except ZeroDivisionError, detail: ... print 'Возбуждено исключение:', detail ... Возбуждено исключение: integer division or modulo

    Использование комбинаторных функций в модуле itertools

    Автор: Дэвид Мертц (David Mertz), разработчик, Gnosis Software, Inc.

    Перевод:


    Использование лямбда функций.

    Лямбда функции пришли в Питон из языка Лисп и могут показаться необычными программисту на Си. Лямбда функции – это небольшие функции, которые создают другие функции, на своей основе. Чтобы быть более понятным, приведу такой пример: lambda a, b: a+b – вычисляет сумму двух своих аргументов. На основе функции, возвращающей lambda можно построить другие функции, например:
    >>> def make_incrementor(n): ... return lambda x: x + n#x – параметр, который передаётся в порождённую функцию f(x) ... >>> f = make_incrementor(42) >>> f(0) 42
    >>> f(1) 43

    Использование списков, как очередей.

    Очередь – это другая структура данных, организованнная по принципу “Первым пришёл, первым ушёл”(FIFO). В Питоне нет встроенного класса очереди, но вы можете также использовать списки Питона: для добавления элемента используйте append, а для получения последнего – метод pop(0)(метод pop удаляет элемент). Например:
    >>> queue = [1, 2, 3] >>> queue.append(4) # Terry arrives >>> queue.append(5) # Graham arrives >>> queue.pop(0) 5
    >>> queue.pop(0) 4
    >>> queue [1, 2, 3]

    Использование списков, как стеков.

    Стек – это структура данных, организованнная по принципу “Последним пришёл, первым ушёл”(LIFO). В Питоне нет встроенного класса стека, но вы можете использовать списки Питона так, как они были бы стеками: для добавления элемента используйте append, а для получения последнего – метод pop() без аргумента(метод pop удаляет элемент). Например:
    >>> stack = [3, 4, 5] >>> stack.append(6) >>> stack.append(7) >>> stack [3, 4, 5, 6, 7]
    >>> stack.pop() 7
    >>> stack [3, 4, 5, 6]
    >>> stack.pop() 6
    >>> stack.pop() 5
    >>>> stack [3, 4]

    Экземпляры

    Хотя функция type() и выдает тип объекта, с помощью функции isinstance() мы также можем выяснить, является ли объект экземпляром определенного типа или определенного пользователем класса:


    Листинг 33. Ты один из них?
    >>> print isinstance.__doc__ isinstance(object, class-or-type-or-tuple) -> Boolean
    Return whether an object is an instance of a class or of a subclass thereof. With a type as second argument, return whether that is the object's type. The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for isinstance(x, A) or isinstance(x, B) or ... (etc.).
    >>> isinstance(42, str) 0 >>> isinstance('a string', int) 0 >>> isinstance(42, int) 1 >>> isinstance('a string', str) 1

    Как изучать объекты Python

    Интроспекция предоставляет полезную информацию об объектах вашей программы. Python, динамический, объектно-ориентированный язык программирования, обеспечивает потрясающую поддержку интроспекции. В этой статье демонстрируются многие из его возможностей, начиная самыми простыми видами помощи и заканчивая более сложными формами изысканий.

    Ключи.

    В Питоне есть другая возможность передавать значения в функцию – через ключи в формате 'ключ=значение', например:
    def parrot(voltage, state='крутой', action='шок', type='Волнистый'): print "-- "Попугай не будет", action, print "если вы пропустите ток в", voltage, "вольт через него." print "-- Попугай", type print "-- Это", state, "!"
    Такую функцию можно вызвать любым из способов ниже:
    parrot(1000) parrot(action = 'А-а-а-а-а-у-у-ш-ш-ш', voltage = 1000000) parrot('тысячу', state = 'заболеет') parrot('миллион', 'вернётся к жизни', 'прыжок')
    А вот таким образом функцию вызывать нельзя:
    parrot() # требуемый аргумент опущен parrot(voltage=5.0, 'dead') #аргумент не ключ использован как ключ parrot(110, voltage=220) # повторение значение одного и того же аргумента parrot(actor='John Cleese') # неизвестный ключ
    В общем, список ключей может содержать ключи со значениями в любом порядке, причём неважно, имеет ли данный аргумент значение по умолчанию или нет. Но важно помнить, что нельзя дублировать аргумент, переданный в функцию, ключом. Например, вызов функции так, как показано ниже вызовет по этой причине ошибку:
    >>> def function(a): ... pass ...
    >>> function(0, a=0) ERROR: ключ переопределён
    Если в заголовке функции присутствует параметр в формате **имя, то в него включаются все ключи, которые были переданы в функцию, но не были определены в её заголовке. С этим параметром может также употребляться другой параметр, имеющий формат *имя. В него передаются аргументы, не входящие в список обязательных параметров функции. Учтите, что аргумент *имя должен стоять перед **имя:
    def cheeseshop(kind, *arguments, **keywords): print "-- У вас есть какие-нибудь", kind, '?' print "-- Нет, всех съели" for arg in arguments: print arg print '-'*40 for kw in keywords.keys(): print kw, ':', keywords[kw]
    Функция может быть вызвана, например, так:
    cheeseshop('Зонты', "Очень жаль.", "Действительно ужасно жаль, товарищ.", client='Михаил Горбачёв', shopkeeper='Борис Ельцин', sketch='Белый Дом')
    и вот каким будет результат работы:
    -- У вас есть какие-нибудь зонты?
    -- Нет, всех съели
    Очень жаль.
    Действительно, ужасно жаль.
    ----------------------------------------
    client : Михаил Горбачёв
    shopkeeper : Борис Ельцин
    sketch : Белый Дом

    Комбинаторные функции

    Несколько реальных комбинаторных функций в itertools уже были вскользь упомянуты. ifilter(), izip() и imap() ведут себя именно так, как следовало бы ожидать от соответствующих функций над последовательностями. ifilterfalse() - вспомогательная функция, так что вам не нужно инвертировать предикатную функцию в lambda и def (и это значительно экономит накладные расходы на вызов функции). Но функционально вы могли бы определить ifilterfalse() (приблизительно, игнорируя предикат None) как:

    def ifilterfalse(predicate, iterable): return ifilter(lambda predicate: not predicate, iterable)

    Функции dropwhile() и takewhile() разделяют итератор предикатом. Первая игнорирует возвращаемые элементы, пока предикат не выполнен, а вторая выдает, пока предикат выполняется. dropwhile пропускает неопределённое число первоначальных элементов итератора, чтобы он смог начать итерации только после задержки. takewhile() запускается немедленно, но завершает итератор, если передаваемый предикат становится true.
    Функция islice(), по существу, просто итераторная версия среза списка. Вы можете задать начало, останов и шаг, как с регулярными срезами. Если начало задано, ряд элементов отбрасывается, пока передаваемый итератор не достигнет требуемого элемента. Это еще один случай, когда, как я думаю, возможно усовершенствовать Python - самое лучшее для итераторов было бы просто распознать разрезы, как делают списки (в качестве синонима того, что делает islice()).
    Последняя функция starmap() - это легкая вариация imap(). Если функция, которая передается как аргумент, принимает набор аргументов, переданный итератор должен выдавать кортежи надлежащего размера. По существу, это то же самое, что и imap() с несколькими передаваемыми итерируемыми аргументами, только с набором итерируемых аргументов, предварительно комбинированных izip().

    Компиляция скриптов на Питоне.

    Питон является интерпретируемым языком, то есть интерпретатор считывает из текста некие операторы и сразу же их выполняет, при этом осуществляется контроль за синтаксисом языка. Есть способ ускорить загрузку скриптов на Питоне, это особенно важно для часто используемых модулей. Для этого существуют компилированные модули(своеобразный аналог байт-кода языка Java). Если в каталоге существуют два файла с одним именем файла: один с расширением .py, а другой - .pyc(компилированный), то компилированный файл будет использоваться только в том случае, когда источником для его компиляции являлся файл .py(одинаковая версия модуля). При исполнении какого-либо модуля, файл .pyc генерируется автоматически, так что об этом вам не приходится заботиться.

    Константные списки.

    Мы до сих пор рассматривали списки, т.е. последовательности, элементы которых могут быть доступны для изменения по отдельности. Другим типом последовательности является константный список(tuple). Такой список в теле программы обозначается списком элементов через запятую, может содержать в себе элементы различных типов, но изменить их через индекс не удастся(см. строки). Константные списки могут содержать в себе в качестве элементов другие последовательности. Для списков константного типа определены операции присваивания, склеивания +, индексации(только чтение). Использовать такие списки удобно при доступе к базам данных(одинаковые поля) и системам координат. Рассмотрим примеры константных списков:
    >>> t = 12345, 54321, 'привет!' >>> t[0] 12345
    >>> t (12345, 54321, 'привет!')
    >>> #Вложенный список: ... u = t, (1, 2, 3, 4, 5) >>> u ((12345, 54321, 'привет!'), (1, 2, 3, 4, 5))#Выходные данные тоже в скобках
    Внимание: для создания пустого константного списка просто присвойте ему значение (), для создания списка, состоящего из одного элемента сделайте следующее: “имя_списка = единственный_элемент,” - не забудьте запятую в конце(выглядит не слишком приятно):
    >>> empty = () >>> singleton = 'привет', # <-- не забыть бы про запятую >>> len(empty) 0
    >>> len(singleton) 1
    >>> singleton ('привет',)
    Создание константного списка из других переменных, вроде “список = 'привет', 'пока', a” является примером упаковки переменных в константный список. Можно провести обратное действие – распаковку, если такой список присваивается нескольким переменным(их число должно быть равно числу элементов в константном списке):
    >>> x, y, z = t
    Внимание: несколько элементов всегда упаковываются в константный список, в то время как распакована вышеописанным образом может быть абсолютно любая последовательность

    Краткий обзор объектно-ориентированного программирования (ООП)

    Давайте, потратив полминуты, вспомним, что такое ООП. В языке объектно-ориентированного программирования можно определять классы, задача которых - объединить связанные данные и поведение. Эти классы могут наследовать некоторые или все свойства своих родителей, они также определяют свои собственные атрибуты (данные) или методы (поведение). В результате, классы, как правило, выступают в качестве шаблонов для создания экземпляров (которые время от времени называют просто объектами). Различные экземпляры одного и того же класса обычно имеют разные данные, хотя они будут представлены в одинаковом виде, например, у обоих объектов класса 'Employee' bob и jane есть .salary и .room_number, но значения room (комната) и salary (жалование) у каждого различны.
    Некоторые объектно-ориентированные языки программирования, включая Python, предусматривают интроспективные (или рефлексивные) объекты. Другими словами, интроспективный объект может сам себя описывать: к какому классу принадлежит этот экземпляр? Кто предки этого класса? Какие методы и атрибуты доступны объекту? Интроспекция позволяет функции или методу, управляющему объектами, принимать решения, основываясь на том, какой вид объекта передается. Даже без интроспекции функции часто ветвятся, опираясь на данные экземпляра - например, маршрут к jane.room_number отличается от пути к bob.room_number, поскольку они в "различных комнатах" (значения room у них различны). С помощью интроспекции также можно безошибочно вычислить bonus (премиальные) jane, пропустив это вычисление для bob, например, потому что у jane есть атрибут .profit_share или из-за того, что bob является экземпляром производного класса Hourly(Employee).

    Метаклассы: решение, требующее проблемы?

    "Метаклассы - большая магия, чем нужно 99% пользователей. Если вы задаетесь вопросом, нужны ли они вам, значит, они вам не нужны (те, кому они действительно нужны, точно это знают, и им не требуется объяснение, зачем)". Тим Питерс (Tim Peters), крупнейший авторитет в области Python

    Методы (классов), как и обычные функции, могут возвращать объекты. В этом смысле очевидно, что фабрики классов столь же могут быть классами, как и функциями. В частности, Python 2.2+ предоставляет специальный класс, называемый type, который именно и есть такая фабрика классов. Разумеется, читатели узнают в type() менее претенциозную встроенную функцию более ранних версий Python - к счастью, поведение старой функции type() поддерживается классом type (другими словами, type(obj) возвращает тип/класс объекта obj). Новый класс работает в качестве фабрики классов точно так же, как прежде делала функция new.classobj:


    Листинг 3. type в качестве метакласса фабрики классов
    >>> X = type('X',(),{'foo':lambda self:'foo'}) >>> X, X().foo() (, 'foo')
    Но поскольку теперь type - это (мета)класс, вы можете создать от него производный класс:


    Листинг 4. Потомок type как фабрика классов
    >>> class ChattyType(type): ... def __new__(cls, name, bases, dct): ... print "Allocating memory for class", name ... return type.__new__(cls, name, bases, dct) ... def __init__(cls, name, bases, dct): ... print "Init'ing (configuring) class", name ... super(ChattyType, cls).__init__(name, bases, dct) ... >>> X = ChattyType('X',(),{'foo':lambda self:'foo'}) Allocating memory for class X Init'ing (configuring) class X >>> X, X().foo() (, 'foo')
    Магические методы .__new__() и .__init__() являются специальными, но концептуально они такие же, как и у любого другого класса. Метод .__init__() позволяет конфигурировать созданный объект; метод .__new__() разрешает настраивать его создание. Последний, разумеется, не используется широко, но существует для каждого класса нового стиля Python 2.2 (обычно наследуется, а не подменяется).

    У потомков type есть одно свойство, которое необходимо учитывать; на нем ловятся все, кто впервые использует метаклассы. Первый аргумент в методах обычно называется cls, а не self, поскольку эти методы обрабатывают созданный класс, а не метакласс. На самом деле, в этом нет ничего особенного; все методы связываются со своимиэкземплярами, а экземпляр метакласса является классом. Неспециальное имя делает это более очевидным:

    Листинг 5. Прикрепление методов класса к созданным классам

    >>> class Printable(type): ... def whoami(cls): print "I am a", cls.__name__ ... >>> Foo = Printable('Foo',(),{}) >>> Foo.whoami() I am a Foo >>> Printable.whoami() Traceback (most recent call last): TypeError: unbound method whoami() [...]

    Вся эта удивительно непримечательная технология сопровождается некими синтаксическими украшениями, упрощающими работу с метаклассами и одновременно запутывающими новых пользователей. В этом дополнительном синтаксисе есть несколько элементов. Порядок интерпретации этих новых вариаций мудреный. Классы могут наследовать метаклассы от своих предков - заметьте, что это не одно и то же, что наличие метаклассов в качестве предков (еще одно обычное заблуждение). Для классов старого стиля определение глобальной переменной __metaclass__ приводит к использованию метаклассса, определенного пользователем. Однако по большей части самый безопасный подход - задать атрибут класса __metaclass__ для класса, который хочет быть созданным по метаклассу, определенному пользователем. Вы должны задать эту переменную в самом определении класса, поскольку метакласс не используется, если этот атрибут задан позднее (после того как объект класса уже был создан). Например:

    Листинг 6. Задание метакласса с атрибутом класса

    >>> class Bar: ... __metaclass__ = Printable ... def foomethod(self): print 'foo' ... >>> Bar.whoami() I am a Bar >>> Bar().foomethod() foo

    Метапреимущества

    Пакет gnosis.magic содержит несколько утилит для работы с метаклассами, а также некоторые примеры метаклассов, которые можно применять в аспектно-ориентированном программировании. Наиболее важная из этих утилит - import_with_metaclass(). Эта функция, задействованная в предыдущем примере, позволяет импортировать произвольный модуль, создавая все классы этого модуля с использованием метакласса, определенного пользователем, а не type. Какую бы новую возможность вы ни захотели задать в этом модуле, она может быть определена в метаклассе, который вы создаете (или получаете). gnosis.magic содержит некоторые подключаемые метаклассы сериализации; другой пакет мог бы включать возможности трассировки, объектную персистентность, регистрацию исключений или же что-нибудь еще.
    Функция import_with_metaclass() иллюстрирует некоторые возможности программирования метаклассов:


    Листинг 13. Функция import_with_metaclass() из [gnosis.magic]
    def import_with_metaclass(modname, metaklass): "Module importer substituting custom metaclass" class Meta(object): __metaclass__ = metaklass dct = {'__module__':modname} mod = __import__(modname) for key, val in mod.__dict__.items(): if inspect.isclass(val): setattr(mod, key, type(key,(val,Meta),dct)) return mod
    В этой функции стоит обратить внимание на стиль - обыкновенный класс Meta создан с использованием заданного метакласса. Но как только Meta добавлен в качестве предка, его потомки также создаются с помощью этого метакласса. В принципе, класс, подобный Meta, мог бы предоставлять и генератор метакласса, и ряд наследуемых методов - эти два аспекта наследования являются ортогональными.

    "Метапрограммный" ответ

    Базовая система ООП, очерченная выше, является достаточно мощной. Однако, в этом описании один момент не получил должного внимания: в Python (и других языках) сами классы являются объектами, которые можно передавать и подвергать интроспекции. Но поскольку объекты, как отмечалось, создаются с использованием классов в качестве шаблонов, то что же является шаблоном для создания классов? Разумеется, метаклассы.
    В Python всегда были метаклассы. Однако, технология, задействованная в метаклассах, стала гораздо более очевидной с выходом Python 2.2. А именно, в версии 2.2 Python перестал быть языком только с одним специальным (обычно невидимым) метаклассом, который создавал каждый объект класса. Теперь программисты могут наследоваться от встроенного метакласса type и даже динамически генерировать классы с различными метаклассами. Разумеется, только то, что вы можете манипулировать метаклассами на Python 2.2, еще не объясняет, зачем вам это.
    Более того, вам не нужно использовать метаклассы, определенные пользователем, чтобы управлять созданием классов. Несколько менее головоломная концепция - фабрика классов (class factory): обыкновенная функция может возвращать класс, который был динамически создан в пределах тела функции. В традиционном синтаксисе Python вы можете написать:


    Листинг 1. Традиционная фабрика классов на Python 1.5.2
    Python 1.5.2 (#0, Jun 27 1999, 11:23:01) [...] Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> def class_with_method(func): ... class klass: pass ... setattr(klass, func.__name__, func) ... return klass ... >>> def say_foo(self): print 'foo' ... >>> Foo = class_with_method(say_foo) >>> foo = Foo() >>> foo.say_foo() foo
    Функция фабрики class_with_method() динамически создает и возвращает класс, содержащий метод/функцию, передаваемую в эту фабрику. Сам класс обрабатывается в пределах тела функции до того, как он возвращен. Модуль new обеспечивает более лаконичное выражение, но без возможности определения пользователем дополнительного кода в пределах тела фабрики классов. Например:


    Листинг 2. Фабрика классов в модуле new
    >>> from new import classobj >>> Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'}) >>> Foo2().bar() 'bar' >>> Foo2().say_foo() foo
    Во всех этих случаях поведение класса (Foo, Foo2) не записано непосредственно в виде кода, а создается посредством вызова во время исполнения функций с вычисляемыми аргументами. Следует подчеркнуть, что динамически создаются именно сами классы, а не просто экземпляры.

    Модуль keyword

    Давайте вернемся к нашему вопросу о ключевых словах Python. Даже несмотря на то, что справка предоставила нам список ключевых слов, оказывается, что часть информации справки жестко закодирована. Список ключевых слов, получается, жестко закодирован, что в конце концов не слишком интроспективно. Давайте посмотрим, сможем ли мы получить такую информацию непосредственно из одного из модулей в стандартной библиотеке Python. Если мы напечатаем help('modules keywords') в приглашении Python, то увидим следующее:


    Листинг 14. Запрашиваем справку о модулях с ключевыми словами
    >>> help('modules keywords')
    Here is a list of matching modules. Enter any module name to get more help.
    keyword - Keywords (from "graminit.c")
    Похоже, что модуль keyword содержит ключевые слова. Открыв файл keyword.py в текстовом редакторе, можно увидеть, что Python действительно создает список ключевых слов, явно доступных в виде атрибута kwlist модуля keyword. В модуле keyword также приводятся комментарии о том, что этот модуль генерируется автоматически на основе исходного кода самого Python, гарантируя точность и полноту списка ключевых слов:


    Листинг 15. Список ключевых слов модуля keyword
    >>> import keyword >>> keyword.kwlist ['and', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'yield']

    Модуль pickle

    Для чтения/записи в файл используются строки. То есть вам необходимо прежде чем записать что-либо в файл преобразовать это что-то в строку. С числовыми значениями всё легко: в модуле string есть множество функций для преобразования чисел в строки и строк в числа(например, строка в целое “string.atoi()”). Но как быть со сложными объектами: списками, например. Для этой цели в Питоне существует особый модуль: pickle, который может преобразовать в строку любой объект(даже некоторый формат кода самого Питона!) и записать его в файл. При этом при помощи модуля pickle можно выполнить обратную операцию из строки, генерированной pickle, в переменную(модуль сам распознает её тип). Причём, функционирование модуля pickle одинаково в любой реализации Питона, поэтому использование подобного механизма способствует переносимости кода и помогает повторно использовать какие-либо сложные объекты. Пример использования модуля pickle(полное описание я пока не перевёл):
    pickle.dump(x, f)#Выгрузка содержимого x в файл f x = pickle.load(f)#Загрузка x из файла f.


    Модуль sys

    Один из модулей, предоставляющих внутреннюю информацию о самом Python, - это модуль sys. Вы используете модуль, импортируя его и ссылаясь на его содержимое (как, например, переменные, функции и классы) с помощью нотации точка (.). Модуль sys содержит множество переменных и функций, которые предоставляют интересную и подробную информацию о текущем интерпретаторе Python. Давайте рассмотрим некоторые из них. И снова мы собираемся, запустив Python интерактивно, вводить команды в приглашении Python. Первое, что мы сделаем - это импортируем модуль sys. Затем введем переменную sys.executable, которая содержит путь к интерпретатору Python:


    Листинг 6. Импортируем модуль sys
    $ python Python 2.2.2 (#1, Oct 28 2002, 17:22:19) [GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.executable '/usr/local/bin/python'
    Если мы введем строку кода, который не содержит ничего кроме имени объекта, Python ответит, показывая представление этого объекта, которое - для простых объектов - как правило, есть значение этого объекта. В этом случае, поскольку выведенное значение заключено в кавычки, мы можем предположить, что sys.executable, вероятно, строковый объект. Позже мы изучим другие, более точные, способы определения типа объекта, однако просто ввод имени объекта в приглашении Python - это быстрый и легкий вид интроспекции.
    Давайте рассмотрим некоторые другие полезные атрибуты модуля sys.
    Переменная platform сообщает, в какой операционной системе мы работаем:


    Атрибут sys.platform
    >>> sys.platform 'linux2'
    Текущая версия Python доступна и в виде строки, и в виде кортежа (кортеж содержит последовательность объектов):


    Листинг 8. Атрибуты sys.version и sys.version_info
    >>> sys.version '2.2.2 (#1, Oct 28 2002, 17:22:19) \n[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)]' >>> sys.version_info (2, 2, 2, 'final', 0)
    Переменная maxint показывает наибольшее допустимое целое значение:

    Атрибут sys.maxint

    >>> sys.maxint 2147483647

    Переменная argv - это список, содержащий параметры командной строки, если она была задана. Первый элемент, argv[0], это путь к скрипту, который был запущен. Когда мы работаем с Python интерактивно, его значением является пустая строка:

    Листинг 10. Атрибут sys.argv

    >>> sys.argv ['']

    Если мы запустим другую оболочку Python, как, например, PyCrust (за более подробной информацией о PyCrust см. ссылку, приведенную в ), то увидим что-нибудь вроде этого:

    Листинг 11. Атрибут sys.argv при использовании PyCrust

    >>> sys.argv[0] '/home/pobrien/Code/PyCrust/PyCrustApp.py'

    Переменная path - это путь поиска модуля, список каталогов, в которых Python будет искать модули во время импорта. Пустая строка, ' ', в первой позиции относится к текущему каталогу:

    Листинг 12. Атрибут path

    >>> sys.path ['', '/home/pobrien/Code', '/usr/local/lib/python2.2', '/usr/local/lib/python2.2/plat-linux2', '/usr/local/lib/python2.2/lib-tk', '/usr/local/lib/python2.2/lib-dynload', '/usr/local/lib/python2.2/site-packages']

    Переменная modules - это словарь, который отображает имена модулей в объекты модулей для всех загруженных в текущий момент модулей. Как можно видеть, Python загружает определенные модули по умолчанию:

    Листинг 13. Атрибут sys.modules

    >>> sys.modules {'stat': , '__future__': , 'copy_reg': , 'posixpath': , 'UserDict': , 'signal': , 'site': , '__builtin__': , 'sys': , 'posix': , 'types': , '__main__': , 'exceptions': , 'os': , 'os.path': }

    Модули в Питоне.

    Работая в интерактивном режиме интерпретатора, вы, наверное, решили, что писать большие программы в Питоне невозможно, так как после выхода из интерпретатора определения ваших функций “исчезают” бесследно. А хотелось бы иметь возможность написать несколько функций в одном файле и потом иметь возможность обращаться к ним из других программ(принцип повторного использования кода). Такая возможность в Питоне существует. Это технология модулей, то есть текстовых файлов, содержащих в себе какой-либо набор функций(желательно объединённых по какому-либо признаку). Большинство функций стандартной библиотеки Питона реализовано именно в модулях. Создать модуль очень просто: просто зайдите в свой любимый текстовый редактор, напишите определения своих функций с помощью ключевого слова def, не забывайте также об отступах, которые выделяют блоки в программе. Ставить символы >>> и .... в текстовом редакторе нельзя, так как это вызовет синтаксическую ошибку. Модули Питона имеют расширение .py и могут содержать только текст. Внутри модуля существует глобальная переменная __name__, в которой содержится имя текущего модуля. Напрмер, создайте файл с именем fibo.py, содержащим следующий код:
    #Модуль Фибоначчи
    def fib(n): # выводит числа Фибоначчи на экран a, b = 0, 1 while b < n: print b, a, b = b, a+b
    def fib2(n): # возвращает числа Фибоначчи, не превосходящие n result = [] a, b = 0, 1 while b < n: result.append(b) a, b = b, a+b return result
    Сохраните файл и зайдите в интерпретатор Питона. К функциям из модуля можно получить доступ посредством ключевого слова import имя_модуля:
    >>> import fibo
    Теперь вы можете вызывать любые функции импортированного модуля в форме “имя_модуля.имя_функции”, то есть обращаться к функциям и переменным модуля не непосредственно, а через имя содержащего их модуля:
    >>> fibo.fib(1000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
    >>> fibo.fib2(100) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    >>> fibo.__name__ 'fibo'
    Существует возможность присваивать локальной переменной любые переменные и функции модуля и обращаться к ним через эту переменную(в памяти локальная переменная указывает на ту же область памяти, что и элемент модуля):
    >>> fib = fibo.fib >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377

    Наследование.

    Одиночное наследование.
    Если бы для каждой конкретной цели нам приходилось бы писать новый код, забывая про всё, что сделано ранее или в лучшем случае копируя старый текст в новый, то программисты долго бы топтались на месте или их разработки были бы полны ошибок. При объектно-ориентированном программировании имеет место другой подход, а именно механизм наследования. То есть класс может включить в себя все элементы родительского класса и использовать их, как свои собственные. При этом может происходить переход от абстрактных к конкретным данным. При наследовании, если в данном классе переопределяются некоторые методы родительского класса, то вызываться будут те методы, которые переопределены в данном классе, а не родительские.
    Синтаксис класса, наследующего одному классу:
    class имя_наследуемого_класса(имя_класса_родителя):
    элемент_класса_1 . . . элемент_класса_n
    При этом класс-родитель может находиться в другой области действия, например, в другом модуле, тогда имя класса-родителя отделяется от имени модуля точкой:
    class имя_наследуемого_класса(имя_модуля.имя_класса_родителя): элемент_класса_1 . . . элемент_класса_n
    Для обращения к элементам и методам родительского класса используется синтаксис: имя_родителя.имя_поля или имя_родителя.имя_метода(аргументы).
    Множественное наследование.
    Часто бывает нужным получить доступ к элементам многих классов сразу, тогда можно воспользоваться механизмом множественного наследования. С точки зрения программы, разницы между одиночным и множественным наследованием нет никакой, то есть одиночное наследование – это частный случай множественного наследования. Для обращения к элементам базовых классов используется синтаксис, подобный одиночному наследованию, только обращаться можно к элементам разных родительских классов. Синтаксис класса, наследующего многим:
    class имя_наследуемого_класса(имя_класса_родителя1, имя_класса_родителя2, ... имя_класса_родителяN): элемент_класса_1 . . . элемент_класса_n
    При этом классы-родители могут находиться в разных областях действия, тогда необходимо указать область действия каждого класса(см. выше).

    Некоторые рекомендации

    Надеюсь, этот обзор функций высшего порядка пробудит у читателя интерес к определенному стилю мышления. Во всяком случае, поэкспериментируйте с ними. Попытайтесь создать собственные функции высшего порядка; некоторые из них могут оказаться весьма полезными и мощными. Поделитесь со мной своими достижениями, и, возможно, в будущей статье мы обсудим оригинальные и замечательные идеи, которыми непрерывно снабжают меня читатели.

    Объяснение новой концепции

    Понятие итераторов было введено в Python версии 2.2. Хотя это не совсем верно; намеки на эту идею присутствовали уже в более ранней функции xrange() и в файловом методе .xreadlines(). Python 2.2 обобщил это понятие в большей части своих внутренних реализаций и значительно упростил программирование итераторов, определенных пользователем, введя ключевое слово yield (присутствие yield превращает функцию в генератор, который в свою очередь возвращает итератор).
    Привлекательность итераторов объясняется двумя причинами. Работа с данными как последовательностями - часто наиболее простой подход, а последовательность, обрабатываемая в линейном порядке, на самом деле зачастую не должна существовать вся сразу.
    Предупреждения x*() предоставляют очевидные примеры этих принципов. Если вы хотите сделать что-нибудь миллиард раз, ваша программа, вероятно, будет исполняться какое время, однако, в общем не нужно требовать для нее много памяти. Подобным образом для многих типов файлов обработка может выполняться построчно, и нет необходимости хранить в памяти весь файл. Все виды других последовательностей также можно наилучшим образом подвергнуть отложенному вычислению; они могли бы полагаться на данные, поступающие в виде приращений по каналу, или на вычисления, выполняемые шаг за шагом.
    Большую часть времени итератор используется в цикле for точно так же, как и истинная последовательность. Итераторы предоставляют метод .next(), который может быть явно запущен, но в 99% времени то, что вы увидите - это что-нибудь вроде:

    for x in iterator: do_something_with(x)

    Этот цикл завершается, когда закулисное обращение к iterator.next() возбуждает исключение StopIteration. Между прочим, истинная последовательность может быть превращена в итератор вызовом iter(seq) - это нисколько не сбережет память, но может быть полезно в функциях, обсуждаемых ниже.

    Области действия переменных.

    В технологии классов важную роль играет область действия переменной. Вы можете обращаться к переменной только внутри блока, где она была определена(в первый раз использована). Если переменная была определена внутри функции, то вне функции к ней нет доступа, если функция определена в основном коде, то она становится глобальной для данной программы(файла-модуля). Но если переменная или функция определена внутри модуля, то обращаться к ней непосредственно по имени невозможно(см.модули). Для обращения к переменной, находящейся внутри модуля, вне модуля чаще всего используется синтаксис имя_модуля.имя_переменной. Для обращения к переменной внутри данной программы, можно воспользоваться модулем главной программы __main__, все переменные и методы, объявленные в различных модулях создаются и разрушаются в определённом порядке. Например, когда вы импортируете модуль, то создаются все объекты, объявленные в нём. Для встроенных функций интерпретатора имеется также особый модуль __builtin__, объекты данного модуля создаются при запуске и не уничтожаются никогда, то есть время жизни таких объектов распространяется на всю программу. Если вы объявляете переменную в функции, цикле, операторе выбора, то она доступна только внутри блока, в котором была объявлена. Если вы хотите объявить глобальную переменную, то воспользуйтесь ключевым словом global. И последнее, о чём я хотел упомянуть в рамках данного раздела, это физические особенности переменных в Питоне. Переменные, естественно, хранятся в памяти компьютера, но имя переменной, это фактически символическая ссылка на ячейку памяти. Поэтому, когда вы присваиваете одной переменной другую, то они фактически указывают на один и тот же объект. При изменении одной переменной, изменяется и другая. Поэтому надо всегда быть осторожным с такого рода операциями.

    Обращение к элементам классов.

    Обращение через ссылку.
    Обращаться через ссылку можно к любым общим элементам класса после того, как объект класса был создан. Вы можете изменять поля класса, можете читать из полей записанные в них данные, однако запись поля вне класса, по-моему, является плохим стилем программирования, так как нарушает принцип сокрытия информации, в то время как чтение полей является абсолютно безопасным. Вы можете также читать методы класса(функции-элементы класса) и присваивать их переменным в программе, чтобы затем вызвать, например:
    x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print x.counter del x.counter При присваивании переменной ссылки на метод класса, как например: xf = x.f while 1: print xf()
    происходит следующее: возникает переменная, которая указывает на некую область памяти, где расположена самая первая исполняемая инструкция функции, но это место в памяти находится внутри класса, то есть при вызове такой функции будет происходить то же, что и при вызове MyClass.f().

    Операции с файлами.

    В Питоне широко используются файлы, для операции с данными, которые предполагается хранить длительное время. Для работы с файлами используется специальный объект файла, который содержит в себе функции для открытия, закрытия файлов, чтения и записи данных.
    Метод open(имя, режим). Открывает файловый объект для последующего оперирования с ним. Функция принимает два параметра: строку, содержащую путь к файлу(может быть абсолютным или относительным текущего каталога или переменной окружения PATH) и режимом открытия файла. Режим открытия файла определяет допустимые операции доступа к файлу:

    w Открыть файл для записи. Если такой файл уже существует, то его содержимое исчезает(если это возможно).
    r Открыть файл только для чтения.
    a Открыть файл для добавления, т.е. записи в конец файла. Предыдущее содержимое файла сохраняется.
    r+ Открыть файл для записи/чтения, содержимое файла сохраняется.
    w+ Открыть файл для записи/чтения, содержимое файла исчезает(см w).
    r+b Открыть двоичный (если такие файлы поддерживаются операционной системой) файл для записи/чтения, содержимое файла сохраняется.
    w+b Открыть двоичный файл для записи/чтения, содержимое файла исчезает(см w).
    rb Открыть двоичный файл только для чтения.
    wb Открыть двоичный файл для записи. Если такой файл уже существует, то его содержимое исчезает(если это возможно).

    Двоичные файлы обеспечивают более быстрый доступ к данным, но бывают не переносимы и, к тому же, их сложно редактировать и просматривать. Текстовые файлы применимы в большинстве случаев, так как они переносимы, легко просматриваются и редактируются, но они медленнее двоичных и могут отображать корректно только символы набора ASCII.
    >>> f=open('/tmp/workfile', 'w') >>> print f
    Метод read([число_байт]). Данный метод читает из файла, открытого для чтения число байтов, указанных в качестве аргумента. Если метод вызывается без параметров, то читается весь файл, если файл был прочитан до конца(встретился символ EOF), то метод read() возвращает пустую строку:

    >>> f.read() 'This is the entire file.\n' >>> f.read() ''

    Метод readline() читает одну строку файла до символа перевода строки(включая сам символ \n). Если строка состоит только из символа перевода строки, то метод readline() возвращает пустую строку. Если в конце файла нет пустой строки с символом \n, то возвращаемый результат неопределён(этого допускать нельзя):

    >>> f.readline() 'This is the first line of the file.\n'

    >>> f.readline() 'Second line of the file\n'

    >>> f.readline() ''

    Метод readlines([размер_строки]) читает все строки файла в список. Читаются только законченные строки. Необязательный параметр размер_строки дополняет читает строку, и если она меньше указанной длины читает дальше, до достижения указанного числа символов, такой приём эффективен для чтения очень больших файлов построчно без необходимости размещения его в памяти:

    >>> f.readlines()

    ['This is the first line of the file.\n', 'Second line of the file\n']

    Метод write(строка) пишет в файл указанную строку, ничего при этом не возвращая:

    >>> f.write('This is a test\n')

    Метод seek(на_сколько_байт[, откуда]) перемещает указатель текущего положения файла на заданное количество байт от позиции, указанной вторым аргументом:

    0(по умолчанию) 1 2
    начало файла текущая позиция в файле конец файла
    При этом, число байт для перемещения может быть как положительным, так и отрицательным(назад).

    Метод tell() возвращает текущую позицию в файле:

    >>> f=open('/tmp/workfile', 'r+')

    >>> f.write('0123456789abcdef')

    >>> f.seek(5) # Переход к шестому байту от начала

    >>> f.read(1)

    '5'

    >>> f.seek(-3, 2) # Переход к третьему байту до конца файла

    >>> f.read(1)

    'd'

    Метод close() закрывает объект файла, перенося все сделанные в нём изменения на диск, возвращая ресурсы операционной системе. Обязательно закрывайте все файлы, сто больше не используете, т.к. не закрытый файл может привести к потере данных. После закрытия обращение к объекту файла автоматически вызывает ошибку

    Оператор del.

    Данный оператор полезен для удаления объектов из памяти, когда они не нужны(после удаления объекта или переменной, вы не сможете больше к ним обращаться). Кроме того, оператор del может использоваться для удаления элемента из списка по его индексу или по промежутку:
    >>> a [-1, 1, 66.6, 333, 333, 1234.5]
    >>> del a[0] >>> a [1, 66.6, 333, 333, 1234.5]
    >>> del a[2:4] >>> a [1, 66.6, 1234.5]

    Опрос объектов Python

    Мы упоминали слово "объект" уже несколько раз, но так и не дали ему определения. Объект в среде программирования во многом подобен объекту реального мира. Реальный объект имеет определенную форму, размер, вес и другие характеристики. Реальный объект может реагировать на свое окружение, взаимодействовать с другими объектами или выполнять задачу. Компьютерные объекты пытаются моделировать объекты, которые окружают нас в реальном мире, включая абстрактные объекты, как, например, документы, каталоги и бизнес-процессы.
    Как и объекты реального мира, некоторые компьютерные объекты могут обладать общими характеристиками, допуская при этом небольшие вариации. Представьте книги, которые продаются в книжном магазине. У любого экземпляра книги может не хватать нескольких страниц, или они могут быть испачканы, и каждая копия книги может включать уникальный идентификационный номер. И, хотя каждая книга -уникальный объект, каждая книга с одним и тем же названием -лишь экземпляр оригинального шаблона; она сохраняет большинство характеристик оригинала.
    Сказанное справедливо и в отношении объектно-ориентированных классов и экземпляров классов. Например, каждая строка Python обладает атрибутами, которые, как мы видели, были показаны функцией dir(). В приведенном выше примере мы определили наш собственный класс Person, который действует как шаблон для создания отдельных экземпляров Python, у каждого из которых свои собственные значения имени и возраста, но общая для всех возможность представиться. Это и есть объектно-ориентированное программирование.
    На языке программирования объекты, следовательно - это нечто, что имеет тождественность и значение, а также определенный тип, обладает некими характеристиками и определенным поведением. Объекты наследуют многие из своих атрибутов от одного или более родительских классов. Кроме ключевых слов и специальных символов (подобных таким операторам, как +, -, *, **, /, %, <, > и т. д.) все на Python является объектом. Python выпускается с широким набором типов объектов: строки, целые числа, числа с плавающей точкой, списки, кортежи, словари, функции, классы, экземпляры классов, модули, файлы и т.п.
    Если у вас произвольный объект, возможно, тот, который был передан в качестве аргумента в функцию, вы, наверное, захотите что-нибудь узнать об этом объекте. В этом разделе мы рассмотрим, как объекты Python отвечают на следующие вопросы:
  • Какое у тебя имя?

  • Что ты за объект?

  • Что ты знаешь?

  • Что ты можешь?

  • Кто твои предки?


  • Ошибки и исключения.

    Программируя на Питоне, вы наверняка встречались с различными ошибками. Самые простые из них – синтаксические. Такие ошибки обнаруживаются во время трансляции кода на Питоне и появляются чаще всего из-за невнимательности программиста. Второй тип ошибок – ошибки пользователя или операционной системы, которые называются иначе исключительными ситуациями, например, деление на нуль. Необходимо постараться предусмотреть возможность обработки таких ошибок (о том, как это сделать будет рассказано далее). Самые сложные ошибки – логические, которые возникают из-за неправильной(то есть непредусмотренной создателем) работы программы. Способов избежать таких ошибок нет – надо быть внимательным при написании своего кода, правильно его отлаживать и писать максимально комментированным, чтобы упростить обнаружение ошибок, как самим автором, так и теми, кто собирается этот код модифицировать.

    Основные элементы программирования.

    Если вы читали всё, что я написал до этого пункта, то можете подумать, что Питон – это очень убогий язык. Но в действительности это не так. Вы можете писать на Питон программы любой сложности. Например, нам надо найти первые числа Фиббоначи(числа Фиббоначи характеризуются тем, что каждое следующее число Фибоначчи равно сумме двух предыдущих):
    >>> # Числа Фибоначчи: ... # Сумма двух предыдущих определяет следующее ... a, b = 0, 1#Два первых числа >>> while b < 10:#Цикл ниже выполняется пока число Фибоначчи меньше 10 ... print b ... a, b = b, a+b ...
    Результат выполнения программы:
    1
    1
    2
    3
    5
    8
    Эта программа демонстрирует многие возможности Питона.
    Первая строка демонстрирует присваивание двум переменным двух различных значений

  • Вторая строка демонстрирует заголовок цикла while(пока). Этот цикл (после двоеточия) обозначается отступом и выполняется пока условие в заголовке цикла(b < 10) является верным(или не равно нулю, например цикл while 0 выполняться не будет, а цикл while 1 будет выполняться бесконечно и зациклит программу, если внутри цикла не предусмотрено какое-либо условие выхода(плохой стиль программирования)). Попробуйте изменять число в цикле, и вы увидите, как будет изменяться верхнее значение переменной b. Кроме этого, в цикле while могут использоваться строки, списки, при этом всё, имеющее нулевую длину, считается нулём, а всё, имеющее ненулевую длину, считается истиной. В операторе while вы можете использовать следующие операции: < (меньше), > (больше), ==(равно), <= (меньше или равно), >= (больше или равно) и != (не равно).

  • Третья строка выводит на экран значение переменной b. В этом операторе вы можете использовать любые переменные, списки, строки(строки выводятся без кавычек) разделённые друг от друга пробелом. Поэтому вы можете отформатировать вывод так, как вам это надо.

  • Четвёртая строка также демонстрирует присваивание двум переменным двух значений. Самое важное – отметить, что вначале оцениваются выражения справа(т.е b, a + b), а затем эти выражения подставляются в левую часть.
    Кроме этого, отметим, что логические части программы(такие как, например, тело цикла) отмечаются отступами от основного уровня текста программы, а не специальными символами({} в Си и begin end в Паскале). Заметьте также, что после заголовка следует символ двоеточия.
    Приведём ещё некоторые примеры несложных программ:
    >>> 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
    Заметьте, что перевод строки осуществляется интерпретатором автоматически, когда следующая переменная не умещается в текущей строке.
    |


    Основные операторы.

    Оператор if.
    Если вы знакомы с английским языком, то легко поймёте, что слово if переводится как “если”. Этот оператор выполняет блок кода, следующий после него с отступами, только в случае, если выражение в его заголовке не нуль(т.е. верно).
    >>> x = int(raw_input("Введите целое число: ")) >>> if x < 0: ... x = 0 ... print 'Отрицательное число стало нулём' ... elif x == 0: ... print 'Это число - нуль' ... elif x == 1: ... print 'Это число - 1' ... else: ... print `Это число больше единицы' ...
    Далее могут быть ещё операторы elif(аналог case в Си). Оператору if, как видно, сопутствуют операторы else(“иначе” - блок кода выполняется если условие в заголовке if приняло нулевое значение, т.е стало ложным) и elif(“иначе если” - блок кода выполняется если условие в заголовке if приняло нулевое значение, т.е стало ложным, а значение в заголовке данного оператора стало ненулевым, т.е истинным).
    Оператор for.
    Оператор for несколько необычен в Питоне, он имеет вид for некая_переменная in некий_диапазон. Блок кода после заголовка выполняется, пока некая_переменная принадлежит некому_диапазону(причём этот диапазон может быть списком, числовой последовательностью, массивом каких-либо значений):
    >>> # Определим какие-то строки: ... a = ['Linux', 'Open', 'Office'] >>> for x in a: ... print x, len(x)
    Результат работы:
    Linux 5
    Open 4
    Office 6
    Довольно опасно менять в теле цикла for значение диапазона(это может повлечь весьма странную его работу, зацикливание и усложняет понимание программы), кроме случаев, когда в роли диапазона выступает список. В этом случае, можно сделать простое копирование списков, удвоение элементов в списке:
    >>> for x in a[:]: # здесь мы делаем копию всего списка a в переменной x ... if len(x) > 6: a.insert(0, x)#Если длина строки списка больше 6 ...
    >>> a
    Результат работы: [Linux , Open, Office, Office]
    Для задания диапазона в форме арифметической прогрессии (1 2 3 4 5 6...) удобно пользоваться функцией range().
    Она имеет три формы, рассмотрим на примерах все:

    >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    Эта форма возвращает все целые числа в диапазоне от 0 до числа 10, не включая само число 10.

    >>> range(5, 10) [5, 6, 7, 8, 9]

    Эта форма возвращает все целые числа в диапазоне от 5 до числа 10, не включая само число 10, но включая начальное число 5.

    >>> range(0, 10, 3) [0, 3, 6, 9]

    >>> range(-10, -100, -30) [-10, -40, -70]

    Эта форма возвращает все целые значения в диапазоне от начального до конечного с шагом, заданным третьим параметром. Причём, если, например, вы попробуете ввести range(1, 100, -1), то очевидно, что чисел в этом диапазоне нет, то есть результатом будет пустое множество([]).

    Если вы хотите задать диапазон через количество элементов в списке, то следует воспользоваться функцией range в сочетании с функцией len:

    >>> a = [`Linux', 'is', 'the', 'best', 'system'] >>> for i in range(len(a)): ... print i, a[i]#Обращение к элементу списка по его индексу ...

    0 Linux

    1 is

    2 the

    3 best

    4 system

    Прерывание и продолжение циклов for и while.

    Для немедленного выхода из цикла можно использовать оператор break(хотя некоторые программисты считают применение break плохим стилем), а для продолжения цикла, но со следующим значением переменной(т.е следующей итерации цикла) можно использовать оператор continue. Всё сказанное выше можно показать на примере поиска простых чисел:

    >>> for n in range(2, 10):#Задаёт верхнюю границу

    ... for x in range(2, n): ... if n % x == 0:#Если n делится на x без остатка и n не простое число ... print n, 'равно', x, '*', n/x ... break#Выход из цикла по x, следующее n ... else: ... # если n не разделилось без остатка ни на одно x от 2 до n, то ... print n, 'простое число' ...

    Результат работы: 2 простое число

    3 простое число

    4 равно 2 * 2

    5 простое число

    6 равно 2 * 3

    7 простое число

    8 равно 2 * 4

    9 равно 3 * 3

    Оператор pass.

    pass означает, что не надо выполнять никаких действий.Этот оператор может использоваться, когда наличие какого-либо кода необходимо синтаксически, но не нужно по логике работы программы:

    >>> while 1: ... pass # Бесконечный цикл, ничего не делающий: ждём прерывания с клавиатуры ...

    Особенности операторов сравнения.

    Теперь, когда мы познакомились со списками, пришёл черёд изучить дополнительные сведения об операторах сравнения. Операторы сравнения (>; <; ==; !=; >=; <=)имеют в выражениях приоритет оценки ниже арифметических операторов, то есть в выражении a + b > c + d * e будут оценены вначале арифметические операции, а затем их результаты будут сравниваться. Операторы сравнения могут комбинироваться друг с другом, например, в выражении “a < b == c” операторы сравнения оцениваются слева направо, так как имеют одинаковый приоритет. Выражения с операторами сравнения могут объединяться с помощью логических операций or(или), and(и) и not(не). Значение логических операторов ясно из названия, поэтому комментарии здесь излишни. Приоритет логических операторов ниже, чем к операторов сравнения, самый высокий приоритет у оператора not, самый низкий – у оператора or. Поэтому, при оценке логических выражений, включающих в себя операторы различного приоритета, встретившееся выражение false может вызвать то, что операторы с более низким приоритетом обработаны не будут.
    Для списков существуют особые операторы сравнивания:
    оператор in(not in) оценивает, входит ли заданный элемент в последовательность;
    оператор is(is not) применяется для изменяемых последовательностей и определяет, действительно ли две последовательности указывают на одну и ту же область памяти(то есть являются идентичными).
    Результат сравнения может быть присвоен переменной логического типа(принимает значения true и false). В отличие от языка Си, в Питоне не допускаивается присваивание при сравнении, что исключает ошибку, распространённую в языке Си и связанную с употреблением =, вместо ==.

    Отсутствующие эквиваленты

    В itertools нет ireduce(), хотя это могло бы показаться естественным; возможная Питоновская реализация такова:
    Листинг 1. Пример реализации ireduce()

    def ireduce(func, iterable, init=None): if init is None: iterable = iter(iterable) curr = iterable.next() else: curr = init for x in iterable: curr = func(curr, x) yield curr

    Случай использования ireduce() подобен варианту с reduce(). Например, предположим, что вы хотите суммировать список чисел, находящихся в большом файле, но остановиться, когда выполняется условие. Вы могли бы контролировать текущий итог с помощью:
    Листинг 2. Добавление списка чисел и подведение итога

    from operator import add from itertools import * nums = open('numbers') for tot in takewhile(condition, ireduce(add, imap(int, nums)): print "total =", tot

    Более реальный пример, вероятно, что-нибудь вроде применения потока событий к объекту, поддерживающему внутреннее состояние, как, например, к элементу управления графического интерфейса пользователя. Но даже простой вышеприведенный пример демонстрирует итераторные комбинаторы в стиле функционального программирования.

    Пакеты.

    Несколько модулей в Питоне могут быть объединены по функциональному или какому-нибудь другому признаку. Для этого используются пакеты. Суть пакетов такова: вы объединяете несколько модулей по некому признаку в пакет, затем могут появиться другие пакеты с модулями, организовать структуру пакетов можно по принципу дерева. Принципу дерева подчиняются также папки в файловых системах. То есть пакеты подобны папкам на диске:
    Sound/ Корневой пакет __init__.py Инициализация звуковой библиотеки Formats/ Подпакет звуковых форматов __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... Effects/ Подпакет звуковых эффектов __init__.py echo.py surround.py reverse.py ... Filters/ Подпакет фильтров __init__.py equalizer.py vocoder.py karaoke.py ...
    Далее могут следовать другие пакеты, которые в свою очередь могут содержать другие пакеты... Такая иерархическая древовидная организация способна упростить поиск необходимого модуля и просто необходима для крупных проектов.
    Файл __init__.py нужен интерпретатору для того, чтобы расценивать каталог на диске, как пакет. Данный файл может быть пустым, но может содержать исполняемый код, для инициализации переменной __all__.
    Из пакетов можно импортировать отдельные модули несколькими путями:
    1. import Sound.Effects.echo
    Загрузка модуля echo. Далее должно указываться полное имя модуля.:
    Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4)
    2. from Sound.Effects import echo
    Также загрузка модуля echo, но теперь к нему можно обращаться без указания имени модуля:
    echo.echofilter(input, output, delay=0.7, atten=4)
    3. from Sound.Effects.echo import echofilter
    Прямой импорт объекта из пакета. Теперь вы можете обращаться к функции echofilter() непосредственно через её имя:
    echofilter(input, output, delay=0.7, atten=4)
    Из пакетов можно также импортировать все модули. Но трюк с import * не пройдёт на некоторых операционных системах(DOS, Windows, MAC), так как в них не различается регистр у файлов и папок, а так как в Питоне регистр различается, то это может привести к непредсказуемым именам модулей(Echo, echo, ECHO, eChO). Поэтому для надёжности лучше создавать список модулей пакета в файле __init__.py. Для этого присвойте переменной __all__ список всех имён модулей(строковый тип элемента списка). Такой подход обеспечит корректную интерпретацию пакета на любой системе.
    __all__ = ["echo", "surround", "reverse"]
    А в самом коде можно теперь применять import *:
    from Sound.Effects import *
    А вообще, лучше импортировать из пакета только нужные модули: во-первых это удобно для понимания кода, а во-вторых куча модулей будет использовать кучу памяти – выбирать вам: или чуть больше написать руками и иметь меньше проблем, или наоборот...


    Передача в функцию переменного числа аргументов.

    Часто используемым приёмом в программировании является передача в функцию переменного числа аргументов. Для этого в Питоне можно воспользоваться символом * перед списком аргументов переменной длины. Аргументы в теле функции могут быть разделены(см. выше). Перед списком аргументов может следовать(необязательно) один или несколько обязательных аргументов:
    def fprintf(file, format, *args): file.write(format % args)

    Переходим на функциональное программирование?

    В предыдущей статье "Функциональное программирование на Python" были освещены основные понятия функционального программирования (ФП). В этой статье мы попытаемся немного углубиться в эту богатейшую концептуальную область. Библиотека Xoltar Toolkit Брина Келлера (Bryn Keller) окажет нам в этом неоценимую помощь. Основные возможности ФП Келлер представил в виде небольшого эффективного модуля на чистом Python. Помимо модуля functional, в Xoltar Toolkit входит модуль lazy, поддерживающий структуры, вычисляемые "только когда это необходимо". Множество функциональных языков программирования поддерживают отложенное вычисление, поэтому эти компоненты Xoltar Toolkit
    предоставят вам многое из того, что вы можете найти в функциональном языке наподобие Haskell.

    Первое знакомство с классами.

    В Питоне есть все средства поддержки объектно-ориентированного подхода – это технология классов. Классы могут содержать в себе самые различные элементы: переменные, константы, функции и другие классы. Типичное описание класса в Питоне выглядит так:
    class имя_класса: элемент_класса_1 . . . элемент_класса_n
    Объявление класса напоминает использование ключевого слова def для функции, пока класс не объявлен, использовать его запрещено. Класс может быть описан внутри функции или структуры if, но всё же желательнее описывать класс вне программных структур, то есть в теле программы, а ещё лучше описать все классы в самом начале программы, так как это облегчает чтение программы. Класс, будучи объявлен, создаёт в программе новую область действия, поэтому всё, описанное внутри класса, включается в область действия класса и является недоступным извне. Обычно класс состоит в основном из функций элементов, они определяются внутри класса словом def. Функции-элементы класса имеют некоторые особенности списка аргументов(об этом будет сказано далее).
    К объектам классов в Питоне можно обращаться двумя способами: ссылкой на элемент(имя_класса.имя_элемента) и через присвоение переменной класса через функцию(переменная = имя_класса()), например:
    class MyClass:
    "Простой класс"
    i = 12345
    def f(self):
    return 'Привет мир'
    x = MyClass()
    Такое объявление присваивает переменной х объект класса MyClass и теперь ко всем элементам данного класса можно обращаться через данную переменную. Причём область действия данного объекта совпадает с областью действия переменной х. Следует отметить особый параметр self, который передаётся функции класса. Этот параметр является типичным для функций-элементов класса, так как содержит ссылку на класс, которому принадлежит данная функция и позволяет обращаться к другим членам класса.
    При создании нового объекта, создаётся пустой объект, но часто такое поведение оказывается неправильным и неудобным. Тогда к вам на помощь может прийти функция-элемент __init__() класса. Подобно функции __init__() модуля, она вызывается при создании объекта класса и выполняет инициализацию полей(переменных) класса. Пример применения функции __init__():
    def __init__(self): self.data = []
    Функция __init__() может принимать сколько угодно параметров, но при создании экземпляра класса через функцию, необходимо указать все параметры, кроме, конечно, self:
    >>> class Complex: ... def __init__(self, realpart, imagpart): ... self.r = realpart ... self.i = imagpart ... >>> x = Complex(3.0, -4.5) >>> x.r, x.i
    (3.0,-4.5)

    Питоновское прогрессирующее раздвоение личности

    В отношении Python к функциональному программированию есть что-то шизофреническое. С одной стороны, многие разработчики Python недооценивают традиционные функции функционального программирования: map(), filter() и reduce() - обычно рекомендуя использовать вместо них списочные включения (list comprehensions). Но весь модуль itertools составлен из функций точно такого же вида и просто оперирует над "отложенными последовательностями" (итераторами), а не над законченными последовательностями (списками, кортежами). Более того, в Python 2.3 отсутствует синтаксис для "итераторных включений" (iterator comprehensions), которые казалось бы имеют те же мотивы, что и списочные включения.
    Я подозреваю, что Python в конечном счёте разовьет некую форму итераторных включений, но это зависит от нахождения подходящего естественного синтаксиса для них. Между тем, у нас имеется ряд удобных комбинаторных функций в модуле itertools. Вообще каждая из этих функций принимает некоторые параметры (обычно включая некоторые базовые итераторы) и возвращает новый итератор. Например, функции ifilter(), imap() и izip() полностью эквивалентны соответствующим встроенным функциям, у которых отсутствует начальное i.

    Поднятие объектно-ориентированного программирования на новый уровень

    Большинство читателей уже знакомо с концепциями объектно-ориентированного программирования: наследованием, инкапсуляцией, полиморфизмом. Но создание объектов заданного класса с определенными родителями обычно представляется исходно заданной операцией. Оказывается, что ряд новых программных конструкций становится либо более простым, либо вообще хоть сколько-нибудь возможным, если вы можете настраивать процесс создания объектов. Метаклассы разрешают определенные типы "аспектно-ориентированного программирования"; например, можно усилить классы такими характеристиками, как возможности трассировки, объектная персистентность, регистрация исключений и так далее.

    вычисление логического выражения заканчивается сразу,

    1. T.е. вычисление логического выражения заканчивается сразу, как только становится известен его логический результат. Стоит также заметить, что в Python, так же как и в Lisp, значением логического выражения является не true/false, а значение последнего вычисленного подвыражения - например, 4 and "Hello!" or 2*2 будет иметь значение "Hello!". прим. перев.

    2. monadic_print() может быть реализована в полностью функциональном стиле, без использования утверждения print: monadic_print = lambda x: sys.write(str(x) + '\n') and x

    прим. перев.
    3. Следует обратить внимание, что пример работает только в том случае, если переменная echo_FP глобальна. Это связано с тем, что в Python всех версий до 2.0 включительно отсутствует статическая вложенность области действия имен. Любое имя, встретившееся в контексте функции или метода, ищется сначала среди локальных имен функции, а потом сразу среди глобальных имен (затем среди встроенных имен). Это отличается от логики языков со статическими областями действия имен (C, C++, Pascal, etc.), где имя последовательно ищется во всех объемлющих блоках. Из этого, в частности, следует, что рекурсивный вызов lambda-функции, привязанной к неглобальному имени, в Python версии меньше 2.1 невозможен. В Python 2.1 введены опциональные статические области действия. Таким образом, начиная с версии 2.1 Python можно рассматривать и как полноценный FP-язык (помимо всего прочего). Вышеприведенный комментарий относится почти ко всем функциональным примерам в статье. прим.
    Оригинальный текст статьи можно посмотреть здесь:

    Присвоение значений

    Внимательный читатель помнит об упоминаемом мной ограничении функциональной техники, описанной в предыдущей статье. Речь шла о том, что ничто в Python не запрещает переприсваивания другого значения имени, ссылающемуся на функциональное выражение. В ФП под именами понимается всего лишь буквенное сокращение более длинных выражений, при этом подразумевается, что одно и то же выражение всегда приводит к одному и тому же результату . Если же уже определенному имени присваивается новое значение, это допущение нарушается. Предположим, мы определяем неколько сокращений для использования в нашей функциональной программе, как в следующем примере:

    #---- FP-сессия Python с переприсваиванием приводит к неприятностям ---#

    >>> car = lambda lst: lst[0]

    >>> cdr = lambda lst: lst[1:]

    >>> sum2 = lambda lst: car(lst)+car(cdr(lst))

    >>> sum2(range(10)) 1

    >>> car = lambda lst: lst[2]

    >>> sum2(range(10)) 5

    К несчастью, одно и то же выражение sum2(range(10))
    вычисляется к разным результатам в двух местах программы, несмотря на то, что аргументы выражении не являются изменяемыми переменными.
    К счастью, модуль functional
    предоставляет класс Bindings (предложенный Келлеру автором), предотвращающий такое переприсваивание (по крайней мере, случайное; Python не препятствует решительному программисту намеренно нарушать правила). Хотя использование Bindings
    требует немного дополнительного кода, это оградит вас от случайностей. Келлер обозначает экземпляр класса Bindings как let
    (я полагаю, из-за зарезервированного слова let
    в ML-языках программирования).
    Например, мы могли бы сделать следующее:

    #------- FP-сессия Python с защитой от переприсваивания -------#

    >>> from functional import *

    >>> let = Bindings()

    >>> let.car = lambda lst: lst[0]

    >>> let.car = lambda lst: lst[2]

    Traceback (innermost last):

    File "", line 1, in ?

    File "d:\tools\functional.py", line 976, in __setattr__

    raise BindingError, "Binding '%s' cannot be modified." % name

    functional.BindingError: Binding 'car' cannot be modified.

    >>> let.car(range(10)) 0


    Разумеется, реальная программа должна перехватить и обработать исключение BindingError, однако сам факт его возбуждения позволяет избежать целого класса проблем.

    Помимо класса Bindings, functional

    содержит функцию namespace, предоставлюющую доступ к пространству имен (на самом деле, к словарю) из экземпляра класса Bindings. Это очень удобно, если вам нужно вычислить выражение в (неизменяемом) пространстве имен, определенном в Bindings. Функция eval() в Python позволяет проводить вычисление в пространстве имен. Следующий пример поясняет сказанное:



    #----- FP-сессия Python, использующая неизменяемые пространства имен -----#

    >>> let = Bindings() # "Real world" function names

    >>> let.r10 = range(10)

    >>> let.car = lambda lst: lst[0]

    >>> let.cdr = lambda lst: lst[1:]

    >>> eval('car(r10)+car(cdr(r10))', namespace(let))

    >>> inv = Bindings() # "Inverted list" function names

    >>> inv.r10 = let.r10

    >>> inv.car = lambda lst: lst[-1]

    >>> inv.cdr = lambda lst: lst[:-1]

    >>> eval('car(r10)+car(cdr(r10))', namespace(inv))

    17


    Проблемы, решаемые магией

    До сих пор мы рассматривали основы метаклассов. Однако реальное применение метаклассов имеет свои тонкости. Сложность с использованием метаклассов заключается в том, что в типичной модели ООП классы в действительности делают не много. Структура наследования классов удобна для инкапсуляции и объединения данных и методов, но обычно реально используются именно экземпляры.
    Существует две общие категории задач программирования, для которых, на наш взгляд, метаклассы являются чрезвычайно полезными.
    Первый, и вероятно более общий случай, это когда в период проектирования вы не знаете точно, что нужно делать классу. Очевидно, что у вас появится представление об этом, но некая определенная деталь может зависеть от информации, которая станет доступной позднее. Само "позднее" может быть двух видов: (а) когда библиотечный модуль будет использоваться приложением; (б) во время исполнения, когда будет существовать некая ситуация. Этот категория близка к тому, что часто называют "Аспектно-ориентированным программированием" (АОП). Продемонстрируем, что мы имеем в виду на следующем элегантном примере:


    Листинг 7. Конфигурирование метакласса во время исполнения
    % cat dump.py #!/usr/bin/python import sys if len(sys.argv) > 2: module, metaklass = sys.argv[1:3] m = __import__(module, globals(), locals(), [metaklass]) __metaclass__ = getattr(m, metaklass)
    class Data: def __init__(self): self.num = 38 self.lst = ['a','b','c'] self.str = 'spam' dumps = lambda self: `self` __str__ = lambda self: self.dumps()
    data = Data() print data
    % dump.py <__main__.Data instance at 1686a0>
    Как вы могли ожидать, это приложение выводит весьма общее описание объекта data (условный объект экземпляра). Однако, если аргументы времени исполнения передаются в приложение, можно получить несколько отличный результат:

    Листинг 8. Добавление метакласса внешней сериализации
    % dump.py gnosis.magic MetaXMLPickler

    В этом частном примере применяется стиль сериализации gnosis.xml.pickle, но текущая версия пакета gnosis.magic также содержит метаклассы сериализаторов MetaYamlDump, MetaPyPickler и MetaPrettyPrint. Кроме того, пользователь "приложения" dump.py может потребовать использование любого желаемого "MetaPickler" из любого пакета Python, который его определяет. Соответствующий метакласс, предназначенный для этой цели, будет выглядеть приблизительно так:

    Листинг 9. Добавление атрибута с метаклассом

    class MetaPickler(type): "Metaclass for gnosis.xml.pickle serialization" def __init__(cls, name, bases, dict): from gnosis.xml.pickle import dumps super(MetaPickler, cls).__init__(name, bases, dict) setattr(cls, 'dumps', dumps)

    Замечательное достижение этого подхода заключается в том, что прикладному программисту не нужно ничего знать о том, какая сериализация будет использоваться -будет ли даже добавляться в командной строке сериализация или другой способ внешнего представления.

    Возможно, наиболее общее использование метаклассов схоже с применением MetaPicklers: добавление, удаление, переименование или подстановка методов вместо методов, определенных в созданном классе. В нашем примере "встроенный" метод Data.dump() заменяется другим методом, внешним по отношению к приложению, во время создания класса Data (и, следовательно, в каждом последующем экземпляре).

    Программное возбуждение исключений.

    Если вы пишете библиотеки функций, то одним из способов сообщить вызывающей программе о том, что случилась непредвиденная ситуация, является возбуждения исключений. Для этой цели используется оператор raise, который возбуждает заданное исключение. Оператор raise имеет следующий синтаксис: raise имя_исключения[, аргумент_исключения, аргумент_исключения ...]. Если raise вызывается без аргументов, то она возбуждает повторно самое последнее исключение:
    >>> raise NameError, 'HiThere' Traceback (most recent call last):
    File "", line 1, in ?
    NameError: HiThere
    raise без параметров часто используется в промежуточных обработчиках исключений:
    >>> try: ... raise NameError, 'HiThere' ... except NameError: ... print 'Произошла ошибка!' ... raise ... Произошла ошибка!
    Traceback (most recent call last):
    File "", line 2, in ?
    NameError: HiThere
    В Питоне можно создавать собственные типы исключений в виде классов. Но эта тема будет рассмотрена в разделе классы и объектно-ориентированное программирование, потому что все исключения – это классы.

    Производные классы

    Как было указано выше, экземпляры класса, определенного пользователем, наследуют свои атрибуты от этого класса. На уровне класса, класс может быть определен в терминах другого класса и будет схожим образом наследовать атрибуты в иерархической форме. Python даже поддерживает множественное наследование, что означает, что отдельный класс может быть определен в терминах более чем одного - и наследоваться более чем от одного - родительского класса. Функция issubclass() позволяет установить, наследуется ли один класс от другого:


    Листинг 34. Не ты ли мой предок?
    >>> print issubclass.__doc__ issubclass(C, B) -> Boolean
    Return whether class C is a subclass (i.e., a derived class) of class B.
    >>> class SuperHero(Person): # SuperHero наследуется из Person... ... def intro(self): # но с новым SuperHero intro ... """Return an introduction.""" ... return "Hello, I'm SuperHero %s and I'm %s." % (self.name, self.age) ... >>> issubclass(SuperHero, Person) 1 >>> issubclass(Person, SuperHero) 0 >>>

    Синтаксические ошибки.

    Ошибки такого типа обычно находятся интерпретатором:
    >>> while 1 print 'Hello world' File "", line 1, in ?
    while 1 print 'Hello world'
    ^
    SyntaxError: invalid syntax #Неверный синтаксис команды
    Старайтесь избегать подобных ошибок(хотя, конечно, полностью от них избавиться не удастся), так как они легко могут превратиться в логические.

    Словари.

    Во всех рассмотренных последовательностях обращаться к отдельным элементам нужно было по индексу. Иную форму организации последовательности представляют словари. В словарях для доступа к отдельным его элементам используются ключевые индексы, подобные индексам в базах данных. Индексом может быть любой неизменяемый объект, такой как строка, число, константный список(такой список может содержать только строки, числа или другие константные списки). В тексте программы словари задаются фигурными скобками {} с элементами словаря. Каждому элементу словаря должен соответствовать определённый индекс, который отделяется от элемента двоеточием(“индекс:значение”). К элементам словаря можно обращаться по соответствующим им индексам. При обращении к несуществующему индексу возникает ошибка. Чтобы узнать список всех индексов словаря, можно воспользоваться методом keys(), которая возвращает все индексы словаря в случайном порядке(но вы можете отсортировать индексы функцией sort()). Чтобы проверить наличие индекса в словаре, можно использовать метод has_key(). Вот простой пример использования словаря:
    >>> tel = {'Ваня': 4098, 'Коля': 4139} >>> tel['Андрей'] = 4127 >>> tel {'Коля': 4139, 'Андрей': 4127, 'Ваня': 4098}
    >>> tel['Ваня'] 4098
    >>> del tel['Коля'] >>> tel['Дима'] = 4127 >>> tel {'Андрей': 4127, 'Дима': 4127, 'Ваня': 4098}
    >>> tel.keys() ['Андрей', 'Дима', 'Ваня']
    >>> tel.has_key('Ваня') 1

    Списки.

    Списки широко распространены в Питоне и имеют множество методов манипулирования(метод отделяется от имени списка точкой: имя_списка.метод()):
    append(x) вставляет в конец списка элемент х. Эквивалентно a[len(a):] = [x].

  • extend(L) добавляет списку в конец все элементы списка L. Эквивалентно a[len(a):] = L.

  • insert(i, x) вставляет элемент x в позицию перед индексом i в списке(для вставки элемента в начало списка воспользуйтесь insert(0, x)).

  • remove(x) удаляет первое вхождение x в список, вызывает ошибку если элемент x не найден.

  • pop(i) удаляет элемент с индексом i и возвращает его. Если вызвать pop() без параметров, то будет возвращён и удалён последний элемент списка.

  • index(x) возвращает индекс первого вхождения элемента х в список, вызывает ошибку если элемент x не найден.

  • count(x) возвращает количество вхождений элемента x в список.

  • sort() сортирует элементы списка по возрастанию.

  • reverse() переворачивает список в обратном порядке.
    Примеры использования методов списков:
    >>> a = [66.6, 333, 333, 1, 1234.5] >>> print a.count(333), a.count(66.6), a.count('x') 2 1 0
    >>> a.insert(2, -1) >>> a.append(333) >>> a [66.6, 333, -1, 333, 1, 1234.5, 333]
    >>> a.index(333) 1
    >>> a.remove(333) >>> a [66.6, -1, 333, 1, 1234.5, 333]
    >>> a.reverse() >>> a [333, 1234.5, 1, 333, -1, 66.6]
    >>> a.sort() >>> a [-1, 1, 66.6, 333, 333, 1234.5]

    Сравнение списков.

    Сравнение списков несколько отличается от сравнения простых числовых значений. Во-первых, списки должны быть одинакового типа. Во-вторых сравнение идёт в лексикографическом порядке, т.е оцениваются вначале первые элементы последовательностей, если они не равны, то далее возвращается результат(>;<;!=), иначе оценивается следующая пара элементов. Последовательности будут равны только в том случае, если все их элементы будут соответственно равны. Кроме этого, более длинная последовательность будет всегда больше более короткой.Строки сравниваются, учитывая порядок символов в строках в таблице ASCII. Приведём примеры сравнения последовательностей:
    (1, 2, 3) < (1, 2, 4) [1, 2, 3] < [1, 2, 4] 'ABC' < 'C' < 'Pascal' < 'Python' (1, 2, 3, 4) < (1, 2, 4) (1, 2) < (1, 2, -1) (1, 2, 3) == (1.0, 2.0, 3.0)
    2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
    При этом, допускается сравнивание элементов различного типа(хотя по-моему это сильно усложняет понимание программы), при этом строка всегда больше списка, константный список больше строки и.т.д. Различные числовые типы сравниваются путём приведения их к типу с точкой.


    Стандартные модули Питона.

    Питон поставляется со множеством программных модулей, призванных облегчать работу программиста. Эти модули являются стандартными для всех реализаций Питона, поэтому использование стандартных функций помогает сделать ваш код переносимым на другие платформы(отмечу, что байт-код Питона также является переносимым с платформы на платформу). В Питоне есть также значительное количество встроенных функций, обеспечивающих самую необходимую работу. В библиотеке Питона есть также модули, которые специфичны для конкретной операционной системы и обеспечивают высокоэффективный(и часто низкоуровневый) доступ к её ресурсам. Использование таких библиотек полностью исключено, если вы хотите писать переносимый код.
    В модулях содержатся некоторые функции, позволяющие настраивать интерпретатор. Это в основном функции и переменные модуля sys. Например, переменные sys.ps1 и sys.ps2 хранят в себе приглашение интерпретатора:
    >>> import sys >>> sys.ps1 '>>> '
    >>> sys.ps2 '... '
    >>> sys.ps1 = '[]# ' []# print 'Ой!' Ой! []#
    Список sys.path хранит в себе пути поиска библиотек интерпретатором:
    >>> import sys >>> sys.path.append('/home/guido/lib/python')
    Встроенная функция dir([имя_модуля]) позволяет вывести все загруженные модули, может также вывести все объекты какого-либо конкретного модуля(с указанием имени модуля, в качестве аргумента dir()):
    >>> import fibo, sys >>> dir(fibo) ['__name__', 'fib', 'fib2']
    >>> dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__',
    '__stdin__', '__stdout__', '_getframe', 'argv', 'builtin_module_names',
    'byteorder', 'copyright', 'displayhook', 'exc_info', 'exc_type',
    'excepthook', 'exec_prefix', 'executable', 'exit', 'getdefaultencoding',
    'getdlopenflags', 'getrecursionlimit', 'getrefcount', 'hexversion',
    'maxint', 'maxunicode', 'modules', 'path', 'platform', 'prefix', 'ps1',
    'ps2', 'setcheckinterval', 'setdlopenflags', 'setprofile',

    'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version',

    'version_info', 'warnoptions']

    Встроенные функции Питона содержатся в модуле __builtin__( импортировать его для использования функций не нужно):

    >>> import __builtin__ >>> dir(__builtin__) ['ArithmeticError', 'AssertionError', 'AttributeError',

    'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError',

    'Exception', 'FloatingPointError', 'IOError', 'ImportError',

    'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt',

    'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented',

    'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning',

    'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError',

    'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',

    'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError',

    'UnicodeError', 'UserWarning', 'ValueError', 'Warning',

    'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__',

    '__name__', 'abs', 'apply', 'buffer', 'callable', 'chr', 'classmethod',

    'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr',

    'dict', 'dir', 'divmod', 'eval', 'execfile', 'exit', 'file', 'filter',

    'float', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id',

    'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len',

    'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object',

    'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range', 'raw_input',

    'reduce', 'reload', 'repr', 'round', 'setattr', 'slice', 'staticmethod',

    'str', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange',

    'zip']

    Строки документации

    Возможно, вы заметили в наших многочисленных примерах с dir() один атрибут среди множества других - __doc__. Этот атрибут - строка, которая содержит комментарии, описывающие объект. Python называет ее строкой документации, или docstring, и вот, как она работает. Если первая директива определения модуля, класса, метода или функции - строка, то эта строка оказывается связанной с объектом в качестве его атрибута __doc__. Посмотрите, например, на строку документации для объекта __builtins__. Воспользуемся Питоновской директивой print, чтобы выходные данные было легче читать, поскольку строки документации часто содержат встроенные разделители строк (\n):


    Листинг 24. Строка документации модуля
    >>> print __builtins__.__doc__ # Модуль докстроки Built-in functions, exceptions, and other objects.
    Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.
    И вновь, Python поддерживает даже строки документации для классов и методов, которые определены интерактивно в оболочке Python. Давайте рассмотрим строки документации для нашего класса Person и его метода intro:


    Листинг 25. Строки документации класса и метода
    >>> Person.__doc__ # Докстрока класса 'Person class.' >>> Person.intro.__doc__ # Докстрока метода класса 'Return an introduction.'
    Поскольку строки документации предоставляют такую ценную информацию, многие среды разработки Python включают способы автоматического отображения строк документации для объектов. Давайте рассмотрим еще одну строку документации - для функции dir():


    Листинг 26. Строка документации функции
    >>> print dir.__doc__ # Function docstring dir([object]) -> list of strings
    Return an alphabetized list of names comprising (some of) the attributes of the given object, and of attributes reachable from it:
    No argument: the names in the current scope. Module object: the module attributes. Type or class object: its attributes, and recursively the attributes of its bases. Otherwise: its attributes, its class's attributes, and recursively the attributes of its class's base classes.

    Строки юникода.

    Строки Юникода позволяют поддерживать символы всех алфавитов, древних и ныне существующих. Дело в том, что обычные строки могут содержать до 256 различных символов, это очень ограничивало алфавит компьютера. Со введением юникода в строке может содержаться до 65536(!) различных символов. Питон, как язык, поддерживающий всё новое в компьютерном мире, поддерживает юникод. Строки юникода обозначаются символом u перед открывающей кавычкой., например u”РоссияUSA”. В юникоде также имеются управляющие символы, но обозначаются они по-другому в формате \0000, где 0000 некий управляющий символ(например \0020 – пробел). Полный список управляющих символов юникода можно найти на сайте . Если вы не хотите отображение управляющих символов, поставьте перед открывающей кавычкой символы ur. Все операции со строками обычного формата применимы к строкам юникода. То есть внутри Питона юникод и обычный текст во многом схожи, и вы можете использовать и то и другое представление строк(учтите, однако, что текст в 2 раза компактнее юникода, зато юникод способен воспринимать любой алфавит). Поэтому, что вам нужно употреблять, зависит от конкретной задачи(переносимость или компактность).
    Все строки юникода имеют метод закодировать encode(), используемый для представления символов юникода в качестве управляющих:
    >>> u"дць".encode('utf-8') '\xc3\xa4\xc3\xb6\xc3\xbc'
    Utf-8 означает тип кодировки юникода(Utf-8, Utf-16, ASCII).
    Функция unicode() обеспечивает доступ ко всем кодерам / декодерам юникода: Utf-8, Utf-16, Latin-1, ASCII(0...127 символов), KOI-8, cp1251, cp866. Кодер / декодер по умолчанию – это ASCII, все вызовы функций str() и print используют кодер по умолчанию.
    >>> unicode('\xc3\xa4\xc3\xb6\xc3\xbc', 'utf-8') u'\xe4\xf6\xfc'
    Внимание:кодер по умолчанию ASCII не поддерживает кириллицу, поэтому в функции unicode надо указать предпочтительную кодировку(например, KOI-8).
    >>> u"abc" u'abc'
    >>> str(u"abc") 'abc'
    >>> u"дць" u'\xe4\xf6\xfc'
    >>> str(u"дць") ERROR: символ с кодом больше 127, не входит в ASCII.

    Строки:

    В Питоне строки могут заключаться как в двойные кавычки, так и в одинарные – это не играет никакой роли(если символ кавычек внутри самой строки, то перед ним ставится \):
    >>> 'привет, Питон' 'привет, Питон'
    >>> 'привет, \”Питон\”' 'привет, “Питон”'
    >>> "doesn't" "doesn't"
    >>> '"Yes," he said.' '"Yes," he said.'
    >>> "\"Yes,\" he said." '"Yes," he said.'
    >>> '"Isn\'t," she said.' '"Isn\'t," she said.'
    В английском одинарные о двойные кавычки употребляются в разном контексте
    Если строка слишком длинная, то можно уместить её в нескольких строках путём указания в конце символа \, например:
    а = “Это очень длинная \ строка, содержащая \ 3 строчки\n” >>>print a
    Это очень длинная строка, содержащая 3 строчки
    Символ \n является так называемым управляющим символом, он переводит строку. Чтобы подавить использование управляющих символов в строке, поставьте пред открывающей кавычкой символ r(сырой формат строки).
    Можно также окружать строки тройными кавычками в операторе print(вывести строку в stdout), например:
    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
    При этом внутри тройных кавычек могут следовать любые символы, они будут выведены как есть.
    Со строками можно производить некоторые простые операции, такие как склеивание(+) и повторение строк(*):
    >>> word = 'Да' + 'Нет' >>> word 'ДаНет'
    >>> '<' + word*5 + '>' '< ДаНетДаНетДаНетДаНетДаНет >'
    В Питоне, как и в С, существует индексация строк первый символ строки имеет индекс 0. Индексы обозначаются в квадратных скобках. Вместо индекса можно использовать интервал в форме begin:end, по умолчанию begin – начало строки, а end – её конец.

    >>> word[4] 'Е'

    >>> word[0:2] 'Да'

    >>> word[2:4] 'Не'

    Внимание: в Питоне нельзя непосредственно изменять символы в строке через индексы – это вызовет ошибку интерпретатора. Можно только читать значение символа, что позволяет быстро создавать новые строки по частям других:

    >>> 'x' + word[1:]#Все символы начиная со 2-го 'хаНет'

    >>> 'Не' + word[4]#Только пятый символ 'Нее'

    Все индексы начинают отсчёт с начала строки, однако применение отрицательных индексов позволяет вести отсчёт с конца строки:

    >>> word[-1] #Последний символ 'т'

    >>> word[-2] #Предпоследний символ 'е'

    >>> word[-2:] # Два последних символа 'ет`

    >>> word[:-2] #Все, кроме 2-х последних символов 'ДаН'

    Представим, как работают индексы:

    Д а Н е т
    0 1 2 3 4
    -5 -4 -3 -2 -1
    Если вы задаёте индексы, будте внимательны к этим важным деталям. Кроме того, если вы напишете word[:-100], то индекс будет урезан до -5(то есть выведена вся строка), но если вы укажите несуществующий элемент в качестве индекса 1-го символа word[-100], то будет выдана ошибка.

    Полезный совет: функция len(строка) возвращает длину переданной строки

    >>>len(word) 5

    Структуры.

    Часто нужно иметь некоторую логическую структуру, содержащую в себе поля различных типов, причём добавлять поля нужно динамически, во время исполнения программы. Тогда можно использовать классы, которые на содержат никаких элементов, а затем произвольно добавлять любые поля:
    class Employee: pass
    john = Employee() # Создание пустой структуры
    # Создаём и заполняем поля структуры john.name = 'Иван Иванович' john.dept = 'Программист' john.salary = 100000

    Связывание выражений

    Недовольный полурешениями, один из читателей - Ричарда Дейвис (Richard Davies) - поднял вопрос, можем ли мы целиком переместить связывания в отдельные выражения. Давайте попытаемся понять, зачем нам может этого захотеться, а также продемонстрируем замечательно элегантный способ этого добиться, предоставленный участником comp.lang.python.
    Давайте сначала вспомним о классе Bindings, определенном в модуле functional. Используя свойства этого класса, мы смогли гарантировать, что отдельное имя имеет единственное значение в пределах области данного блока:

    #------- Python FP session with guarded rebinding -------#

    >>> from functional import *

    >>> let = Bindings()

    >>> let.car = lambda lst: lst[0]

    >>> let.car = lambda lst: lst[2]

    Traceback (innermost last):

    File "", line 1, in ?

    File "d:\tools\functional.py", line 976, in __setattr__

    raise BindingError, "Binding '%s' cannot be modified." % name

    functional.BindingError: Binding 'car' cannot be modified.

    >>> let.car(range(10))

    0

    С помощью класса Bindings нам удалось достичь желаемого результата в пределах модуля или функции, но в отношении отдельного выражения мы бессильны. Тем не менее, для семейства ML-языков вполне естественно создавать связывания в пределах отдельного выражения:

    #-------- Haskell expression-level name bindings --------#

    -- car (x:xs) = x -- *could* create module-level binding

    list_of_list = [[1,2,3],[4,5,6],[7,8,9]]

    -- 'where' clause for expression-level binding

    firsts1 = [car x | x <- list_of_list] where car (x:xs) = x

    -- 'let' clause for expression-level binding

    firsts2 = let car (x:xs) = x in [car x | x <- list_of_list]

    -- more idiomatic higher-order 'map' technique

    firsts3 = map car list_of_list where car (x:xs) = x

    -- Result: firsts1 == firsts2 == firsts3 == [1,4,7]


    Грэг Эвинг (Greg Ewing) заметил, что мы можем достичь того же эффекта, воспользовавшись списочными встраиваниями Python (list comprehensions); мы даже можем сделать это почти столь же ясным способом, как в Haskell:



    #------ Python 2.0+ expression-level name bindings ------#

    >>> list_of_list = [[1,2,3],[4,5,6],[7,8,9]]

    >>> [car_x for x in list_of_list for car_x in (x[0],)]

    [1, 4, 7]


    Этот прием - размещение выражения внутри одноэлементного кортежа в списочном встраивании - не позволяет использовать связывание на уровне выражений с функциями высшего порядка. Для использования таких функций мы все еще должны использовать область блока:



    #------- Python block-level bindings with 'map()' -------#

    >>> list_of_list = [[1,2,3],[4,5,6],[7,8,9]]

    >>> let = Bindings()

    >>> let.car = lambda l: l[0]

    >>> map(let.car,list_of_list)

    [1, 4, 7]


    Неплохо, хотя если мы хотим использовать map(), область связывания остается несколько шире, чем мы того хотели. Тем не менее, можно уговорить списочное встраивание делать для нас связывание имен, даже если список - не то, что нам нужно в конечном счете:



    #---- "Stepping down" from Python list-comprehension ----#

    # Compare Haskell expression:

    # result = func car_car

    # where

    # car (x:xs) = x

    # car_car = car (car list_of_list)

    # func x = x + x^2

    >>> [func for x in list_of_list

    ... for car in (x[0],)

    ... for func in (car+car**2,)][0]

    2


    В этом примере мы произвели арифметическое действие над первым элементом первого элемента списка list_of_list

    и одновременно поименовали это действие (но только в области объемлющего выражения). В качестве "оптимизации" можно посоветовать создавать список длиной не более одного элемента, поскольку с помощью индекса [0] в конце выражения выбираем только первый элемент:



    #---- Efficient stepping down from list-comprehension ---#

    >>> [func for x in list_of_list[:1]

    ... for car in (x[0],)

    ... for func in (car+car**2,)][0] 2


    Время опроса

    Давайте подведем итог, собрав вместе некоторые из интроспективных технологий, которые мы рассмотрели в последнем разделе. Для этого определим нашу собственную функцию interrogate(), которая выводит различную информацию о любом объекте, передаваемом в нее. Ниже приведен код, который сопровождается несколькими примерами его применения:


    Листинг 35. Никто и не надеялся
    >>> def interrogate(item): ... """Print useful information about item.""" ... if hasattr(item, '__name__'): ... print "NAME: ", item.__name__ ... if hasattr(item, '__class__'): ... print "CLASS: ", item.__class__.__name__ ... print "ID: ", id(item) ... print "TYPE: ", type(item) ... print "VALUE: ", repr(item) ... print "CALLABLE:", ... if callable(item): ... print "Yes" ... else: ... print "No" ... if hasattr(item, '__doc__'): ... doc = getattr(item, '__doc__') ... doc = doc.strip() # Remove leading/trailing whitespace. ... firstline = doc.split('\n')[0] ... print "DOC: ", firstline ... >>> interrogate('a string') # Строка CLASS: str ID: 141462040 TYPE: VALUE: 'a string' CALLABLE: No DOC: str(object) -> string >>> interrogate(42) # Целое CLASS: int ID: 135447416 TYPE: VALUE: 42 CALLABLE: No DOC: int(x[, base]) -> integer >>> interrogate(interrogate) # Функция, определенная пользователем NAME: interrogate CLASS: function ID: 141444892 TYPE: VALUE: CALLABLE: Yes DOC: Print useful information about item.
    Как следует из последнего примера, наша функция interrogate() работает даже сама с собой. Большей ретроспективности вы вряд ли сможете получить.

    Выражения в списках.

    В Питоне есть альтернативный способ создания списков по определённым правилам, позволяющий избегать использования функций filter(), map(), reduce(): использование выражений внутри списков. Такие выражения имеют следующий формат: заголовок цикла for, задающий ограничения при создании списков, за этим циклом может(необязательно) следовать некоторое количество условий if и циклов for, по которым, собственно, и создаётся результативный список. Приведём пример таких выражений:
    >>> freshfruit = [' банан', ' ананас', 'яблоко '] >>> [weapon.strip() for weapon in freshfruit] ['банан', 'ананас', 'яблоко']
    >>> vec = [2, 4, 6] >>> [3*x for x in vec] [6, 12, 18]
    >>> [3*x for x in vec if x > 3] [12, 18]
    >>> [3*x for x in vec if x < 2] []
    >>> [{x: x**2} for x in vec] [{2: 4}, {4: 16}, {6: 36}]
    >>> [[x,x**2] for x in vec] [[2, 4], [4, 16], [6, 36]]
    >>> [(x, x**2) for x in vec] [(2, 4), (4, 16), (6, 36)]
    >>> vec1 = [2, 4, 6] >>> vec2 = [4, 3, -9] >>> [x*y for x in vec1 for y in vec2] [8, 6, -18, 16, 12, -36, 24, 18, -54]
    >>> [x+y for x in vec1 for y in vec2] [6, 5, -7, 8, 7, -5, 10, 9, -3]
    >>> [vec1[i]*vec2[i] for i in range(len(vec1))] [8, 12, -54]

    Вызываемые структуры

    Объекты, которые представляют потенциальное поведение (функции и методы), могут быть запущены, или вызваны. С помощью функции callable() мы можем установить, вызываемый ли это объект:


    Листинг 32. Можешь что-нибудь для меня сделать?
    >>> print callable.__doc__ callable(object) -> Boolean
    Return whether the object is callable (i.e., some kind of function). Note that classes are callable, as are instances with a __call__() method.
    >>> callable('a string') 0 >>> callable(dir) 1

    представляемые как отложенные последовательности, мощная

    Итераторы, представляемые как отложенные последовательности, мощная концепция, открывающая новые стили Питоновского программирования. Хотя имеется тонкое различие между представлением итератора как источника данных и его представлением в последовательном стиле. Ни один способ представления сам по себе не правильнее другого, но второй открывает путь к комбинаторному сокращению для манипулирования программными событиями. Комбинаторные функции в itertools (и особенно некоторые, которые он может развить, как те, что я предлагаю) близко подходят к декларативному стилю программирования. По моему мнению, эти декларативные стили менее подвержены ошибкам и более мощны.

    Кто бы мог подумать, что

    Кто бы мог подумать, что интроспекция окажется столь простой и полезной? И все же, заканчивая, я должен предостеречь: не примите результаты интроспекции за знание в последней инстанции. Опытный программист Python знает, что всегда есть больше, чем дает интроспекция, и, следовательно, ее исход вовсе не исчерпывающее знание. Программирование порождает больше вопросов, чем ответов. Что исключительно хорошо в Python, как мы поняли из этой статьи, так это то, что он действительно отвечает на ваши вопросы. Что до меня, не считайте себя обязанными отплатить мне за то, что я помог вам понять, что предлагает Python. Программирование на Python само по себе награда. Все, что я прошу у своих друзей-Питонистов, это угощения за счет общественности.


    Эта статья продемонстрировала способы замены

    Эта статья продемонстрировала способы замены практически любой конструкции управления потоком в Python на функциональный эквивалент (избавляясь при этом от побочных эффектов). Эффективный перевод конкретной программы требует дополнительного обдумывания, но мы увидели, что встроенные функциональные примитивы являются полными и общими. В последующих статьях мы рассмотрим более мощные подходы к функциональному программированию; и, я надеюсь, сможем подробнее рассмотреть "pro" и "contra" функционального подхода.

    Заключительные действия.

    Часто исключения бывают критическими, то есть после того, как они возникнут дальнейшее продолжение программы не является целесообразным. Но при завершении программы необходимо, например, закрыть все файлы, записать несохранённые данные на диск, послать отчёт автору. Для этого используется конструкция try...__finally:
    >>> try: ... raise KeyboardInterrupt ... finally: ... print 'Goodbye, world!' ... Goodbye, world!
    Traceback (most recent call last):
    File "", line 2, in ?
    KeyboardInterrupt
    __finally блок выполняется независимо от того произошло исключение или нет, если исключение произошло, то оно возбуждается ещё раз. Оператор try может иметь или блоки except или __finally, но не одновременно. Блок __finally выполняется даже во вложенных try, поэтому использование вложенных try(с except) и внешнего с __finally способно отловить и корректно обработать любые возникшие исключения.


    Закрытые переменные.

    В Питоне пока очень ограниченная поддержка закрытых(private) элементов класса, то есть элементам, доступным только членам данного класса. Применение таких элементов соответствует принципу сокрытия информации и исключает бесконтрольное изменение важных полей класса вне его. Рассмотрим механизм таких элементов.
    Если вы объявляете какой-либо элемент, начиная его с двойного подчёркивания, то он автоматически становится закрытым и обращение к нему вне класса вызывает синтаксическую ошибку, в то время как обращение через self является приемлемым:
    >>> class Test2: ... __foo = 0 ... def set_foo(self, n): ... if n > 1: ... self.__foo = n ... print self.__foo ... >>> x = Test2() >>> x.set_foo(5)
    5
    >>> x.__foo Traceback (most recent call last): File "", line 1, in ? AttributeError: Test2 instance has no attribute '__foo'
    Кроме этого закрытой является также переменная, содержащаяся в любом модуле __dict__.

    Замыкание

    В ФП существует очень интересное понятие - замыкание (closure). На самом деле, эта идея оказалась настолько заманчивой для многих разработчиков, что реализована даже в нефункциональных языках программирования, таких как Perl и Ruby. Кроме того, похоже, что в Python 2.1 неизбежно будет включен лексический контекст, что на 99% приблизит нас к замыканиям.
    Так что же такое замыкание? Стив Маджевски (Steve Majewski) замечательно охарактеризовал это понятие в одной из посвященных Python сетевых конференций:
    Объект - это совокупность данных вместе с привязанными к ним процедурами...

    Замыкание - это процедура вместе с привязанноой к ней совокупностью данных.
    Другими словами, замыкание есть что-то наподобие функционального доктора Джекилла по отношению к объектно-ориентированному мистеру Хайду (или, возможно, наоборот). Замыкание, так же как и экземпляр объекта, есть способ представления функциональности и данных, связанных и упакованных вместе.
    Давайте вернемся немного назад, чтобы понять, какую проблему решают как объекты, так и замыкания, и как эта проблема решается без них. Обычно результат, возвращаемый функцией, определяется контекстом во время ее вычисления. Самый общий - и, возможно, самый очевидный - способ определить этот контекст - это передать в функцию параметры, указывающие, какие значения должны обрабатываться. Но иногда имеется вполне очевидное различие между "фоновыми" и "приоритетными" параметрами - между тем, что делает функция в данный момент, и тем, как она сконфигурирована для выполнения многократных потенциальных вызовов.
    Существует ряд способов поддержки фоновых аргументах, акцентируя внимание на приоритетных. Например, можно просто передавать каждый необходимый аргумент в функцию при каждом ее вызове. Часто это сводится к передаче ряда значений (или структуры со множеством членов) на протяжении всей последовательности вызовов, чтобы передать данные туда, где они могут потребоваться. Это иллюстрируется простым примером:



    #--------- Python session showing cargo variable --------#

    >>> def a(n):

    ... add7 = b(n)

    ... return add7

    ...

    >>> def b(n):

    ... i = 7

    ... j = c(i,n)

    ... return j

    ...

    >>> def c(i,n):

    ... return i+n

    ...

    >>> a(10) # Pass cargo value for use downstream

    17


    В этом примере, параметр n в пределах функции b() нужен только для того, чтобы быть доступным для передачи в c().

    Другое возможное решение - использование глобальных переменных:



    #--- Сессия Python, показывающая использование глобальной переменной ---#

    >>> N = 10

    >>> def addN(i):

    ... global N

    ... return i+N

    ...

    >>> addN(7) # Добавить глобальную переменную N к аргументу

    17

    >>> N = 20

    >>> addN(6) # Добавить глобальную переменную N к аргументу

    26


    Глобальная переменная доступна в любой момент, где бы вы ни вызывали addN(), при этом вовсе не обязательно явно передавать фоновый контекст .

    Несколько более "питоновская" техника - "заморозить" переменную в функции, используя для этого значение параметра по умолчанию во время определения функции:



    #-------- Сессия Python, иллюстрирующая замороженную переменную --------#

    >>> N = 10

    >>> def addN(i, n=N):

    ... return i+n

    ...

    >>> addN(5) # Добавить 10

    15

    >>> N = 20

    >>> addN(6) # Добавить 10 (текущее значение N не играет роли)

    16


    Замороженная нами переменная, в сущности, замыкание. Некие данные прикреплены к функции addN(). В случае полного

    замыкания, все данные, присутствовавшие в момент описания этой функции, были бы доступны при ее вызове. Однако в данном примере (и во многих более серьезных) можно просто обеспечить доступ к достаточному количеству данных с помощью параметров по умолчанию.


    Ведь переменные, которые не используются функцией addN(), не играют никакой роли при ее вычислении.

    Давайте рассмотрим объектный подход на примере несколько более насущной проблемы. В это время года мысли обычно заняты интерактивными программами, используемыми для вычисления налога. Они собирают различные данные - необязательно в определенном порядке - а затем в какие-то момент используют их при вычислении. Давайте создадим упрощенный вариант такой программы:



    #----- Класс в стиле Python для вычисления налога ------#

    class TaxCalc:

    def taxdue(self):

    return (self.income-self.deduct)*self.rate

    taxclass = TaxCalc()

    taxclass.income = 50000

    taxclass.rate = 0.30

    taxclass.deduct = 10000

    print "Pythonic OOP taxes due =", taxclass.taxdue()


    В нашем классе TaxCals (точнее, в его экземпляре) мы можем собрать данные - в любом порядке - и как только у нас будут все необходимые элементы, можно будет вызвать метод объекта, чтобы выполнить вычисление над собранными данными. Все собрано в пределах экземпляра и, тем самым, разные экземпляры могут задавать разные наборы данных. Создание множества экземпляров, отличающихся друг от друга только своими данными, невозможно при использовании глобальных или "замороженных" переменных. Подход "грубой силы" может решить эту задачу, но можно видеть, что в реальном примере может понадобиться передача множества значений.

    Теперь давайте посмотрим, как можно решить эту задачу с помощью ООП с использованием передачи сообщений (это похоже на Smalltalk или Self, так же как и на некоторые объектно-ориентированные варианты xBase, которыми я пользовался):




    #------- Smalltalk-style (Python) tax calculation -------#

    class TaxCalc:

    def taxdue(self):

    return (self.income-self.deduct)*self.rate

    def setIncome(self,income):

    self.income = income

    return self

    def setDeduct(self,deduct):

    self.deduct = deduct

    return self

    def setRate(self,rate):

    self.rate = rate

    return self

    print "Smalltalk-style taxes due =", \

    TaxCalc().setIncome(50000).setRate(0.30).setDeduct(10000).taxdue()


    Возвращение self каждым установщиком позволяет нам рассматривать текущий экземпляр как результат вызова каждого метода. Как видно в дальнейшем, этот подход имеет интересные общие черты с использованием замыкания в ФП.

    Работая с Xoltar toolkit, можно создавать полные замыкания, имеющие требуемое свойство объединения данные с функцией, а также множественные замыкания, несущие в себе различные наборы данных:



    #------- Python Functional-Style tax calculations -------#

    from functional import *

    taxdue = lambda: (income-deduct)*rate

    incomeClosure = lambda income,taxdue: closure(taxdue)

    deductClosure = lambda deduct,taxdue: closure(taxdue)

    rateClosure = lambda rate,taxdue: closure(taxdue)

    taxFP = taxdue

    taxFP = incomeClosure(50000,taxFP)

    taxFP = rateClosure(0.30,taxFP)

    taxFP = deductClosure(10000,taxFP)

    print "Functional taxes due =",taxFP()

    print "Lisp-style taxes due =", \

    incomeClosure(50000,

    rateClosure(0.30,

    deductClosure(10000, taxdue)))()


    Каждая описанная нами функция замыкания берет любые значения, определенные в ее области видимости, и привязывает эти величины к глобальной области видимости функционального объекта. Однако то, что кажется глобальной областью данной функции, необязательно совпадает как с "настоящей" глобальной областью действия модуля, так и с глобальной областью другого замыкания. Замыкание просто "несет данные с собой".

    В нашем примере, чтобы поместить определенные значения в область действия замыкания, мы используем несколько частных функций (income, deduct, rate). Было бы достаточно просто изменить дизайн так, чтобы было можно присваивать произвольные значения. Кроме того, ради развлечения, мы используем в этом примере два слегка различных функциональных стиля. Первый последовательно привязывает дополнительные значения к области замыкания; сделав taxFP изменяемой, мы позволяем строкам добавить в замыкание появляться в любом порядке. Однако, если бы мы использовали неизменяемые имена наподобие tax_with_Income, нам пришлось бы расположить связывания в определенном порядке и передавать более ранние последующим.


    В любом случае, как только все необходимое привязано к замыканию, мы можем вызывать выращенную функцию.

    Второй стиль, на мой взгляд, несколько похож на Lisp (исключительно из-за присутствия круглых скобок). Помимо эстетики, во втором стиле присутствуют два интересных момента.

    Во-первых, полностью отсутствует привязывание имен. Второй стиль - одиночное выражение без использования директив. (см. предыдущую статье, где объясняется, почему это играет роль).

    Другая интересная деталь Lisp-стиля в том, насколько сильно его использование замыканий напоминает методы передачи сообщений a la Smalltalk, о которых говорилось выше. В обоих случаях значения накопливаются до вызова функции/метода taxdue()

    (оба в этих упрощенных версиях возбуждают исключения, если требуемые данные недоступны). Smalltalk-стиль на каждом шаге передает объект, в то время как Lisp-стиль - продолжение (continuation). Но если смотреть в корень, то функциональное и объектно-ориентированное программирование приводят почти к одному и тому же.

    

        Программирование: Языки - Технологии - Разработка