FAQ по библиотеке и расширению

Содержание

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

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

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

Для поиска пакетов сторонних разработчиков воспользуйтесь поиском Python Package Index или попробуйте воспользоваться 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 - для Windows не существует модуля curses).

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

Есть ли в Python эквивалент onexit() языка C?

Модуль atexit предоставляет функцию регистра, которая похожа на функцию языка C onexit().

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

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

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.

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

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

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

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

Нитки

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

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

Ни один из моих потоков не запускается: почему?

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

Простое решение - добавить в конец программы 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)  # <---------------------------!

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

Простым исправлением является добавление крошечного сна в начало функции run:

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

# The worker thread gets jobs off the queue.  When the queue is empty, it
# assumes there will be no more work and exits.
# (Realistically workers will run until terminated.)
def worker():
    print('Running worker')
    time.sleep(0.1)
    while True:
        try:
            arg = q.get(block=False)
        except queue.Empty:
            print('Worker', threading.current_thread(), end=' ')
            print('queue empty')
            break
        else:
            print('Worker', threading.current_thread(), end=' ')
            print('running with argument', arg)
            time.sleep(0.5)

# Create queue
q = queue.Queue()

# Start a pool of 5 workers
for i in range(5):
    t = threading.Thread(target=worker, name='worker %i' % (i+1))
    t.start()

# Begin adding work to the queue
for i in range(50):
    q.put(i)

# Give threads time to run
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 предоставляет функциональный интерфейс.

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

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

В теории это означает, что точный учет требует точного понимания реализации байткода PVM. На практике это означает, что операции над общими переменными встроенных типов данных (ints, lists, dicts, etc), которые «выглядят атомарными», на самом деле таковыми являются.

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

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__() этих объектов, когда количество ссылок на них достигает нуля, и это может повлиять на ситуацию. Это особенно актуально для массовых обновлений словарей и списков. Если есть сомнения, используйте мьютекс!

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

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

Еще во времена Python 1.5 Грег Стайн фактически реализовал комплексный набор патчей («free threading» patches), который удалил 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(). Обратите внимание, что на томах Windows NTFS она не копирует ни alternate data streams, ни resource forks на томах macOS HFS+, хотя обе эти функции сейчас редко используются. Она также не копирует разрешения и метаданные файлов, хотя использование shutil.copy2() вместо этого сохранит большинство (хотя и не все) из них.

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

Для чтения или записи сложных форматов двоичных данных лучше всего использовать модуль 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 байта) из строки.

Для данных, которые являются более регулярными (например, однородный список ints или float), вы также можете использовать модуль 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, OSX, Linux, BSD, Jython, IronPython:

Что касается Unix, см. сообщение в Usenet Митча Чепмена:

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

Python file objects - это высокоуровневый уровень абстракции на низкоуровневых файловых дескрипторах языка C.

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

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

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

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

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

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

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

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

Сводка доступных фреймворков поддерживается Полом Бодди на сайте https://wiki.python.org/moin/WebProgramming.

Камерон Лэрд ведет полезную подборку страниц о веб-технологиях Python на сайте https://web.archive.org/web/20210224183619/http://phaseit.net/claird/comp.lang.python/web_python.

Как я могу имитировать отправку формы CGI (METHOD=POST)?

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

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

#!/usr/local/bin/python

import urllib.request

# build the query string
qs = "First=Josephine&MI=Q&Last=Public"

# connect and send the server a path
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.'

См.также

КАК получить интернет-ресурсы с помощью пакета urllib для получения обширных примеров.

Какой модуль следует использовать для помощи в генерации HTML?

Вы можете найти коллекцию полезных ссылок на сайте Web Programming wiki page.

Как отправить почту из сценария 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

# The actual mail send
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 location
p = os.popen("%s -t -i" % SENDMAIL, "w")
p.write("To: receiver@example.com\n")
p.write("Subject: test\n")
p.write("\n")  # blank line separating headers from body
p.write("Some text\n")
p.write("some more text\n")
sts = p.close()
if sts != 0:
    print("Sendmail exit status", sts)

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

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

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

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

Примечание

Модуль asyncio предоставляет однопоточную и параллельную асинхронную библиотеку общего назначения, которую можно использовать для написания неблокирующего сетевого кода. Библиотека стороннего производителя Twisted является популярной и многофункциональной альтернативой.

Базы данных

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

Да.

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

Имеется поддержка большинства реляционных баз данных. Подробности см. в DatabaseProgramming wiki page.

Как реализовать постоянные объекты в 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, который можно инстанцировать для создания независимых множественных генераторов случайных чисел.

Вернуться на верх