В глубь языка Python

         

Фильтрование списков


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

Пример 2.13. Синтаксис фильтрования списков

[mapping-expression for element in source-list if filter-expression]

Это дополнительная возможность в , которую вы обязательно полюбите. Начало расширенной записи остается прежним, а в конце, начиная с if, добавляется условие, по которому будет производиться фильтрование. Условие может быть любым выражением, которое дает истину или ложь (в Python это может быть ). Любой элемент, для которого условие дает истину, будет участвовать в отображении. Все остальные элементы игнорируются, то есть в выражение отображения не подставляются и в результат не включаются.

Пример 2.14. Введение в фильтрование списков

>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"] >>> [elem for elem in li if len(elem) > 1]

['mpilgrim', 'foo'] >>> [elem for elem in li if elem != "b"]

['a', 'mpilgrim', 'foo', 'c', 'd', 'd'] >>> [elem for elem in li if li.count(elem) == 1]

['a', 'mpilgrim', 'foo', 'c']

В данном случае выражение отображения совсем простое (значение каждого элемента), так что сосредоточьтесь на условии фильтра. Каждый элемент пробегаемого списка Python пропускает через фильтр. Если условие фильтра дает истину, элемент участвует в преобразовании и результат включается в возвращаемый список. В данном случае мы исключили все строки длиной в один символ.
Здесь мы исключаем элементы с одним определенным значением, "b". Обратите внимание, что фильтром отбрасываются все элементы со значением "b", так как во всех случаях выражение условия будет давать ложь.
Метод count списка возвращает количество вхождений элементов определенного значения в список. Вы можете подумать, что этот фильтр исключает все дубликаты, и возвращаемый список будет содержать по одному значению исходного списка. Но это не так, потому что значения, входящие в исходный список дважды (в данном случае это, "b" and "d") полностью исключаются. Существует множество способов исключить дубликаты из списка, но не так.
<
Пример 2.15. Фильтрование списка в apihelper.py

methodList = [method for method in dir(object) if callable(getattr(object, method))]

Этот пример выглядит сложным, но основная структура остается прежней. Все выражение дает список, который присваивается переменной methodList. Выражение отображения простое: оно дает значение каждого элемента. Функция dir(object) возвращает список всех атрибутов и методов объекта object — это тот список, который мы преобразуем. И единственная новая часть — это условие фильтра после if.

Выражение фильтра выглядит жутко, но он таковым не является. Вы уже знаете о , и . Как вы могли видеть в , выражение getattr(object, method) дает объект-функцию, если object является модулем и method содержит имя функции из этого модуля.

Таким образом, мы берем объект object, получаем список имен его атрибутов, методов, функций, и затем фильтруем этот список, избавляясь от всего, что нас не интересует. Для того, чтобы избавиться от ненужного, мы берем имя каждого атрибута/метода/функции, с помощью функции getattr получаем настоящий объект. Затем мы проверяем, поддерживает ли объект вызов, таким образом подхватывая все функции и методы — как встроенные (например, метод pop списков), так и определенные пользователем (например, функция buildConnectionString в модуле odbchelper). Нас не интересуют другие атрибуты, такие как обязательный для всех моделей атрибут __name__.

Дополнительная литература

описывает другой способ фильтрования списков .
  5  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Сила самоанализа


Содержание

2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. 2.8. 2.9.



Использование lambda-функций


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

Пример 2.21. lambda-функции

>>> def f(x): ... return x*2 ...

>>> f(3) 6 >>> g = lambda x: x*2

>>> g(3) 6 >>> (lambda x: x*2)(3)



6

Эта lambda-функция делает то же самое, что и обычная функция, определенная выше. Обратите внимание на сокращенный синтаксис: список аргументов записывается без скобок и ключевое слово return

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

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

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

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

Пример 2.22. lambda-функции в apihelper.py

processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

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


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

Пример 2.23. split без аргументов

>>> s = "this is\na\ttest"


>>> print s this is a test >>> print s.split()


['this', 'is', 'a', 'test'] >>> print " ".join(s.split())


'this is a test'
Это строка, которая содержит символ переход на новую строку, записанный в виде специальной последовательности (такие строки могут быть также записаны с использованием ). \n — переход на новую строку, \t — символ горизонтальной табуляции.
Метод split без аргументов разбивает строку по символам пропуска. В данном случае три пробела, переход на новую строку и табуляция воспринимаются одинаково.
Вы можете нормализовать пропуски разбив строку, а затем снова объединив ее, используя один пробул в качестве разделителя. Именно это делает функция help для того, чтобы свернуть строку документации.
Так что же на самом деле делает функция help с этими lambda-функциями, методами split и приемом с and-or?

Пример 2.24. Присваивание функции переменной

processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

processFunc теперь ссылается на функцию, но на какую — это зависит от значения переменной collapse. Если collapse является истиной, processFunc(string)

будет сворачивать символы пропуска, в противном случае processFunc(string)

будет возвращать аргумент без изменений.

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

Дополнительная литература

описывает использование lambda

для . показывает, как ( поясняет, что в будущих версиях Python это будет сделать проще).В приводятся примеры .
  7  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Необязательные и именованные аргументы


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

Пример 2.4. help — функция с двумя необязательными аргументами

def help(object, spacing=10, collapse=1):

Аргументы spacing и collapse могут быть опущены при вызове, так как для них определены значения по умолчанию. Аргумент object не имеет значения по умолчанию, поэтому должен быть указан всегда. Если функция help вызывается только с одним аргументом, то spacing принимает значение 10 и collapse принимает значение 1. Если help вызывается с двумя аргументами, collapse также принимает значение 1.

Представьте теперь, что вы хотите указать значение аргумента collapse, но использовать значение по умолчанию для spacing. В большинстве языков вам придется передавать все три аргумента. Однако Python позволяет передавать аргументы в произвольном порядке по имени.

Пример 2.5. Возможные способы вызова help

help(odbchelper)

help(odbchelper, 12)

help(odbchelper, collapse=0)

help(spacing=15, object=odbchelper)

С одним аргументом, spacing и collapse получают значения по умолчанию, 10 и 1 соответственно.
С двумя аргументами, collapse получает значение по умолчанию 1.
Здесь вы явно указываете имя аргумента, collapse, для которого передается значение. Аргумент spacing

по-прежнему получает значение по умолчанию 10.

Даже обязательные аргументы (в данном случае — object), которые не имеют значения по умолчанию, могут быть переданы по имени, и именованные аргументы могут быть указаны в произвольном порядке.

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

Все что Вам нужно сделать для вызова функции — это указать значение для каждого обязательного аргумента. Способ передачи аргументов и порядок их следования — дело Вашего вкуса.
Дополнительная литература

описывает, . Это важно в случаях, когда значение по умолчанию является списком, словарем или другим изменяемым объектом.
  2  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Объединяем все вместе


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

Пример 2.25. Основная часть apihelper.py

print "\n".join(["%s %s" % (method.ljust(spacing), processFunc(str(getattr(object, method).__doc__))) for method in methodList])

Обратите внимание, что это одна команда разбитая на несколько строк без явного использования признака продолжения (“\”). Помните, я говорил, что без использования обратного слэша? Расширенная запись списков — один из таких случаев, так как все выражение заключено в квадратные скобки.

Давайте разберем его с конца. Фраза

for method in methodList

говорит о том, ято мы имеем дело с . Как вы уже знаете, methodList является списком объекта object. Таким образом мы пробегаем по списку методов и каждый метод доступен через переменную method.

Пример 2.26. Динамическое извлечение строки документации

>>> import odbchelper >>> object = odbchelper

>>> method = 'buildConnectionString'

>>> getattr(object, method)

<function buildConnectionString at 010D6D74> >>> print getattr(object, method).__doc__

Создает и возвращает строку соединения из словаря параметров.

В функцию help object — объект, для которого мы хотим получить помощь — передается в качестве аргумента.
По ходу того, как мы пробегаем по списку methodList, method содержит имя текущего метода.
С помощью функции мы получаем сам метод method объекта object.
Теперь осталось самое простое — распечатать строку документации метода.

Следующий кирпичик — использование функции str. Как вы уже знаете, str — встроенная функция, . Но строка документации уже является строкой, так зачем же суетиться с использованием функции str? На самом деле не каждая функция имеет строку документации, и если строки документации нет, ее атрибут __doc__ равен None.

Пример 2.27. Зачем использовать str для строк документации?


>>> {}.keys.__doc__


>>> {}.keys.__doc__ == None


1 >>> str({}.keys.__doc__)


'None'
Метод keys словаря не имеет строки документации, так что его атрибут __doc__ равен None. Если вы набираете выражение для атрибута __doc__

непосредственно в интерактивном режиме, интерпретатор Python ничего не выводит, что может сбить с толку (такое поведение интерпретатора действительно удобно, если впомнить о процедурах, которые в языке Python возвращают None).
Вы можете убедиться, что атрибут __doc__ действительно равен None простым сравнением.
Функция str возвращает строковое представление объекта, в данном случае 'None'.
В SQL вы должны использовать IS NULL вместо = NULL для сравнения с пустым значением. В языке Python вы можете использовать как == None, так и is None, но второй вариант работает быстрее.
Теперь, когда мы уверены в том, что значение всегда будет строковым, можно передать его функции processFunc, которая сворачивает символы пропуска или возвращает строку без изменений. Теперь вы видите, почему важно использовать функцию str для преобразования None к строке. processFunc считает, что ее аргумент является строкой и вызывает его метод split. При попытке вызвать processFunc

для None будет сгенерировано исключение, так как у None нет метода split.

Далее мы снова используем форматирование, чтобы соединить значение, возвращаемое функцией processFunc, со результатом применения метода ljust к строке method. Это новый метод строковых объектов, который мы раньше не видели.

Пример 2.28. Метод ljust

>>> s = 'buildConnectionString' >>> s.ljust(30)


'buildConnectionString ' >>> s.ljust(20)


'buildConnectionString'
ljust дополняет строку пробелами до указанной длины. Именно это делает функция help при выводе в две колонки для выравнивания строк документации во второй колонке.
Если требуемая длина меньше, чем исходная длина строки, ljust вернет строку без изменений. Этот метод никогда не обрезает строку.
Ну вот и почти все сделано.


Имея метод ljust, дополняющий имя метода пробелами до нужной длины, и строку документации (возможно со свернутыми символами пропуска), которую возвращает processFunc, мы объединяем их и получаем одну строку. Так как мы пробегаем по списку methodList, то получим список строк. Используя метод join

строки "\n", мы объединяем строки в одну с символом перехода на новую строку в качестве разделителя и печатаем результат.

Пример 2.29. Печать списка

>>> li = ['a', 'b', 'c'] >>> print "\n".join(li)


a b c
Этот прием также будет будет полезен при отладке для вывода списков. А в языке Python вы будете работать со списками постоянно.
Теперь код обрел смысл.

Пример 2.30. Основная часть apihelper.py, пересмотренная

print "\n".join(["%s %s" % (method.ljust(spacing), processFunc(str(getattr(object, method).__doc__))) for method in methodList])
  8  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Особенности операторов and и or


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

Пример 2.16. Оператор and

>>> 'a' and 'b'

'b' >>> '' and 'b'

'' >>> 'a' and 'b' and 'c'

'c'

При использовании оператора and, значения вычисляются в булевом контексте слева напрво. Значения 0, '', [], (), {} и None

являются ложью, все остальное является истиной[3]. Если у and оба операнда являются истиной, результатом будет последнее значение. В данном случае вычисляется выражение 'a', которое является истиной, затем 'b', которое также является истиной, и возвращается 'b'.

Если какой-либо из операндов является ложью, результатом будет первое такое значение. В данном случает '' — первое значение, являющееся ложью.
Все значения являются истиной, так что в результате мы получаем последнее — 'c'.

Пример 2.17. Оператор or

>>> 'a' or 'b'

'a' >>> '' or 'b'

'b' >>> '' or [] or {}

{} >>> def sidefx(): ... print "in sidefx()" ... return 1 >>> 'a' or sidefx()

'a'

Как и для and опреранды or вычисляются в булевском контексте слева направо. Если операнд является истиной, or

немедленно возвращает результат. В данном случае 'a'

— первое истинное значение.

or вычисляет выражение '', которое является ложью, затем 'b', которое является истиной, и возвращает 'b'.
Если все значения являются ложью, or возвращает последнее. or вычисляет '' (ложь), []

(ложь), затем {} (ложь) и возвращает {}.

Обратите внимание, что or вычисляет операнды до тех пор, пока не найдет истинное значение, остальное игнорируется. Это имеет значение, когда вычисление операнда дает сторонние эффекты. В данном случае функция sidefx не вызывается, так как для получения результата выражения с опереатором or достаточно того, что первый операнд, 'a', является истиной.

Если вы используете C, то, наверное, знакомы с выражением bool ? a : b, которое дает a, если bool


является истиной, b, если bool

ложно. Благодаря особенностям работы операторов and и or

в Python, вы можете достигнуть анологичного эффекта.

Пример 2.18. Прием с and-or

>>> a = "first" >>> b = "second" >>> 1 and a or b


'first' >>> 0 and a or b


'second'
Синтаксис bool ? a : b в языке Python выглядит похоже. Все выражение вычисляется слева направо, так что оператор and применяется первым. 1 and 'first' дает 'first', затем 'first' or 'second' дает 'first'.
0 and 'first' дает 0, тогда 0 or 'second' дает 'second'.
Однако, так как такое выражение выполняет обычные логические операции, а не является специальной конструкцией языка, существует очень важная разница между приемом с and-or в языке Python и конструкцией bool ? a : b в C. Если значение a является ложью, это прием не будет работать так как вы могли ожидать. (Вы можете сказать, что обожгись на этом? Более одного раза?)

Пример 2.19. Когда прием с and-or не работает

>>> a = "" >>> b = "second" >>> 1 and a or b


'second'
Так как a — пустая строка, которую Python считает ложью в булевом контексте, 1 and '' дает '', а '' or 'second' дает 'second'. Ой! Это не то, что мы хотели получить.
Прием с and-or, bool and a or b, не будет работать так же, как конструкция bool ? a : b в C, если a является ложью.
Для безопасного использования приема с and-or необходимо сделать так, чтобы a всегда было истинным. Один из самых распространенных способо это сделать — заменить a на [a] и b на [b], тогда первый элемент получаемого списка будет либо a, либо b.

Пример 2.20. Безопасное использование приема с and-or

>>> a = "" >>> b = "second" >>> (1 and [a] or [b])[0]


''
Так как выражение [a] является непустым списком, оно никогда не будет ложно. Даже если a

равно 0, '' или другому значению, являющемуся ложью, список [a] всегда будет являться истиной, потому что содержит один элемент.


Кажется, этот прием не стоит того, чтобы его использовать. В конце концов вы всегда можете воспользоваться интсрукцией if. Тогда зачем вся эта суета? Конечно, в большинстве случаев выбирая между двумя константами вы можете использовать простой синтаксис и не беспокоиться, потому что значение a всегда будет истинным. И даже если придется использовать более сложный синтакс, на это могут найтись весские причины: в некоторых случаях в языке Python использование инструкции if не допускается, например в lambda-функциях.

Дополнительная литература

В обсуждаются .

Footnotes

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

  6  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Получение атрибутов с помощью getattr


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

Пример 2.11. Функция getattr

>>> li = ["Larry", "Curly"] >>> li.pop

<built-in method pop of list object at 010DF884> >>> getattr(li, "pop")

<built-in method pop of list object at 010DF884> >>> getattr(li, "append")("Moe")

>>> li ["Larry", "Curly", "Moe"] >>> getattr({}, "clear")

<built-in method clear of dictionary object at 00F113D4> >>> getattr((), "pop")

Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'pop'

Таким образом вы получаете метод pop списка. Обратите внимание, что вы не вызываете его (для этого нужно выполнить li.pop()), а получаете в виде объекта.
Здесь мы также получаем метод pop, но теперь имя метода указано в виде строкового аргумента функции getattr. getattr — очень полезная функция, позволяющая получить любой атрибут любого объекта. В данном случае объектом является список, а его атрибутом — метод pop.
В случае, если вы еще не до конца осознали, насколько это может быть полезным, попробуйте выполнить этот код: значение, возвращаемое функцией getattr является методом, который можно вызвать, как если бы вы просто вызвали li.append("Moe"). Но вы не вызываете метод напрямую — вы указываете имя метода в виде строки.
getattr работает и для словарей.
Теоретически, getattr работает и для кортежей, но , так что getattr сгенерирует исключение независимо от имени атрибута, которое вы дадите.

getattr предназначен не только для встроенных типов данных. Он также работает и для модулей.

Пример 2.12. getattr в apihelper.py

>>> import odbchelper >>> odbchelper.buildConnectionString

<function buildConnectionString at 00D18DD4> >>> getattr(odbchelper, "buildConnectionString")


<function buildConnectionString at 00D18DD4> >>> object = odbchelper >>> method = "buildConnectionString" >>> getattr(object, method)


<function buildConnectionString at 00D18DD4> >>> type(getattr(object, method))


<type 'function'> >>> import types >>> type(getattr(object, method)) == types.FunctionType 1 >>> callable(getattr(object, method))


1
Так мы можем получить функцию buildConnectionString из модуля odbchelper, который сы изучали в разделе . (Шестнадцатиричный адрес характерен для моей машины, у вас он будет другим.)
Используя getattr, мы можем получить ту же самую функцию. В общем, getattr(object, "attribute") дает такой же результат, как object.attribute. Если object является модулем, attribute может быть любым объектом, определенном в этом модуле: функцией, классом или другой глобальной переменной.
На самом деле именно это и делает функция help. object передается функции в качестве аргумента; method является строкой с именем метода или функции.
В данном случае, method содержит имя функции, в чем мы можем убедиться получив ее .
Так как method является функцией, ее можно .
  4  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

В глубь


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

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

Пример 2.1. apihelper.py

Если вы еще этого не сделали, можете , используемые в книге.

def help(object, spacing=10, collapse=1):

"""Выводит методы и строки документации.

В качестве аргумента может использоваться модуль, класс, список, словарь или строка.""" methodList = [method for method in dir(object) if callable(getattr(object, method))] processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s) print "\n".join(["%s %s" % (method.ljust(spacing), processFunc(str(getattr(object, method).__doc__))) for method in methodList])

if __name__ == "__main__":

print help.__doc__

Этот модуль определяет одну функцию — help. Она воспринимает три аргумента: object, spacing и collapse. Последние два аргумента, как мы скоро увидим, не являются обязательными.
Функция help имеет многострочную , кратко описывающую предназначение функции. Обратите внимание, что здесь не упомянуто возвращаемое значение — функция предназначениа для выполнения определенных действий а не возврата значения.
Тело функции .
с if __name__ позволяет использовать модуль в качестве программы без каких-либо конфликтов. В данном случае программа просто выводит строку документации функции help.
для сравнения используется оператор ==. Заключать в скобки выражение совсем не обязательно.
<
Функция help предназначена для использования программистом при работе в Python IDE. В качестве аргумента можно использовать любой объект, имеющий функции (например, модуль) или методы (например, список). Эта функция выводит имена функций (или метододов) объекта и их строки документации.

Пример 2.2. Использование apihelper.py

>>> from apihelper import help >>> li = [] >>> help(li) append L.append(object) -- append object to end count L.count(value) -> integer -- return number of occurrences of value extend L.extend(list) -- extend list by appending list elements index L.index(value) -> integer -- return index of first occurrence of value insert L.insert(index, object) -- insert object before index pop L.pop([index]) -> item -- remove and return item at index (default last) remove L.remove(value) -- remove first occurrence of value reverse L.reverse() -- reverse *IN PLACE* sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1

По умолчанию информация выводится в легко воспринимаемом виде. Многострочные строки документации преобразуются в одну длинную строку, но это особенность можно отключить, передав 0 в качестве аргумента collapse. Кроме того, если имена функций содержат 10 символов, вы можете установить большее значение аргумента spacing.

Пример 2.3. Дополнительные возможности apihelper.py

>>> import odbchelper >>> help(odbchelper) buildConnectionString Build a connection string from a dictionary Returns string. >>> help(odbchelper, 30) buildConnectionString Build a connection string from a dictionary Returns string. >>> help(odbchelper, 30, 0) buildConnectionString Build a connection string from a dictionary

Returns string.
 1  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

В качестве аргумента может использоваться


Теперь код программы apihelper.py обрел смысл.
Пример 2.31. apihelper.py
def help(object, spacing=10, collapse=1): """Выводит методы и строки документации.
В качестве аргумента может использоваться модуль, класс, список, словарь или строка.""" methodList = [method for method in dir(object) if callable(getattr(object, method))] processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s) print "\n".join(["%s %s" % (method.ljust(spacing), processFunc(str(getattr(object, method).__doc__))) for method in methodList])
if __name__ == "__main__": print help.__doc__
Пример 2.32. Вывод apihelper.py
>>> from apihelper import help >>> li = [] >>> help(li) append L.append(object) -- append object to end count L.count(value) -> integer -- return number of occurrences of value extend L.extend(list) -- extend list by appending list elements index L.index(value) -> integer -- return index of first occurrence of value insert L.insert(index, object) -- insert object before index pop L.pop([index]) -> item -- remove and return item at index (default last) remove L.remove(value) -- remove first occurrence of value reverse L.reverse() -- reverse *IN PLACE* sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1
Перед тем как перейти к следующей главе убедитесь, что вы овладели следующими навыками:
Определение и вызов функций с Использование функции для получения строкового представления произвольного объектаИспользование функции для динамического доступа к атрибутамИспользование расширенной записи списков для Распозновани и его безопасное использованиеОпределение и использование этой переменной для вызова. Понимание этого приема жизнено необходимо для понимания языка Python. В книге вы увидите более сложные приложения этой концепции.
 

A.0. Preamble


The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

 1  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



A. GNU Free Documentation License


Содержание

A.0. A.1. A.2. A.3. A.4. A.5. A.6. A.7. A.8. A.9. A.10. A.11.

Version 1.1, March 2000

Copyright (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.



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


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

Пример 1.4. Определение строки документации для buildConnectionString

def buildConnectionString(params): """Создает и возвращает строку соединения из словаря параметров."""

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

Утроенные кавычки являются удобным способом определения строк, содержащих одновременно одинарные и двойные кавычки, аналогично qq/.../

в Perl.

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

Многие Python IDE используют строки документации для контекстной справки. Например, во время ввода имени функции строка документации может отображаться в виде всплывающей подсказки. Такая помощь может быть очень полезной — все зависит от того, насколько хорошо написана строка документации.

Дополнительная литература

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

  3  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



Форматированное представление


Python позволяет получить форматированное представление значений в виде строки. Хотя строка формата может содержать довольно сложные выражения, чаще всего используется вставка значений в строку с помощью шаблона %s.

Python использует для строк формата такой же синтаксис, как и функция sprintf в C.

Пример 1.28. Введение в форматирование

>>> k = "uid" >>> v = "sa" >>> "%s=%s" % (k, v)

'uid=sa'

При вычислении всего выражения получается строка. Первый шаблон %s заменяется значением k, второй — значением v. Все остальные символы в строке формата (в данном случае — знак равенства) попадают в результат без изменений.

Обратите внимание, что (k, v) является кортежем. Я уже говорил, что кортежи весьма полезный тип данных.

Вы можете подумать, что здесь слишком много работы для простого объединения строк. И вы будете правы, но форматирование — это не просто объединение строк. Это даже не просто форматирование. Данная операция также выполняет приведение типа.

Пример 1.29. Форматирование vs. объединение

>>> uid = "sa" >>> pwd = "secret" >>> print pwd + " is not a good password for " + uid

secret is not a good password for sa >>> print "%s is not a good password for %s" % (pwd, uid)

secret is not a good password for sa >>> userCount = 6 >>> print "Users connected: %d" % (userCount, )

Users connected: 6 >>> print "Users connected: " + userCount

Traceback (innermost last): File "<interactive input>", line 1, in ? TypeError: cannot add type "int" to string

Оператор + позволяет объединять строки.
В простейшем случае форматирование дает такой же результат, как и объединение строк.
(userCount, ) — кортеж с одним элементом. Да, его синтаксис выглядит немного странно, но для этого есть весомая причина: только так можно однозначно определить кортеж. На самом деле, вы всегда можете ставить запятую после последнего элемента в определении списка, кортежа или словаря, но запятая необходима, если вы определяете кортеж из одного элемента. Если бы запятая не была обязательной, интерпретатор не смог бы определить, что имелось ввиду под выражением (userCount) — кортеж с одним элементом или просто значение userCount.
Форматирование также работает и с целыми числами при использовании шаблона %d вместо %s.
При попытке сложить строку со значением не строкового типа генерируется исключение. В отличии от форматирования, простое сложение работает, только если все значения уже являются строками.

Дополнительная литература

В описаны . описывает , а также технику их использования, включая .

  12  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



Знакомство с языком Python


Содержание

1.1. 1.2. 1.3. 1.4. 1.5. 1.6. 1.7. 1.8. 1.9. 1.10. 1.11. 1.12. 1.13. 1.14. 1.15.



Кортежи


Кортеж — это неизменяемый список. С момента создания кортеж не может быть изменен никакими способами.

Пример 1.22. Определение кортежей

>>> t = ("a", "b", "mpilgrim", "z", "example")

>>> t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t[0]

'a' >>> t[-1]

'example' >>> t[1:3]

('b', 'mpilgrim')

Кортеж определяется так же, как и список, но элементы перечисляются в круглых скобках вместо квадратных.
Как и в списках, элементы в кортежах имеют определенный порядок. Точно так же нумерация элементов начинается с нуля, то есть первым элементом непустого кортежа всегда является t[0].
Как и для списков, отрицательные индексы позволяют вести отсчет элементов с конца кортежа.
К кортежам, как и к спискам можно применить операцию среза. Обратите внимание, что срез списка — новый список, а срез кортежа — новый кортеж.

Пример 1.23. У кортежей нет методов

>>> t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t.append("new")

Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'append' >>> t.remove("z")

Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'remove' >>> t.index("example")

Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'index' >>> "z" in t

1

Вы не можете добавлять элементы в кортеж. У кортежей нет методов append и extend.
Вы не можете удалять элементы из кортежа. У кортежей нет методов remove и pop.
Вы не можете искать элементы в кортеже с помощью метода index — у кортежей нет такого метода.
Однако, вы можете проверить наличие элемента в кортеже с помощью оператора in.

Так для чего же нужны кортежи?

Работа с кортежами быстрее, чем со списками. Если вы определяете постоянный набор значений, и все, что вы хотите с ним когда-либо делать, это перебирать его элементы, используйте кортеж вместо списка.Помните, я сказал, что в качестве могут выступать числа, строки и объекты “некоторых других типов”? Кортежи могут быть ключами словаря, а списки нет.[2]Как мы скоро увидим, кортежи используются для получения форматированного представления.

Кортеж может быть преобразован в список и наоборот. Встроенная функция tuple воспринимает список в качестве аргумента и возвращает кортеж с теми же самыми элементами, и функция list воспринимает кортеж в качестве аргумента и возвращает список. В результате tuple “замораживает” список, а list его “размораживает”.
<
Дополнительная литература

обучает работе с кортежами и показывает как . показывает как . объясняет как .

Footnotes

[2] На самом деле все несколько сложнее. Ключи словаря должны быть неизменяемыми. Кортежи сами по себе неизменяемы, но, если у вас имеется кортеж списков, то он считается изменяемым и не может быть использован в качесве ключа словаря. Только кортежи чисел, строк и других таких же кортежей могут быть ключами словаря.

  9  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Объединение и разбиение строк


Вы имеете список строк с парами ключ-значение вида key=value

и хотите объединить их в одну строку. Для этих целей можно воспользоваться методом join строковых объектов.

Пример 1.34. Объединение строк в buildConnectionString

return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

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

сама по себе является объектом, метод join которого вы вызываете.

В любом случае, метод join объединяет элементы списка в одну строку, в которой элементы оказываются разделены точкой с запятой. Совсем не обязательно, чтобы в качестве разделителя выступала точка с запятой, не обязательно, чтобы он был одним символом — разделитель может быть произвольной строкой.

Метод join может работать только со списками строк, он не делает никаких преобразований. Если список содержит один или более нестроковых элементо, будет сгенерировано исключение.

Пример 1.35. Вывод программы odbchelper.py

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> ["%s=%s" % (k, v) for k, v in params.items()] ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> ";".join(["%s=%s" % (k, v) for k, v in params.items()]) server=mpilgrim;uid=sa;database=master;pwd=secret

Именно эту строку возвращает функция buildConnectionString и выводит программа.

Историческая справка. Когда я начал изучать Python, я ожидал, что join будет методом списка, а строка-разделитель — его аргументом. Многие люди думали точно также. До появления версии Python 1.6 строки не имели всех этих полезных методов.
Был отдельный модуль string, содержащий функции для работы со строками. Каждая функция воспринимала строку в качесве первого аргумента. Функции посчитали достаточно важными, чтобы поместить их непосредственно в строковые объекты, что имеет смысл для таких функций, как lower, upper и split. Однако многие программисты обратили внимание на новый метод join, приводя аргументы в пользу того, что он должен быть методом списка или остаться частью старого модуля string (в котором и сейчас содержится много полезных вещей). Сам я использую новый метод join, но вы можете увидеть код написанный и по-другому. Если это вас беспокоит, вы можете использовать вместо него старую функцию string.join.

Вы, наверное, зададите вопрос, есть ли аналогичный метод для разбиения строки, возвращающий список подстрок? Да, конечно, и называется он split.

Пример 1.36. Разбиение строк

>>> li = ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s = ";".join(li) >>> s 'server=mpilgrim;uid=sa;database=master;pwd=secret' >>> s.split(";")



['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s.split(";", 1)


['server=mpilgrim', 'uid=sa;database=master;pwd=secret']
Метод split выполняет действия, обратные методу join, разбивая строку на подстроки. Обратите внимание, что сам разделитель (“;”) не содержится в результате.
Метод split воспринимает второй необязательный аргумент, указывающий максимальное число разбиений. (“О-о-о, необязательные аргументы…” Вы узнаете, как определить функцию с необязательными аргументами, в следующей главе.)
Вызов anystring.split(delimiter, 1) может быть полезен, если вы хотите найти первое вхождение подстроки и, затем, работать с фрагментами до этой подстроки (он является первым элементом возвращаемого списка) и после него (второй элемент возвращаемого списка).
Дополнительная литература

отвечает на и содержит множество . описывает . содержит информацию о . объяняет, , а не списков.
  14  
<

Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Объявление функций


В языке Python, как и в большинстве других языков программирования, есть функции, но в нем нет отдельных заголовочных файлов, как в C++, или разделов интерфейс/реализация, как в языке Pascal. Если вам нужна функция — просто определите ее.

Пример 1.3. Определение функции buildConnectionString

def buildConnectionString(params):

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

Во-вторых, вы не определяете тип возвращаемого значения. В языке Python никогда не указывается не только тип возвращаемого значения, но даже его наличие. На самом деле каждая функция возвращает значение; если функция выполняет инструкцию return, она возвращает указанное в ней значени, иначе функция возвращает специальное значение — None.

В языке Visual Basic определение функций (возвращающих значение) начинается с ключевого слова function, а подпрограмм (не возвращающих значение) — с sub. В языке Python нет подпрограмм. То, что называется подпрограммой в других языках, в языке Python является функцией. Любая функция возвращает значение, даже если это None, и определение любой функции начинается с def.

В третьих, не указан тип аргумента params. В языке Python тип переменных никогда не указывается явно. Python отслеживает тип объектов во время выполнения программы.

В Java, C++ и других языках со статической типизацией вы должны указывать тип возвращаемого значения и аргументов функции. В языке Python вы никогда не указываете тип. Тип переменной определяется, когда вы присваиваете ей значение.

Дополнение. Эрудированный читатель прислал мне следуещее объяснение сравнения Python с другими языками программирования:

Язык со статической типизациейЯзык, в котором тип (переменной, возвращаемого значения) должен быть указан, например Java или C.Язык с динамической типизациейЯзык, в котором тип определяется во время выполнения.
VBScript и Python являются языками с динамической типизацией, так как они определяют тип переменной, когда ей присваивается значение.Язык со строгой типизациейЯзык, в котором тип всегда четко определен, например Java или Python. Например, когда целое число не можете быть вами использовано в качестве строки без явного преобразования (мы опишем, как это сделать ниже).Язык со слабой типизациейЯзык, в котороом типы можно игнорировать. Например в VBScript вы можете объединить строку '12' с целым числом 3 для получения строки '123', которую затем будете использовать в качестве целого числа 123, и все это без явных преобразований.

Таким образом Python является языком со строгой

(тип переменной имеет значение) динамической (не используется объявление типа) типизацией.

  2  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Обработка списков


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

Пример 1.30. Введение в расширенную запись списков

>>> li = [1, 9, 8, 4] >>> [elem*2 for elem in li]

[2, 18, 16, 8] >>> li

[1, 9, 8, 4] >>> li = [elem*2 for elem in li]

>>> li [2, 18, 16, 8]

Чтобы понять такую запись, читайте ее справа налево. li — список, который вы преобразуете. Python пробегает по всем элементам li, временно присваивает каждый из них переменной elem, вычисляет значение выражения elem*2 и добавляет в конец списка, который вы в результате получаете.
Обратите внимание, что при использовании расширенной записи списка исходный список не изменяется.
Присваивание результата переменной, в которой хранится обрабатываемый список, безопасно, так как новый список создается в памяти и, когда вычисление выражения закончено, он присваивается переменной.

Пример 1.31. Расширенная запись списка в buildConnectionString

["%s=%s" % (k, v) for k, v in params.items()]

Во-первых, обратите внимание на вызов метода items

словаря params. Этот метод возвращает список кортежей, которые представляют записи в словаре.

Пример 1.32. keys, values, and items

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> params.keys()

['server', 'uid', 'database', 'pwd'] >>> params.values()

['mpilgrim', 'sa', 'master', 'secret'] >>> params.items()

[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]

Метод keys словаря возвращает список всех ключей. Порядок следования ключей в этом списке может не соответствовать порядку, в котором определялись записи словаря (помните, записи в словаре не упорядочены).
Метод values возвращает список всех значений. Значения в этом списке идут в таком же порядке, как и ключи в списке, возвращаемом методом keys, то есть params.values()[n] == params[params.keys()[n]] для всех n.
Метод items возвращает список всех записей словаря в виде кортежа (key, value).


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

Пример 1.33. Расширенная запись списка в buildConnectionString, шаг за шагом

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> params.items() [('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')] >>> [k for k, v in params.items()]


['server', 'uid', 'database', 'pwd'] >>> [v for k, v in params.items()]


['mpilgrim', 'sa', 'master', 'secret'] >>> ["%s=%s" % (k, v) for k, v in params.items()]


['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
Обратите внимание, что мы используем две переменные при переборе элементов списка params.items(). Это — еще один пример использования . Первым элементом списка params.items()

является кортеж ('server', 'mpilgrim'), поэтому для первой итерации k получит значение 'server' и v получит значение 'mpilgrim'. В данном случае мы игнорируем v и включаем в результат только k, так что в результате мы получим список, эквивалентный params.keys().
Здесь мы делаем то же самое, но игнорируем k, так что в результате мы получим список, эквивалентный params.values().
Комбинируя эти два примера с простым , мы получаем список строк, содержащих ключ и значение для каждой записи словаря. Результат получается подозрительно похожим на программы, все что осталось сделать — это объединить все элементы списка в одну строку.
Дополнительная литература

описывает другой способ преобразования элементов списка — . описывает, как использовать .
  13  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Определение переменных


После того, как вы познакомились со словарями, кортежами и списками, давайте вернемся к нашему примеру программы — odbchelper.py.

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

Пример 1.24. Определение переменной myParams

if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret"}

Здесь есть несколько интересных моментов. Во-первых, обратите внимание на отступы. Инструкция if является составной и ее тело должно вводиться с отступом, как и тело функции.

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

Если инструкция разбита на несколько строк с использованием признака наличия продолжения (“\”), строки продолжения могут иметь произвольный отступ. В этом случае строгие правила использования отступов не действуют. Если ваш Python IDE автоматически выставляет отступы для строк продолжения, следует, по-видимому, принять их в качестве умолчания, если у вас нет весомой причины от этого отказаться.
Строго говоря, выражения в круглых, квадратных или фигурных скобках (например, ) могут быть разбиты на несколько строк и без использования явного признака наличия продолжения (“\”). Я предпочитаю использовать обратный слэш даже если в нем нет необходимости, так как, на мой взгляд, это облегчает чтение кода. Но это уже дело вкуса.

В третьих, вы нигде не объявляли переменную myParams, а просто присвоили ей значение. Это соответствует поведению VBScript без опции option explicit. К счастью, в отличии от VBScript, Python не позволит ссылаться на переменную, которой нигде не было присвоено значение — при попытке это сделать будет сгенерировано исключение.

Пример 1.25. Использование несуществующей переменной

>>> x Traceback (innermost last): File "<interactive input>", line 1, in ? NameError: There is no variable named 'x' >>> x = 1 >>> x 1

В один прекраснй день вы будете благодарны языку Python за такое поведение.

Дополнительная литература

приводит примеры, и .

  10  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



Отступы


В определении функций нет явных begin и end, нет фигурных скобок, обозначающих начало и конец тела функции. Единственным разделителем является двоеточие (“:”) плюс отсуп кода в теле функции.

Пример 1.6. Отступы в определении функции buildConnectionString

def buildConnectionString(params): """Создает и возвращает строку соединения из словаря параметров.""" return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

Блоки кода (функции, инструкций if, for и др.) определяются отступами. Увеличение отступа обозначает начало блока и уменьшение — его конец. Никакие дополнительные скобки или ключевые слова для этих целей не используются. Таким образом отступы являются значимыми и должны быть согласованными. В приведенном примере тело функции (включая строку документации) набрано с отступом в четыре пробела. Число пробелов в отступе не обязательно должно быть равно четырем, но должно быть везде одинаковым. Первая строка, без отступа, находится за пределами тела функции.

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

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

Дополнительная литература

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

  5  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



Присваивание сразу нескольких значений


Одна из приятных возможностей языка Python — использование последовательностей для односременного присваивания нескольких значений.

Пример 1.26. Присваивание сразу нескольких значений

>>> v = ('a', 'b', 'e') >>> (x, y, z) = v

>>> x 'a' >>> y 'b' >>> z 'e'

v — кортеж из трех элементов и (x, y, z) — кортеж из трех элементов. Присваивание одного другому приводит к присваиванию каждого значения из v

соответствующей переменной.

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

Пример 1.27. Присваивание идущих подряд значений

>>> range(7)

[0, 1, 2, 3, 4, 5, 6] >>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)

>>> MONDAY

0 >>> TUESDAY 1 >>> SUNDAY 6

Встроенная функция range возвращает список целых чисел. В простейшем случае она воспринимает в качестве аргумента верхний предел и возвращает список целых чисел от нуля до обозначенного придела, но не включая предельное значение. При желании вы можете указать нижний предел отличный от нуля и шаг отличный от еденицы. Выполните инструкцию print range.__doc__ для получения более подробной информации.
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY и SUNDAY — переменные, которые мы определяем. Этот пример взят из модуля calendar, печатающего календари, аналогично программе cal в UNIX. Этот модуль определяет целочисленные константы для дней недели.
Теперь каждая переменная имеет свое значение: MONDAY

равна 0, TUESDAY — единице, и т. д.

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

Дополнительная литература

В показано, как использовать множественное присваивание для .

  11  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



Словари


Сделаем небольшое отступление, так как вам необходимо познакомиться со словарями, кортежами и списками. Если вы знаток Perl, то уже имеете некоторое представление о словарях и списках, но вам, тем не менее, необходимо обратить внимание на кортежи.

Один из втроенных типов языка Python, словарь, сопоставляет ключам значения.

Словари в Python аналогичны хешам в Perl. В Perl имена переменных, хранящих хеши, всегда начинаются с символа %. Переменные в языке Python могут иметь произвольное имя, интерпретатор сам определяет их тип.
Словари в Python схожи с экземплярами класса Hashtable в Java.
Словари Python схожи с экземплярами объекта Scripting.Dictionary в Visual Basic.

Пример 1.9. Определени словарей

>>> d = {"server":"mpilgrim", "database":"master"}

>>> d {'server': 'mpilgrim', 'database': 'master'} >>> d["server"]

'mpilgrim' >>> d["database"]

'master' >>> d["mpilgrim"]

Traceback (innermost last): File "<interactive input>", line 1, in ? KeyError: mpilgrim

Сначала мы создаем словарь с двумя записями и присваиваем его переменной d. Каждая запись является парой ключ-значение, весь набор записей перечисляется в фигурных скобках.
"server" является ключом, а ассоциированное с ним значение, d["server"], равно "mpilgrim".
"database" является ключом, а ассоциированное с ним значение, d["database"], равно "master".
Вы можете определить значение по ключу, но не можете определить ключи по значению. Так, выражение d["server"] дает "mpilgrim", но d["mpilgrim"] генерирует исключение, так как словарь не содержит записи с ключом "mpilgrim".

Пример 1.10. Изменение словарей

>>> d {'server': 'mpilgrim', 'database': 'master'} >>> d["database"] = "pubs"

>>> d {'server': 'mpilgrim', 'database': 'pubs'} >>> d["uid"] = "sa"


>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'}
Ключи в словаре уникальны. Присваивание нового значения с существующим ключом заменяет старое.
В любой момент вы можете добавить новую запись. Синтаксис добавления записи аналогичен синтаксису замены значения в записи с существующим ключом. Возможно, в какой-то момент вы будете думать, что добавляете новую запись, в то время как на самом деле вы только изменяете значение в уже существующей записи.
Обратите внимание, что новая запись (ключ "uid", значение "sa") появилась в середине. В самом деле, то что записи появились в том же порядке, в котором были введены — это случайность.

Словари не поддерживают порядок следования записей. Говорить, что порядок следования записей нарушается, некорректно — они просто не упорядочены. Эта важная особенность возможно будет раздражать, когда вы захотите получить записи в каком-то определенном воспроизводимом порядке (например, в алфавитном порядке ключей). Конечно, существует возможность это сделать, просто такая возможность не встроена в словарь.
Пример 1.11. Смешивание различных типов данных в словарях

>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'} >>> d["retrycount"] = 3


>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d[42] = "douglas"


>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3}
Словари предназначены не только для строк. Значения в словарях могут быть любого типа: строки, числа и даже другие словари. Совсем не обязательно, чтобы все значения в одном словаре были одного типа, по мере необходимости вы можете смешивать объекты различных типов.
На ключи в словарях все же накладываются некоторые ограничения. Они могут быть строками, числами и объектами некоторых других типов (более подробно об этом мы раскажем позже). Вы также можете использовать ключи разного типа в одном словаре.


Пример 1.12. Удаление записей из словарей

>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3} >>> del d[42]


>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d.clear()


>>> d {}
Инструкция del позволяет удалять отдельные записи из словаря по ключу.
Метод clear удаляет все записи из словаря. Обратите внимание, что пустые фигурные скобки определяют словарь, не содержащий ни одной записи.
Пример 1.13. Регистр букв в строках имеет значение

>>> d = {} >>> d["key"] = "value" >>> d["key"] = "other value"


>>> d {'key': 'other value'} >>> d["Key"] = "third value"


>>> d {'Key': 'third value', 'key': 'other value'}
Присваивание значения с существующим ключом просто заменяет старое значение на новое.
В этом случае мы не присваиваем значение с существующим ключом, так как Python различает регистр букв в строках, и строка 'key' не равна 'Key'. Буквы разного регистра могут выглядеть для вас похожими, но для интерпретатора они совершенно разные. Таким образом в данном случае в словарь добавляется новая запись.
Дополнительная литература

обучает работе со словарями и показывает, как . приводит множество .В обсуждается .В описаны .
  7  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Списки


Список являются одним из самых используемых типов данных в языке Python. Если все ваше знакомство со списками ограничивается массивами в Visual Basic или (не дай бог) datastore в Powerbuilder, возмите себя в руки для знакомства со списками в языке Python.

Списки в языке Python похожи на массивы в языке Perl. Имена пееменных, хранящих массивы, в языке Perl всегда начинаются с символа @. Python не накладывает никаких дополнительных ограничений на имя переменных, в которых хранятся списки, интерпретатор сам определяет тип переменной.
Списки в языке Python — нечто большее, чем массивы в Java (хотя вы можете использовать их в таком качестве, если это все, что вам требуется от жизни). Более близкой аналогией будет класс Vector, способный содержать произвольные объекты и динамически увеличиваться при добавлении новых элементов.

Пример 1.14. Определение списков

>>> li = ["a", "b", "mpilgrim", "z", "example"]

>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[0]

'a' >>> li[4]

'example'

Сначала мы определяем список из пяти элементов. Обратите внимание, что исходный порядок элементов сохраняется. Это не случайно, список является упорядоченным множеством элементов, перечисленных в квадратных скобках.
Списки могут быть использованы в качестве массивов. Отсчет элементов всегда ведется от нуля, так что первый элемент непустого списка — li[0].
Последний элемент списка из пяти элементо — li[4], так как отсчет ведется от нуля.

Пример 1.15. Отрицательные индексы

>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[-1]

'example' >>> li[-3]

'mpilgrim'

При использовании отрицательных индексов отсчет ведется с конца списка. Послединий элемент любого непустого списка можно получить, используя выражение li[-1].
Если вас смущают отрицательные индексы, запомните простое правило: li[-n] == li[len(li) - n]. То есть, в нашем случае li[-3] == li[5 - 3] == li[2].

Пример 1.16.
Срез


>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[1:3]



['b', 'mpilgrim'] >>> li[1:-1]


['b', 'mpilgrim', 'z'] >>> li[0:3]


['a', 'b', 'mpilgrim']
Указав через двоеточие два индекса, вы можете получить подмножество элементов списка, называемое “срезом”. Получаемое значение является новым списком, содержащим все элементы исходного списка в том же порядке, начиная с первого индекса (здесь li[1]) до, но не включая, второго индекса (здесь li[3]).
В операции среза также можно использовать отрицательные идексы. Если это поможет, считайте первый индекс соответствующим первому элементу, который вам нужен, и второй — первому элементу, который не нужен. Получаемое значение — все, что между ними находится.
Нумерация элементов начинается с нуля, так что li[0:3]

дает первые три элемента исходного списка.
Пример 1.17. Сокращения в записи среза

>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[:3]


['a', 'b', 'mpilgrim'] >>> li[3:]


['z', 'example'] >>> li[:]


['a', 'b', 'mpilgrim', 'z', 'example']
Если первый индекс среза равен нулю, то его можно опустить. Аналогично можно опустить второй индекс, если он равен длине списка. То есть li[:3] дает такой же результат, как и li[0:3] в предыдущем примере.
Обратите внимание на симметрию. Для данного списка из пяти элементов li[:3] дает первые три элемента и li[3:]

— последние два. В самом деле, li[:n] всегда дает первые n элементов, а li[n:] — все остальное.
Если опущены оба индекса, будут включены все элемента исходного списка. Но это не тот же список, это новый список с теми же элементами. Таким образом, li[:] позволяет создать копию списка.
Пример 1.18. Добавление элементов в список

>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li.append("new")


>>> li ['a', 'b', 'mpilgrim', 'z', 'example', 'new'] >>> li.insert(2, "new")


>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new'] >>> li.extend(["two", "elements"])




>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
Метод append добавляет один элемент в конец списка.
Метод insert вставляет один элемент в список. Целочисленный аргумент является индексом первого элемента, позиция которого изменится. Обратите внимание, что элементы списка могут быть не уникальными — после этой операции в списке содержится два элемента со значением "new", li[2] и li[6].
Метод extend добавляет в конец элементы другого списка. В данном случае второй список содержит два элемента.
Пример 1.19. Поиск в списке

>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.index("example")


5 >>> li.index("new")


2 >>> li.index("c")


Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.index(x): x not in list >>> "c" in li


0
Метод index находит первое вхождение элемента в списке и возвращает его индекс.
index позволяет найти только первое вхождение элемента. В нашем списке строка "new" присутствует дважды (li[2] и li[6]), но метод index вернет индекс только первого — 2.
Если указанный элемент в списке не найден, генерируется исключение. Такое поведение заметно отличается от поведения аналогичных средств в других языках, возвращающих какой-либо некорректный индекс. Генерация исключения более удобна, так как работа программы останавливается в месте возникновения ошибки, а не в момент использования некорректного индекса.
Для проверки наличия элемента в списке используйте оператор in, возвращающий 1, если значение найдено, и 0, если в списке такого значения нет.
Python не имеет отдельного булева типа. В булевом контексте (например, в условии инструкции if), 0 является ложью, а все остальные сисла являются истиной. Аналогично и для других типов: пустая строка (""), список ([]) и словарь ({}) являются ложью, а все остальные строки, списки и словари — истиной.


Пример 1.20. Удаление элементов из списка

>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.remove("z")


>>> li ['a', 'b', 'new', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("new")


>>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("c")


Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.remove(x): x not in list >>> li.pop()


'elements' >>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two']
Метод remove удаляет из списка первый элемент с указанным значением.
remove удаляет только один элемент. В данном случае строка "new" присутствует в списке дважды, но li.remove("new") удалит только первую.
Если элемент с указанным значением в списке не найден, remove, как и index, генерирует исключение.
Метод pop выполняет сразу два действия: удаляет последний элемент из списка и возвращает его. Этим он отличается от li[-1], возвращающего последний элемент, но не изменяющего список, и li.remove(value), изменяющего список, но не возвращающего элемент.
Пример 1.21. Применение операторов к спискам

>>> li = ['a', 'b', 'mpilgrim'] >>> li = li + ['example', 'new']


>>> li ['a', 'b', 'mpilgrim', 'example', 'new'] >>> li += ['two']


>>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two'] >>> li = [1, 2] * 3


>>> li [1, 2, 1, 2, 1, 2]
С помощью оператора + можно “склеивать” списки. list = list + otherlist эквивалентно list.extend(otherlist), но при этом создается новый список, в то время как extend изменяет существующий.
Python поддерживает операцию +=. li += ['two'] полностью эквивалентно li.extend(['two']). Операция += работает для списков, строк, чисел и может быть переопределена для классов (более подробно о классах читайте в главе 3).
Оператор * размножает элементы списка. li = [1, 2] * 3 эквивалентно li = [1, 2] + [1, 2] + [1, 2].
Дополнительная литература

обучает работе со списками и делает важные замечания по поводу . показывает, как . отвечает на и содержит множество . описывает .
  8  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

Тестирование модулей


Модули в языке Python также являются объектами и имеют несколько полезных атрибутов. Вы можете использовать их, например, для тестирования.

Пример 1.7. Прием с if __name__

if __name__ == "__main__":

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

Аналогично C, Python использует == для проверки равенства и = для присваивания. Но, в отличии от C, Python не поддерживает присваивание внутри выражения, поэтому исключена возможность случайного присваивания значения, когда имеется ввиду проверка на равенство.

Так что же делает этот прием с if? Модули являются объектами и имеют атрибут __name__. Значение этого атрибута зависит от того, как используется модуль. Если вы импортируете модуль, атрибут __name__ равен имени файла без каталога и расширения. Но если вы запускаете модуль как отдельную программу, __name__ будет иметь специальное значение — __main__.

Пример 1.8. __name__ импортированного модуля

>>> import odbchelper >>> odbchelper.__name__

'odbchelper'

Зная об этом, вы можете определить код для тестирования модуля непосредственно в самом модуле. При непосредственном запуске модуля __name__

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

Для того, чтобы данный прием работал в MacPython, необходимо выполнить дополнительные шаги. Откройте меню опций модуля, нажав на черный треугольник в правом верхнем углу, и убедитесь, что отмечен пункт Run as __main__.

Дополнительная литература

подробно описывает, что происходит при .

  6  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



В глубь


Здесь представлена полноценная программа на языке Python.

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

Пример 1.1. odbchelper.py

Если вы еще этого не сделали, можете , используемые в книге.

def buildConnectionString(params): """Создает и возвращает строку соединения из словаря параметров.""" return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret"} print buildConnectionString(myParams)

Теперь запутите программу и посмотрите, что произойдет.

В Python IDE под Windows вы можете запустить программу из меню File->Run... (Ctrl-R). Вывод программы отображается в интерактивном окне.
В Python IDE on Mac OS вы также можете запустить программу из меню Python->Run window... (Cmd-R), но сначала необходимо выставить одну важную опцию. Откройте модуль в IDE, откройте меню модуля, нажав на черный треугольник в верхнем правом углу окна модуля, и убедитесь, что установлена опция “Run as __main__”. Эта опция сохраняется вместе с модулем, так что ее необходимо установить для каждого модуля только один раз.
В UNIX-совместимых система (включая Mac OS X) вы можете запустить программу из командной строки: python odbchelper.py

Пример 1.2. Вывод odbchelper.py

server=mpilgrim;uid=sa;database=master;pwd=secret

 1  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,



Все является объектами


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

Функция, как и все остальное в языке Python, является объектом.

Пример 1.5. Доступ к строке документации функции buildConnectionString

>>> import odbchelper

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> print odbchelper.buildConnectionString(params)

server=mpilgrim;uid=sa;database=master;pwd=secret >>> print odbchelper.buildConnectionString.__doc__

Создает и возвращает строку соединения из словаря параметров.

В первой строке импортируется odbchelper в качестве модуля. После того, как модуль проимпортирован, вы можете ссылаться на его функции, классы или атрибуты. Модули также могут импортировать другие модули для доступа к их функциональности, и вы можете импортировать модули из IDE. Это очень важная концепция, и мы поговорим о ней позже.
Для использования функции из импортированного модуля вы должны указать имя этого модуля. То есть, вместо buildConnectionString необходимо написать odbchelper.buildConnectionString. Если вы использовали классы в Java, то заметили отдаленное сходство с ними.
Вместо привычного вызова функции мы запрашиваем один из ее атрибутов — __doc__.
import в Python работает аналогично require в Perl. Проимпортировав в Python один раз модуль с помощью инструкции import вы можете обращаться к его функциям в виде module.function; проимпортировав модуль с помощью инструкции require в Perl, вы можете обращаться к его функциям в виде module::function.

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

Это настолько важно, что я повторю еще раз: в языке Python все является объектами. Строки являются объектами.
Списки являются объектами. Функции являются объектами. И даже модули, как мы скоро увидим, являются объектами.

Дополнительная литература

объясняет, что значит "". резюмирует .

Footnotes

[1] Различные языки определяют понятие “объект” по-разному. В некоторых языках все

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

  4  
Copyright © 2000, 2001, 2002

Copyright © 2001, 2002, 2003 Перевод,

и возвращает строку соединения из


Теперь программа odbchelper.py и ее вывод приобретают смысл.
Пример 1.37. odbchelper.py
def buildConnectionString(params): """Создает и возвращает строку соединения из словаря параметров.""" return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
if __name__ == "__main__": myParams = {"server":"mpilgrim", \ "database":"master", \ "uid":"sa", \ "pwd":"secret"} print buildConnectionString(myParams)
Пример 1.38. Вывод программы odbchelper.py
server=mpilgrim;uid=sa;database=master;pwd=secret
Перед тем, как погрузиться в чтение второй главы, убедитесь, что вы овладели следующими навыками:
Использование Python IDE в интерактивном режиме для проверки выраженийНаписание модуля Python таким образом, чтобы он мог быть запущен , хотябы с целью тестирования
и вызов его функций, использование и , Определение , и Доступ к атрибутам и методам , включая строки, списки, словари, функции и модулиПолучение с помощью расширенной записи списков строк на подстроки и их объединение
  15 
Copyright © 2000, 2001, 2002
Copyright © 2001, 2002, 2003 Перевод,