Часто задаваемые вопросы о библиотеке и расширении

Содержание

Общие библиотечные вопросы

Как мне найти модуль или приложение для выполнения задачи X?

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

Для сторонних пакетов выполните поиск в Пакетном индексе Python или попробуйте Google или другую веб-поисковую систему. Поиск «Python» плюс ключевой или два для интересующей вас темы, как правило, найдет что-то полезное.

Где находится исходный файл math.py (socket.py, regex.py и т.д.)?

Если исходный файл для модуля не найден, он может быть встроенным или динамически загруженным модулем, реализованным на языке C, C++ или другом компилируемом языке. В этом случае исходный файл может отсутствовать, или это может быть что-то вроде mathmodule.c, где-то в исходном каталоге C (не в Python каталоге).

Существуют (по крайней мере) три вида модулей в Python:

  1. модули, написанные на Python (.py);

  2. модули, написанные на языке C и динамически загруженные (.dll, .pyd, .so, .sl и т.д.);

  3. модули, написанные на языке C и слинкованные с интерпретатором; чтобы получить их список, введите:

    import sys
    print(sys.builtin_module_names)
    

Как создать исполняемый файл сценария Python в Unix?

Нужно сделать две вещи: режим файла сценария должен быть исполняемым, а первая строка должна начинаться с #!, за которым следует путь к Python интерпретатору.

Первое делается путем выполнения chmod +x scriptfile или, возможно, chmod 755 scriptfile.

Второе можно сделать несколькими способами. Самый простой способ - написать:

#!/usr/local/bin/python

в качестве самой первой строки файла, используя путь к месту установки Python интерпретатора на вашей платформе.

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

#!/usr/bin/env python

Не делайте этого для сценариев CGI. Переменная PATH для сценариев CGI часто очень минимальна, поэтому необходимо использовать фактическое абсолютное имя пути интерпретатора.

Иногда среда пользователя настолько заполнена, что происходит сбой /usr/bin/env программы; или вообще нет программы env. В таком случае можно попробовать следующий хак (из-за Алекса Резинского):

#! /bin/sh
""":"
exec python $0 ${1+"$@"}
"""

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

__doc__ = """...Whatever..."""

Существует ли пакет curses/termcap для Python?

Для вариантов Unix: стандартный исходный дистрибутив Python поставляется с модулем curses в подкаталоге Modules, хотя он не скомпилирован по умолчанию. (Обратите внимание, что это недоступно в дистрибутиве Windows - нет модуля curses для Windows.)

Модуль curses поддерживает основные функции curses, а также множество дополнительных функций от ncurses и SYSV curses, например, цвет, поддержка альтернативных наборов символов, пады и поддержка мыши. Это означает, что модуль не совместим с операционными системами, имеющими только curses BSD, но, по-видимому, в настоящее время не поддерживается ни одна ОС, подпадающая под эту категорию.

Для Windows: используйте модуль consolelib.

Существует ли C эквивалент onexit() в Python?

Модуль atexit обеспечивает функцию регистра, аналогичную onexit() C.

Почему не работают мои обработчики сигналов?

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

handler(signum, frame)

поэтому он должен быть объявлен с двумя аргументами:

def handler(signum, frame):
    ...

Общие задачи

Как мне протестировать программу или компонент Python?

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

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

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

«Глобальная основная логика» вашей программы может быть так же проста, как:

if __name__ == "__main__":
    main_logic()

в нижней части основного модуля программы.

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

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

if __name__ == "__main__":
    self_test()

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

Как создать документацию из докстринга?

Модуль pydoc может создавать HTML на основе докстрингов в исходном коде Python. Альтернативой для создания документации API исключительно из докстрингов является epydoc. Sphinx также может включать содержимое докстринг.

Как получить нажатие одной клавиши за раз?

Для вариантов Unix существует несколько решений. Это просто сделать с помощью curses, но curses довольно большой модуль для изучения.

Потоки

Как программировать с использованием потоков?

Обязательно используйте модуль threading, а не модуль _thread. Модуль threading создаёт удобные абстракции поверх низкоуровневых примитивов, предоставляемых модулем _thread.

У Aahz есть набор слайдов из своего учебного пособия по многопоточности, которые полезны; см. сайт.

Кажется, ни один из моих потоков не запущен: почему?

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

Простое исправление - добавить sleep в конец программы, который достаточно продолжительный, чтобы все потоки завершились:

import threading, time

def thread_task(name, n):
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)  # <---------------------------!

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

Простое исправление - добавить крошечный sleep к началу выполнения функции:

def thread_task(name, n):
    time.sleep(0.001)  # <--------------------!
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)

Вместо того чтобы пытаться угадать хорошее значение задержки для time.sleep(), лучше использовать какой-то семафорный механизм. Одна идея состоит в том, чтобы использовать модуль queue для создания объекта очереди, позволить каждому потоку добавлять маркер в очередь по завершении, и позволить главному потоку считывать столько маркеров из очереди, сколько есть потоков.

Как распределить работу между кучей работающих потоков?

Самый простой способ - использовать новый модуль concurrent.futures, особенно класс ThreadPoolExecutor.

Или, если вам нужен точный контроль над алгоритмом диспетчеризации, вы можете написать собственную логику вручную. Используйте модуль queue для создания очереди, содержащей список заданий. Класс Queue поддерживает список объектов, и имеет метод .put(obj), который добавляет элементы в очередь, и метод .get() для их возвращения. Класс позаботится о блокировке, необходимой для того, чтобы каждое задание было разослано ровно один раз.

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

import threading, queue, time

# Рабочий поток выводит задания из очереди. Когда очередь пуста,
# предполагается, что больше не будет работы и выходит.
# (Реально воркеры будут работать до прекращения.)
def worker():
    print('Running worker')
    time.sleep(0.1)
    while True:
        try:
            arg = q.get(block=False)
        except queue.Empty:
            print('Worker', threading.currentThread(), end=' ')
            print('queue empty')
            break
        else:
            print('Worker', threading.currentThread(), end=' ')
            print('running with argument', arg)
            time.sleep(0.5)

# Создать очередь
q = queue.Queue()

# Стартовать пул из 5 рабочих
for i in range(5):
    t = threading.Thread(target=worker, name='worker %i' % (i+1))
    t.start()

# Начать добавление работы в очередь
for i in range(50):
    q.put(i)

# Дать потокам время для запуска
print('Main thread sleeping')
time.sleep(5)

При выполнении будут получены следующие выходные данные:

Running worker
Running worker
Running worker
Running worker
Running worker
Main thread sleeping
Worker <Thread(worker 1, started 130283832797456)> running with argument 0
Worker <Thread(worker 2, started 130283824404752)> running with argument 1
Worker <Thread(worker 3, started 130283816012048)> running with argument 2
Worker <Thread(worker 4, started 130283807619344)> running with argument 3
Worker <Thread(worker 5, started 130283799226640)> running with argument 4
Worker <Thread(worker 1, started 130283832797456)> running with argument 5
...

Для получения более подробной информации обратитесь к документации модуля; класс Queue обеспечивает функциональный интерфейс.

Какие виды мутаций глобального значения являются потокобезопасными?

Глобальная блокировка интерпретатора (GIL) внутри обеспечивает одновременную работу только одного потока в виртуальной машине Python. В общем случае Python предлагает переключаться между потоками только между операторами байт-кода; периодичность коммутации устанавливается с помощью sys.setswitchinterval(). Каждый оператор байт-кода и таким образом, весь код реализации C, полученный из каждого оператором, поэтому является атомарным с точки зрения программы Python.

Теоретически это означает, что точный учет требует точного понимания реализации байт-кода PVM. На практике это означает, что операции с общими переменные встроенных типов данных (целые числа, списки, словари и т. д.), которые действительно «выглядят атомарными».

Например, следующие операции являются атомарными (L, L1, L2 - списки, D, D1, D2 - словари, x, y - объекты, i, j - ints):

L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()

Это не:

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1

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

Разве мы не можем избавиться от глобальной блокировки интерпретатора?

Глобальная блокировка интерпретатора (GIL) часто рассматривается как препятствие для Python’а развертывания на многопроцессорных серверных машинах высшего класса, поскольку многопоточная программа Python эффективно использует только один ЦП из-за того, что (почти) весь Python код может работать только во время удержания GIL.

Ещё во времена Python 1.5 Грег Стейн фактически реализовал комплексный набор патчей (патчи «свободные потоки»), которые убрали GIL и заменил его мелкозернистой блокировкой. Адам Олсен недавно сделал подобный эксперимент в своем python-safethread проекте. К сожалению, оба эксперимента показали резкое падение производительности одного потока (по меньшей мере, на 30% медленнее) из-за количества мелкозернистых блокировок, необходимых для компенсации удаления GIL.

Это не означает, что нельзя правильно использовать Python на машинах с несколькими ЦП! Просто нужно быть креативным с разделением работы на несколько процессов, а не на несколько потоков. Класс ProcessPoolExecutor в новом модуле concurrent.futures обеспечивает простой способ для этого; модуль multiprocessing обеспечивает более низкий уровень API, если требуется больше контроля над диспетчеризацией задач.

Разумное использование расширений C также поможет; при использовании расширения C для выполнения трудоемкой задачи расширение может освободить GIL, пока поток выполнения находится в коде C, и позволит другим потокам выполнить определенную работу. Некоторые стандартные библиотечные модули, такие как zlib и hashlib, уже делают это.

Было высказано предположение, что GIL должен быть блокировкой для каждого состояния интерпретатора, чем действительно глобально; интерпретаторы тогда не смогли бы обмениваться объектами. К сожалению, это тоже вряд ли произойдет. Это привело бы к взрывообразному объему работы, поскольку многие реализации объектов в настоящее время имеют глобальное состояние. Например, небольшие целые числа и короткие строки кэшируются; эти кэши будут должны быть перемещены в состояние интерпретатора. У других типов объектов есть свой свободный список; эти свободные списки должны быть перемещены в состояние интерпретатора. И так далее.

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

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

Ввод и вывод

Как удалить файл? (И другие вопросы к файлам …)

Использовать os.remove(filename) или os.unlink(filename); для получения документации см. модуль os. Эти две функции идентичны; unlink() является просто именем системного вызова Unix для этой функции.

Чтобы удалить каталог, используйте os.rmdir(); используйте os.mkdir() для его создания. os.makedirs(path) создаст промежуточные каталоги в несуществующих path. os.removedirs(path) удалит промежуточные каталоги, пока они пусты; для удаления всего дерева каталогов и его содержимого используйте shutil.rmtree().

Чтобы переименовать файл, используйте команду os.rename(old_path, new_path).

Чтобы обрезать файл, откройте его с помощью f = open(filename, "rb+") и используйте f.truncate(offset); значение смещения по умолчанию соответствует текущей позиции поиска. Существует также os.ftruncate(fd, offset) для файлов, открытых с помощью os.open(), где fd - это файловый дескриптор (небольшое целое число).

Модуль shutil также содержит ряд функций для работы с файлами, включая copyfile(), copytree() и rmtree().

Как скопировать файл?

Модуль shutil содержит функцию copyfile(). Обратите внимание, что на MacOS 9 он не копирует fork ресурсов и Finder информацию.

Как мне читать (или записывать) двоичные данные?

Для чтения или записи сложных двоичных форматов данных лучше всего использовать модуль struct. Позволяет взять строку, содержащую двоичные данные (обычно числа), и преобразовать ее в Python объекты; и наоборот.

Например, следующий код считывает из файла два 2-байтовых целых числа и одно 4-байтовое целое число в формате прямого порядка байтов (big-endian):

import struct

with open(filename, "rb") as f:
    s = f.read(8)
    x, y, z = struct.unpack(">hhl", s)

«>» в форматной строке принудительно передает данные big-endian; буква «h» читает одно «короткое целое» (2 байта), а «l» читает одно «длинное целое» (4 байта) из строки.

Для данных, которые являются более регулярными (например, однородный список int или floats), можно также использовать модуль array.

Примечание

Для чтения и записи двоичных данных обязательно открывать файл в двоичном режиме (здесь передача "rb" в open()). Если вместо этого используется "r" (по умолчанию), файл будет открыт в текстовом режиме, и f.read() будут возвращены объекты str, а не объекты bytes.

Я не могу использовать os.read() для пайпа, созданного os.popen(); почему?

os.read() - это низкоуровневая функция, которая принимает файловый дескриптор, небольшое целое число, представляющее открытый файл. os.popen() создает файловый объект высокого уровня того же типа, возвращенный встроенной функцией open(). Таким образом, для чтения n байтов из пайпа p, созданного с помощью os.popen(), необходимо использовать p.read(n).

Как получить доступ к последовательному (RS232) порту?

Для Win32, POSIX (Linux, BSD и т.д.), Jython:

Для Unix см. сообщение Usenet Митча Чепмена:

Почему закрытие sys.stdout (stdin, stderr) действительно не закрывает его?

Python файловые объекты представляют собой высокоуровневый уровень абстракции в файле низкоуровневые C дескрипторы.

Для большинства файловых объектов, создаваемых в Python с помощью встроенной функции open(), f.close() помечает файловый объект Python как закрытый с точки зрения Python, а также упорядочивает закрытие базового файлового дескриптора C. Это также происходит автоматически в деструкторе f, когда f становится мусором.

Но stdin, stdout и stderr обрабатываются специально Python, из-за особого статуса, также присвоенного им C. Выполнение sys.stdout.close() помечает файловый объект Python уровня как закрытый, но не закрывает связанный файловый дескриптор C.

Чтобы закрыть базовый файловый дескриптор C для одного из этих трех, необходимо сначала убедиться, что вы действительно хотите это сделать (например, вы можете запутать модули расширения, пытаясь сделать I/O). Если это так, используйте os.close():

os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())

Также можно использовать числовые константы 0, 1 и 2 соответственно.

Сетевое/Интернет-программирование

Какие средства WWW существуют для Python?

См. главы под названием Интернет-протоколы и поддержка и Обработка данных в Интернете в справочном руководстве библиотеки. У Python много модулей, которые помогут вам построить веб-системы на стороне сервера и клиента.

Сводку доступных фреймворков ведет Пол Бодди по адресу.

Кэмерон Лэрд поддерживает полезный набор страниц о Python веб-технологиях.

Как я могу имитировать отправку формы CGI (МЕТОД = POST)?

Я хотел бы получить веб-страницы, которые являются результатом постинга формы. Существуют ли код, которые позволили бы мне сделать это легко?

Да. Вот простой пример использования urllib.request:

#!/usr/local/bin/python

import urllib.request

# построить строку запроса
qs = "First=Josephine&MI=Q&Last=Public"

# подключиться и отправить серверу путь
req = urllib.request.urlopen('http://www.some-server.out-there'
                             '/cgi-bin/some-cgi-script', data=qs)
with req:
    msg, hdrs = req.read(), req.info()

Следует отметить, что в общем случае для процент-кодированной операции POST строки запроса должны цитироваться с помощью urllib.parse.urlencode(). Например, для отправки name=Guy Steele, Jr.:

>>> import urllib.parse
>>> urllib.parse.urlencode({'name': 'Guy Steele, Jr.'})
'name=Guy+Steele%2C+Jr.'

Как отправить почту из Python сценария?

Используйте стандартный библиотечный модуль smtplib.

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

import sys, smtplib

fromaddr = input("From: ")
toaddrs  = input("To: ").split(',')
print("Enter message, end with ^D:")
msg = ''
while True:
    line = sys.stdin.readline()
    if not line:
        break
    msg += line

# Фактическая отправка почты
server = smtplib.SMTP('localhost')
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

Альтернатива Unix использует sendmail. Расположение программы sendmail варьируется между системами; иногда это /usr/lib/sendmail, иногда /usr/sbin/sendmail. Страница руководства по sendmail поможет вам. Вот пример кода:

import os

SENDMAIL = "/usr/sbin/sendmail"  # местоположение sendmail
p = os.popen("%s -t -i" % SENDMAIL, "w")
p.write("To: [email protected]\n")
p.write("Subject: test\n")
p.write("\n")  # пустая строка, отделяющая заголовки от тела
p.write("Some text\n")
p.write("some more text\n")
sts = p.close()
if sts != 0:
    print("Sendmail exit status", sts)

Как избежать блокировки в методе connect() сокета?

Модуль select обычно используется для поддержки асинхронного I/O на сокетах.

Чтобы предотвратить блокировку TCP-соединения, можно перевести сокет в неблокирующий режим. После этого при выполнении connect() можно либо немедленно подключиться (маловероятно), либо получить исключение, содержащее номер ошибки, как .errno. errno.EINPROGRESS указывает, что подключение выполняется, но еще не завершено. Различные ОС будут возвращать разные значения, поэтому вам придется проверить, что возвращается вашей системе.

Во избежание создания исключения можно использовать метод connect_ex(). Он просто возвращает значение errno. Для опроса можно вызвать connect_ex() позже - 0 или errno.EISCONN указать, что вы подключены – или передать это сокету, чтобы проверить, можно ли выполнить запись.

Примечание

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

Базы данных

Есть ли в Python интерфейсы для пакетов баз данных?

Да.

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

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

Как реализовать постоянные объекты в Python?

Модуль библиотеки pickle решает эту проблему очень общим способом (хотя вы все еще не можете хранить такие вещи, как открытые файлы, сокеты или окна), и модуль библиотеки shelve использует pickle и (g)dbm для создания постоянных сопоставлений, содержащих произвольные Python объекты.

Математика и числа

Как создать случайные числа в Python?

Стандартный модуль random реализует генератор случайных чисел. Использование просто:

import random
random.random()

Возвращает случайное число с плавающей запятой в диапазоне [0, 1).

В данном модуле также много других специализированных генераторов, например:

  • randrange(a, b) выбирает целое число в диапазоне [a, b).
  • uniform(a, b) выбирает число с плавающей запятой в диапазоне [a, b).
  • normalvariate(mean, sdev) отсчеты нормального (гауссово) распределения.

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

  • choice(S) выбирает случайный элемент из заданной последовательности
  • shuffle(L) перестановка списка на месте, т. е. перестановка его случайным образом

Существует также класс Random, который можно создать для создания независимых генераторов случайных чисел.