Книга: Простой Python. Современный стиль программирования. 2-е изд.
Назад: Глава 10. Ой-ой-ой: объекты и классы
Дальше: Часть II. Python на практике

Глава 11. Модули, пакеты и программы

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

Текст этой книги организован в виде иерархической структуры: слова, предложения, абзацы и главы. Иначе читать его стало бы невозможно уже через пару страниц. Код имеет подобную организацию: типы данных можно сравнить со словами, операторы и выражения — с предложениями, функции можно представить абзацами, а модули — главами. Продолжу аналогию: когда я говорю, что какую-то тему более подробно мы рассмотрим, например, в главе 8, в программировании это была бы отсылка к коду другого модуля.

Модули и оператор import

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

На код других модулей мы ссылаемся с помощью оператора import. Это делает код и переменные импортированного модуля доступными для вашей программы.

Импортируем модуль

Простейший вариант использования оператора import выглядит как importмодуль, где модуль — это имя другого файла Python без расширения .py.

Предположим, вы с друзьями хотите съесть на обед что-то из фастфуда, но не желаете долго выбирать, поскольку все равно последнее слово останется за тем, кто будет громче всех кричать. Пусть выбирает компьютер! Напишем модуль всего с одной функцией, которая будет возвращать случайное блюдо, и основную программу, которая вызовет эту функцию и выведет результат на экран. Модуль (fast.py) показан в примере 11.1.

Пример 11.1. fast.py

from random import choice

 

places = ["McDonalds", "KFC", "Burger King", "Taco Bell",

          "Wendys", "Arbys", "Pizza Hut"]

 

def pick():  # смотрите строку документации

    """Return random fast food place"""

    return choice(places)

А в примере 11.2 показана основная программа, которая его импортирует (назовем ее lunch.py).

Пример 11.2. lunch.py

import fast

 

place = fast.pick()

print("Let's go to", place)

Если вы поместите оба этих файла в один каталог и укажете Python запустить файл lunch.py в качестве основной программы, он обратится к модулю fast и запустит его функцию pick(). Мы написали эту версию функции pick() так, чтобы она возвращала случайный результат из списка строк. Таким образом, основная программа выведет:

$ python lunch.py

Let's go to Burger King

$ python lunch.py

Let's go to Pizza Hut

$ python lunch.py

Let's go to Arbys

Мы использовали импортирование дважды.

Основная программа lunch.py импортировала наш новый модуль fast.

Файл модуля fast.py импортировал функцию choice из стандартного модуля Python random.

Мы также использовали импортирование двумя разными способами.

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

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

Мы могли бы написать модуль fast.py так, как это показано в примере 11.3, импортировав модуль random внутри функции pick(), а не в начале файла.

Пример 11.3. fast2.py

places = ["McDonalds", "KFC", "Burger King", "Taco Bell",

          "Wendys", "Arbys", "Pizza Hut"]

 

def pick():

    import random

    return random.choice(places)

Как и вообще в программировании, лучше выбирать стиль, который кажется вам наиболее понятным. Имя функции со стоящим перед ним именем модуля random.choice использовать безопаснее, однако в таком случае придется набирать немного больше текста.

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

Импортируем модуль с другим именем

В нашей основной программе lunch.py мы вызывали importfast. Но что, если:

у вас есть другой модуль с таким же именем;

• вы хотите использовать более короткое или простое имя;

ваши пальцы прищемило дверью и вы хотите набирать меньше текста?

В такой ситуации можно импортировать, используя псевдоним, как показано в примере 11.4. Используем псевдоним f.

Пример 11.4. fast3.py

import fast as f

place = f.pick()

print("Let's go to", place)

Импортируем только самое необходимое

С помощью Python можно импортировать одну или несколько частей модуля. Со второй ситуацией вы уже имели дело: из модуля random нам нужна была только функция choice().

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

Теперь несколько раз перепишем файл lunch.py. Сначала импортируем функцию pick() из модуля fast с ее исходным именем (пример 11.5).

Пример 11.5. fast4.py

from fast import pick

place = pick()

print("Let's go to", place)

Теперь импортируем ее под именем who_cares (пример 11.6).

Пример 11.6. fast5.py

from fast import pick as who_cares

place = who_cares()

print("Let's go to", place)

Пакеты

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

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

Мы только что написали модуль для выбора места, где можно поесть фастфуд. Давайте добавим похожий модуль для выдачи жизненного совета. Итак, создадим еще одну основную программу с названием questions.py в текущем каталоге. Теперь создадим подкаталог с именем choices и поместим туда два модуля — fast.py и advice.py. Каждый модуль имеет функцию, которая возвращает строку.

Основная программа questions.py будет содержать дополнительное утверждение import и еще одну строку (пример 11.7).

Пример 11.7. questions.py

from sources import fast, advice

 

print("Let's go to", fast.pick())

print("Should we take out?", advice.give())

Утверждение fromsources заставляет Python искать каталог с именем sources, начиная с вашего текущего каталога. Внутри каталога sources он ищет файлы fast.py и advice.py.

Код первого модуля choices/fast.py не изменился, просто файл переместился в каталог choices (пример 11.8).

Пример 11.8. choices/fast.py

from random import choice

 

places = ["McDonalds", "KFC", "Burger King", "Taco Bell",

          "Wendys", "Arbys", "Pizza Hut"]

 

def pick():

    """Return random fast food place"""

    return choice(places)

Второй модуль choices/advice.py новый, но работает практически так же, как и его собрат, посвященный фастфуду (пример 11.9).

Пример 11.9. choices/advice.py

from random import choice

 

answers = ["Yes!", "No!", "Reply hazy", "Sorry, what?"]

 

def give():

    """Return random advice"""

    return choice(answers)

109187.png

Если версия вашего Python ниже, чем 3.3, понадобится еще кое-что сделать в подкаталоге sources, чтобы он стал пакетом Python, — создать файл с именем __init__.py. Это может быть даже пустой файл, но для более старых версий Python его присутствие необходимо, чтобы содержащий его каталог считался пакетом. (Это еще один распространенный вопрос на собеседованиях.)

Запустите основную программу questions.py (из вашего текущего каталога, а не из sources), чтобы увидеть, что произойдет:

$ python questions.py

Let's go to KFC

Should we take out? Yes!

$ python questions.py

Let's go to Wendys

Should we take out? Reply hazy

$ python questions.py

Let's go to McDonalds

Should we take out? Reply hazy

Путь поиска модуля

Я только что сказал, что Python просматривает ваш текущий каталог для поиска подкаталога choices и его модулей. На самом деле он выполняет поиск не только там, и вы можете сами выбирать места поиска нужных файлов. Ранее мы импортировали функцию choice() из модуля стандартной библиотеки random. Его не было в текущем каталоге, поэтому Python надо было искать и в другом месте.

Чтобы узнать, где интерпретатор Python ищет файлы, импортируйте стандартный модуль sys и используйте его список path, в котором содержатся имена каталогов и файлы ZIP-архивов.

Вы можете получить доступ к этому списку и изменить его. Вот так выглядит значение переменной sys.path в Python 3.3 в моей версии операционной системы Mac:

>>> import sys

>>> for place in sys.path:

...     print(place)

...

 

/Library/Frameworks/Python.framework/Versions/3.3/lib/python33.zip

/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3

/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/plat-darwin

/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/lib-dynload

/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/site-packages

Эта начальная пустая строка вывода является пустой строкой '', обозначающей текущий каталог. Если строка '' находится первой в sys.path, Python сначала выполнит поиск в текущем каталоге, когда вы попробуете что-то импортировать: importfast выглядит как fast.py. Python обычно действует именно так. Кроме того, если создать подкаталог sources и поместить туда файлы с кодом, их можно импортировать командами importsources и fromsourcesimportfast.

Использован будет первый найденный модуль. Это означает, что, если вы определите модуль с именем random и он будет найден раньше оригинального модуля, вы не получите доступ к элементу random стандартной библиотеки.

Вы можете изменить путь, по которому производится поиск, внутри вашего кода. Предположим, вы хотите, чтобы Python искал файлы в каталоге /my/modules перед тем, как обратиться к другим местам:

>>> import sys

>>> sys.path.insert(0, "/my/modules")

Относительный и абсолютный импорт

К этому моменту в наших примерах мы импортировали собственные модули из:

текущего каталога;

• подкаталога choices;

стандартной библиотеки Python.

Этот подход хорошо работает до тех пор, пока у вас нет локального модуля с тем же именем, что и у стандартного модуля. Какой именно из них вам нужен?

Python поддерживает абсолютный и относительный импорт. В рассмотренных примерах вы видели абсолютный импорт. Если вы введете команду importrougarou, то для каждого указанного каталога Python будет искать файл с именем rougarou.py (модуль) или каталог с именем rougarou (пакет).

Если файл rougarou.py находится в том же каталоге, что и вызывающая сторона, вы можете импортировать его относительно текущей локации с помощью команды from.importrougarou.

• Если он находится в каталоге более высокого уровня: from..importrougarou.

Если находится в каталоге того же уровня с именем creatures: from..creaturesimportrougarou.

Нотации . и .. были позаимствованы из ОС семейства Unix — там эти сокращения используются для обозначения текущего и родительского каталогов.

Подробнее о проблемах импорта в Python вы можете прочитать в статье Traps for the Unwary in Python’s Import System ().

Пакеты пространств имен

Вы уже знаете, что можно упаковать модули Python как:

единый модуль (файл с расширением .py);

пакет (каталог, содержащий модули и, возможно, другие пакеты).

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

создать новые каталоги для каждой локации на уровень выше пакета critters;

• создать «двоюродные» critters-каталоги ниже этих новых «родителей»;

переместить существующие модули в соответствующие каталоги.

Этот подход нужно проиллюстрировать. Предположим, мы начали работу с такой раскладкой файлов:

critters

⌞ rougarou.py

⌞ wendigo.py

Обычные команды импорта для модулей будут выглядеть следующим образом:

from critters import wendigo, rougarou

Теперь, если мы решим выделить локации north и south, файлы будут размещаться вот так:

north

⌞ critters

   ⌞ wendigo.py

south

⌞ critters

   ⌞ rougarou.py

Если оба каталога, north и south, будут находиться в вашем пути поиска модуля, вы сможете импортировать модули так же, как если бы они все еще находились в пакете, состоящем из одного каталога:

from critters import wendigo, rougarou

Модули против объектов

В каких случаях нужно размещать код в модуле, а в каких — в объекте?

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

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

Это значит, что вы можете сделать следующее:

>>> import math

>>> math.pi

3.141592653589793

>>> math.pi = 3.0

>>> math.pi

3.0

Неужели мы только что испортили вычисления для всех, кто пользуется этим компьютером? Да! На самом деле нет, я шучу. Это действие не повлияло на модуль math. Вы изменили только значение pi для копии модуля math, которая импортируется вашей вызывающей программой, и все улики вашего преступления исчезнут по ее завершении.

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

Достоинства стандартной библиотеки Python

Одно из основных преимуществ Python заключается в том, что у него есть собственный «запас мощности» — большая стандартная библиотека модулей, которые выполняют множество полезных задач и располагаются отдельно друг от друга во избежание разрастания ядра языка. Нередко, когда вы собираетесь писать код, сначала стоит проверить, нет ли уже стандартного модуля, который делает то, что вы хотите. Python также предоставляет авторитетную документацию для модулей наряду с руководством для пользователей (). Сайт Дага Хеллмана Python Module of the Week () и его книга The Python Standard Library by Example («Стандартная библиотека Python в примерах»), выпущенная издательством Addison-Wesley Professional, также являются очень полезными руководствами.

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

Обрабатываем отсутствующие ключи с помощью функций setdefault() и defaultdict()

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

>>> periodic_table = {'Hydrogen': 1, 'Helium': 2}

>>> print(periodic_table)

{'Helium': 2, 'Hydrogen': 1}

Если ключа еще нет в словаре, будет использовано новое значение:

>>> carbon = periodic_table.setdefault('Carbon', 12)

>>> carbon

12

>>> periodic_table

{'Helium': 2, 'Carbon': 12, 'Hydrogen': 1}

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

>>> helium = periodic_table.setdefault('Helium', 947)

>>> helium

2

>>> periodic_table

{'Helium': 2, 'Carbon': 12, 'Hydrogen': 1}

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

>>> from collections import defaultdict

>>> periodic_table = defaultdict(int)

Теперь любое отсутствующее значение будет заменяться целым числом int со значением 0:

>>> periodic_table['Hydrogen'] = 1

>>> periodic_table['Lead']

0

>>> periodic_table

defaultdict(<class 'int'>, {'Lead': 0, 'Hydrogen': 1})

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

>>> from collections import defaultdict

>>>

>>> def no_idea():

...     return 'Huh?'

...

>>> bestiary = defaultdict(no_idea)

>>> bestiary['A'] = 'Abominable Snowman'

>>> bestiary['B'] = 'Basilisk'

>>> bestiary['A']

'Abominable Snowman'

>>> bestiary['B']

'Basilisk'

>>> bestiary['C']

'Huh?'

Вы можете использовать функции int(), list() или dict(), чтобы возвращать пустые значения по умолчанию: int() возвращает 0, list() возвращает пустой список [], dict() возвращает пустой словарь {}. Если вы опустите аргумент, исходное значение нового ключа будет равно None.

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

>>> bestiary = defaultdict(lambda: 'Huh?')

>>> bestiary['E']

'Huh?'

Использовать int — это один из способов создать собственный счетчик:

>>> from collections import defaultdict

>>> food_counter = defaultdict(int)

>>> for food in ['spam', 'spam', 'eggs', 'spam']:

...     food_counter[food] += 1

...

>>> for food, count in food_counter.items():

...     print(food, count)

...

eggs 1

spam 3

Если бы в предыдущем примере food_counter был обычным словарем, а не defaultdict, Python генерировал бы исключение всякий раз при попытке увеличить элемент словаря food_counter[food], ведь тот не инициализирован. Нам понадобилось бы сделать дополнительную работу, как показано здесь:

>>> dict_counter = {}

>>> for food in ['spam', 'spam', 'eggs', 'spam']:

...     if not food in dict_counter:

...    dict_counter[food] = 0

...     dict_counter[food] += 1

...

>>> for food, count in dict_counter.items():

...     print(food, count)

...

spam 3

eggs 1

Подсчитываем элементы с помощью функции Counter()

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

>>> from collections import Counter

>>> breakfast = ['spam', 'spam', 'eggs', 'spam']

>>> breakfast_counter = Counter(breakfast)

>>> breakfast_counter

Counter({'spam': 3, 'eggs': 1})

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

>>> breakfast_counter.most_common()

[('spam', 3), ('eggs', 1)]

>>> breakfast_counter.most_common(1)

[('spam', 3)]

Счетчики можно объединять. Для начала снова взглянем на содержимое breakfast_counter:

>>> breakfast_counter

>>> Counter({'spam': 3, 'eggs': 1})

Создадим новый список с именем lunch и счетчик соответственно с именем lunch_counter:

>>> lunch = ['eggs', 'eggs', 'bacon']

>>> lunch_counter = Counter(lunch)

>>> lunch_counter

Counter({'eggs': 2, 'bacon': 1})

Счетчики можно объединить с помощью оператора +:

>>> breakfast_counter + lunch_counter

Counter({'spam': 3, 'eggs': 3, 'bacon': 1})

Как нетрудно догадаться, счетчики можно вычитать друг из друга с помощью оператора -. Например, «что мы будем есть на завтрак, но не на обед?».

>>> breakfast_counter — lunch_counter

Counter({'spam': 3})

О’кей. А теперь узнаем, что мы можем съесть на обед, но не можем на завтрак:

>>> lunch_counter — breakfast_counter

Counter({'bacon': 1, 'eggs': 1})

По аналогии с множествами, показанными в главе 8, вы можете получить общие элементы с помощью оператора пересечения &:

>>> breakfast_counter & lunch_counter

Counter({'eggs': 1})

В результате пересечения был получен общий элемент 'eggs' с наименьшим количеством. Это имеет смысл: на завтрак было только одно яйцо, поэтому указанное (наименьшее) количество является общим.

Наконец, вы можете получить все элементы с помощью оператора объединения |:

>>> breakfast_counter | lunch_counter

Counter({'spam': 3, 'eggs': 2, 'bacon': 1})

Элемент 'eggs' снова оказался общим для обоих счетчиков. В отличие от сложения объединение не складывает счетчики, а выбирает тот, который имеет наибольшее значение.

Упорядочиваем по ключу с помощью OrderedDict()

Перед вами пример, запущенный с помощью интерпретатора Python 2:

>>> quotes = {

...     'Moe': 'A wise guy, huh?',

...     'Larry': 'Ow!',

...     'Curly': 'Nyuk nyuk!',

...     }

>>> for stooge in quotes:

...  print(stooge)

...

Larry

Curly

Moe

109193.png

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

Функция OrderedDict() запоминает порядок, в котором добавлялись ключи, и возвращает их в том же порядке с помощью итератора. Попробуем создать словарь OrderedDict из последовательности кортежей вида «ключ — значение»:

>>> from collections import OrderedDict

>>> quotes = OrderedDict([

...     ('Moe', 'A wise guy, huh?'),

...     ('Larry', 'Ow!'),

...     ('Curly', 'Nyuk nyuk!'),

...     ])

>>>

>>> for stooge in quotes:

...     print(stooge)

...

Moe

Larry

Curly

Стек + очередь == deque

deque (произносится как «дэк») — это двусторонняя очередь, которая имеет функции как стека, так и очереди. Она полезна, когда нужно добавить или удалить элементы с любого конца последовательности. В следующем примере мы будем двигаться с обоих концов слова к его середине, чтобы проверить, является ли оно палиндромом. Функция popleft() удаляет крайний слева элемент и возвращает его, функция pop() удаляет крайний справа элемент и возвращает его. Вместе они движутся с концов слова к его середине. Эти действия продолжатся до тех пор, пока совпадают крайние символы и пока не будет достигнута середина:

>>> def palindrome(word):

...     from collections import deque

...     dq = deque(word)

...     while len(dq) > 1:

...        if dq.popleft() != dq.pop():

...            return False

...     return True

...

...

>>> palindrome('a')

True

>>> palindrome('racecar')

True

>>> palindrome('')

True

>>> palindrome('radar')

True

>>> palindrome('halibut')

False

Я воспользовался этим примером, чтобы проще проиллюстрировать работу deque. Если же вы действительно хотите создать программу для определения палиндромов, гораздо удобнее сравнивать строку с ее «перевернутой» копией. В Python строковой функции reverse() не существует, но можно записать строку в обратном порядке с помощью слайса, как показано здесь:

>>> def another_palindrome(word):

...     return word == word[::-1]

...

>>> another_palindrome('radar')

True

>>> another_palindrome('halibut')

False

Итерируем по структурам кода с помощью модуля itertools

Модуль itertools () содержит особые функции итератора. Все они возвращают один элемент при каждом вызове из цикла for…in и запоминают свое состояние между вызовами.

Функция chain() проходит по своим аргументам, как если бы они были единым итерабельным объектом:

>>> import itertools

>>> for item in itertools.chain([1, 2], ['a', 'b']):

...     print(item)

...

1

2

a

b

Функция cycle() является бесконечным итератором, проходящим в цикле по своим аргументам:

>>> import itertools

>>> for item in itertools.cycle([1, 2]):

...     print(item)

...

1

2

1

2

.

.

.

…И т.д.

Функция accumulate() подсчитывает накопленные значения. По умолчанию она высчитывает сумму:

>>> import itertools

>>> for item in itertools.accumulate([1, 2, 3, 4]):

...     print(item)

...

1

3

6

10

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

>>> import itertools

>>> def multiply(a, b):

...     return a * b

...

>>> for item in itertools.accumulate([1, 2, 3, 4], multiply):

...     print(item)

...

1

2

6

24

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

Красиво выводим данные на экран с помощью функции pprint()

Во всех наших примерах использовалась функция print() (или просто имя переменной в интерактивном интерпретаторе) для вывода информации на экран. Иногда прочитать результаты бывает трудно. Нам нужен prettyprinter (красивый принтер), такой как pprint():

>>> from pprint import pprint

>>> quotes = OrderedDict([

...     ('Moe', 'A wise guy, huh?'),

...     ('Larry', 'Ow!'),

...     ('Curly', 'Nyuk nyuk!'),

...     ])

>>>

Старая добрая функция print() просто выводит всю информацию:

>>> print(quotes)

OrderedDict([('Moe', 'A wise guy, huh?'), ('Larry', 'Ow!'),

    ('Curly', 'Nyuk nyuk!')])

А функция pprint() пытается выровнять элементы для лучшей читаемости:

>>> pprint(quotes)

{'Moe': 'A wise guy, huh?',

'Larry': 'Ow!',

'Curly': 'Nyuk nyuk!'}

Работаем со случайными числами

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

>>> from random import choice

>>> choice([23, 9, 46, 'bacon', 0x123abc])

1194684

>>> choice( ('a', 'one', 'and-a', 'two') )

'one'

>>> choice(range(100))

68

>>> choice('alphabet')

'l'

Используем функцию sample() для того, чтобы получить больше одного значения за один раз:

>>> from random import sample

>>> sample([23, 9, 46, 'bacon', 0x123abc], 3)

[1194684, 23, 9]

>>> sample(('a', 'one', 'and-a', 'two'), 2)

['two', 'and-a']

>>> sample(range(100), 4)

[54, 82, 10, 78]

>>> sample('alphabet', 7)

['l', 'e', 'a', 't', 'p', 'a', 'b']

Чтобы получить случайное целое число из любого диапазона, можно обратиться к функциям choice() или sample(), передав в них возвращаемое значение функции range(). Можно воспользоваться также функциями randint() или randrange():

>>> from random import randint

>>> randint(38, 74)

71

>>> randint(38, 74)

60

>>> randint(38, 74)

61

Функция randrange(), как и функция range(), имеет аргументы для начального значения (включительно), конечного (не включительно), а также для необязательного целочисленного параметра шага:

>>> from random import randrange

>>> randrange(38, 74)

65

>>> randrange(38, 74, 10)

68

>>> randrange(38, 74, 10)

48

Итак, получим случайное вещественное число (число с плавающей точкой) между 0.0 и 1.0:

>>> from random import random

>>> random()

0.07193393312692198

>>> random()

0.7403243673826271

>>> random()

0.9716517846775018

Нужно больше кода

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

PyPi (известный также как Cheese Shop («Магазин сыра») после старой пародии Monty Python) (/);

• github ();

readthedocs (/).

Небольшие фрагменты кода вы можете найти по адресу /.

Почти везде в этой книге код Python использует функции стандартных библиотек Python. Но кое-где представлены и внешние пакеты: в главе 1 я упоминал requests, а в главе 18 рассматривал его более подробно. В приложении Б показано, как устанавливать стороннее программное обеспечение Python, и дана другая подробная информация.

Читайте далее

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

Упражнения

11.1. Создайте файл с именем zoo.py. В нем объявите функцию hours(), которая выводит на экран строку 'Open9-5daily'. Далее используйте интерактивный интерпретатор, чтобы импортировать модуль zoo и вызвать его функцию hours().

11.2. В интерактивном интерпретаторе импортируйте модуль zoo под именем menagerie и вызовите его функцию hours().

11.3. Оставаясь в интерпретаторе, импортируйте непосредственно функцию hours() из модуля zoo и вызовите ее.

11.4. Импортируйте функцию hours() под именем info и вызовите ее.

11.5. Создайте словарь с именем plain, содержащий пары «ключ — значение» 'a':1, 'b':2 и 'c':3, а затем выведите его на экран.

11.6. Создайте OrderedDict с именем fancy из пар «ключ — значение», приведенных в упражнении 11.5, и выведите его на экран. Изменился ли порядок ключей?

11.7. Создайте defaultdict с именем dict_of_lists и передайте ему аргумент list. Создайте список dict_of_lists['a'] и присоедините к нему значение 'somethingfora' за одну операцию. Выведите на экран dict_of_lists['a'].

Как минимум читать было бы гораздо сложнее.

Или не шучу? Муа-ха-ха!

Назад: Глава 10. Ой-ой-ой: объекты и классы
Дальше: Часть II. Python на практике

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