ЧАВО по библиотекам и расширениям
-
- Как найти модуль или приложение для выполнения задачи X?
- Где находится исходный файл math.py (socket.py, regex.py и т. д.)?
- Как сделать скрипт Python исполняемым на Unix?
- Есть ли пакет curses/termcap для Python?
- Есть ли аналог функции C onexit() в Python?
- Почему мои обработчики сигналов не работают?
-
- Как программировать с использованием потоков?
- Ни один из моих потоков, похоже, не запускается: почему?
- Как распределить работу между несколькими рабочими потоками?
- Какие типы изменения глобальных значений являются потокобезопасными?
- Разве мы не можем избавиться от глобальной блокировки интерпретатора?
-
- Как удалить файл? (И другие вопросы о файлах…)
- Как скопировать файл?
- Как читать (или записывать) двоичные данные?
- Не могу использовать os.read() для канала, созданного с помощью os.popen(); почему?
- Как получить доступ к последовательному (RS232) порту?
- Почему закрытие sys.stdout (stdin, stderr) не закрывает его действительно?
Общие вопросы о библиотеках
Как найти модуль или приложение для выполнения задачи X?
Проверьте справочник по библиотекам, чтобы узнать, есть ли там подходящий стандартный модуль. (В конечном итоге вы узнаете, что содержится в стандартной библиотеке, и сможете пропустить этот шаг.)
Для сторонних пакетов выполните поиск в Python Package Index или попробуйте Google или другой поисковый движок. Поиск по ключевым словам «Python» плюс несколько ключевых слов по интересующей вас теме обычно дает полезные результаты.
Где находится исходный файл math.py (socket.py, regex.py и т. д.)?
Если вы не можете найти исходный файл для модуля, это может быть встроенный или динамически загружаемый модуль, реализованный на C, C++ или другом языке скомпилированным языком. В этом случае у вас может не быть исходного файла, или он может находиться где-то в каталоге с исходным кодом C (не в пути Python).
В Python существует (по крайней мере) три типа модулей:
- модули, написанные на Python (.py);
- модули, написанные на C и динамически загружаемые (.dll, .pyd, .so, .sl и т. д.);
-
модули, написанные на 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 вообще отсутствует. В этом случае вы можете попробовать следующий хак (от Alex Rezinsky):
#! /bin/sh
""":"
exec python $0 ${1+"$@"}
"""
Небольшой недостаток заключается в том, что это определяет строку документации скрипта __doc__. Однако вы можете исправить это, добавив
__doc__ = """...Whatever..."""
Есть ли пакет curses/termcap для Python?
Для вариантов Unix: стандартное распределение исходного кода Python поставляется с модулем curses в подкаталоге Modules, хотя он не компилируется по умолчанию. (Обратите внимание, что это недоступно в дистрибутиве для Windows — модуля curses для Windows нет.)
Модуль curses поддерживает базовые функции curses, а также многие дополнительные функции из ncurses и SYSV curses, такие как цвет, поддержка альтернатного набора символов, области и поддержка мыши. Это означает, что модуль несовместим с операционными системами, в которых есть только BSD curses, но, похоже, в настоящее время нет поддерживаемых ОС, которые попадают в эту категорию.
Есть ли аналог функции C onexit() в Python?
Модуль 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.
Как создать документацию из строк документации?
Модуль pydoc может создавать HTML на основе строк документации в вашем исходном коде Python. Альтернативой для создания документации API только из строк документации является epydoc. Sphinx также может включать содержимое строк документации.
Как получить один нажатие клавиши за раз?
Для вариантов Unix существует несколько решений. Это легко сделать с помощью curses, но curses — это достаточно большой модуль для изучения.
Потоки
Как программировать с использованием потоков?
Не забудьте использовать модуль threading, а не модуль _thread. Модуль threading создает удобные абстракции поверх низкоуровневых примитивов, предоставляемых модулем _thread.
Ни один из моих потоков, похоже, не работает: почему?
Как только основной поток завершается, все потоки убиваются. Ваш основной поток работает слишком быстро, не давая потокам времени на выполнение работы.
Простым решением является добавление задержки в конце программы, достаточной для завершения всех потоков:
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.currentThread(), end=' ')
print('queue empty')
break
else:
print('Worker', threading.currentThread(), 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 предоставляет функциональный интерфейс.
Какие виды глобальных изменений значений безопасны для потоков?
Внутренне используется глобальная блокировка интерпретатора (GIL), чтобы гарантировать, что только один поток работает в виртуальной машине Python за раз. В целом, Python предлагает переключаться между потоками только между инструкциями байткода; частоту переключения можно задать с помощью sys.setswitchinterval(). Каждая инструкция байткода, а следовательно, и весь код реализации на C, достижимый из каждой инструкции, является атомарным с точки зрения программы Python.
Теоретически, для точного учета требуется точное понимание реализации байткода PVM. На практике это означает, что операции со общими переменными встроенных типов данных (целые числа, списки, словари и т. д.), которые «выглядят атомарными», действительно являются таковыми.
Например, следующие операции являются атомарными (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__() этих других объектов, когда их счетчик ссылок достигает нуля, и это может повлиять на ситуацию. Это особенно актуально для массовых обновлений словарей и списков. В случае сомнений используйте мьютекс!
Разве мы не можем избавиться от глобальной блокировки интерпретатора?
Глобальная блокировка интерпретатора (GIL) часто рассматривается как препятствие для развертывания Python на высокопроизводительных многопроцессорных серверных машинах, потому что многопоточная программа Python фактически использует только один процессор из-за того, что (почти) весь код Python может выполняться только при удержании GIL.
В те времена, когда Python был 1.5, Greg Stein фактически реализовал полный набор исправлений (патчи «бесплатной многопоточности»), которые убрали GIL и заменили его на мелкозернистые блокировки. Adam Olsen недавно провел аналогичный эксперимент в своем проекте 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 не копируются вилка ресурсов и информация 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 байта) из строки.
Для более упорядоченных данных (например, однородного списка целых чисел или чисел с плавающей точкой) вы также можете использовать модуль array.
Не могу использовать os.read() на канале, созданном с помощью os.popen(); почему?
os.read() — функция низкого уровня, которая принимает дескриптор файла, малое целое число, представляющее открытый файл. os.popen() создаёт высокоуровневый объект файла, тот же тип, что возвращает встроенная функция open(). Таким образом, чтобы прочитать n байт из канала p, созданного с помощью os.popen(), вам нужно использовать p.read(n).
Как получить доступ к последовательному порту (RS232)?
Для Win32, OSX, Linux, BSD, Jython, IronPython:
https://pypi.org/project/pyserial/
Для Unix, см. сообщение на Usenet от Митча Чапмена:
https://groups.google.com/groups?selm=34A04430.CF9@ohioee.com
Почему закрытие 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 файла для одного из этих трёх, сначала убедитесь, что это действительно то, что вы хотите сделать (например, вы можете спутать модули расширения, пытающиеся выполнять ввод/вывод). Если да, используйте 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 по адресу http://phaseit.net/claird/comp.lang.python/web_python.
Как я могу имитировать отправку формы CGI (METHOD=POST)?
Я хотел бы получить веб-страницы, которые являются результатом отправки формы 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(), чтобы проверить, можно ли с ним писать.
Базы данных
Есть ли какие-либо интерфейсы к пакетам баз данных в Python?
Да.
Интерфейсы к базам данных на диске, таким как DBM и GDBM также включены в стандартный Python. Также есть модуль sqlite3, который предоставляет легкую реляционную базу данных на диске.
Поддержка большинства реляционных баз данных доступна. Подробности см. на странице вики DatabaseProgramming 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, который можно создать для создания независимых нескольких генераторов случайных чисел.
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/faq/library.html