Часто задаваемые вопросы о библиотеках и расширениях
-
- Как удалить файл? (И другие вопросы о файлах…)
- Как скопировать файл?
- Как читать (или записывать) двоичные данные?
- Почему не удаётся использовать os.read() для канала, созданного с помощью os.popen()?
- Как получить доступ к последовательному порту (RS232)?
- Почему закрытие sys.stdout (stdin, stderr) на самом деле его не закрывает?
Общие вопросы о библиотеках
Как найти модуль или приложение для выполнения задачи X?
Проверьте справочник по библиотеке — возможно, там есть подходящий модуль стандартной библиотеки. (Со временем вы узнаете, что входит в стандартную библиотеку, и сможете пропускать этот шаг.)
Чтобы найти сторонние пакеты, выполните поиск в индексе пакетов Python или воспользуйтесь Google либо другой поисковой системой. Обычно достаточно выполнить поиск по слову «Python» и одному-двум ключевым словам, связанным с интересующей вас темой.
Где находится исходный файл math.py (socket.py, regex.py и т. д.)?
Если вы не можете найти исходный файл модуля, возможно, это встроенный или динамически загружаемый модуль, реализованный на C, C++ или другом компилируемом языке. В таком случае у вас может не быть исходного файла, либо он может называться, например, mathmodule.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 вообще нет. В таком случае можно попробовать следующий обходной приём (предложенный Алексом Резинским):
#! /bin/sh
""":"
exec python $0 ${1+"$@"}
"""
Небольшой недостаток заключается в том, что это задаёт строку __doc__ скрипта. Однако это можно исправить, добавив
__doc__ = """...Whatever..."""
Есть ли для Python пакет curses/termcap?
Для вариантов Unix: стандартный дистрибутив исходного кода Python содержит модуль curses в подкаталоге Modules, хотя по умолчанию он не компилируется. (Обратите внимание: в дистрибутиве для Windows он недоступен — модуля curses для Windows нет.)
Модуль curses поддерживает основные возможности curses, а также множество дополнительных функций из ncurses и SYSV curses, таких как цвет, поддержка альтернативного набора символов, окон и мыши. Это означает, что модуль несовместим с операционными системами, в которых есть только BSD curses, однако, похоже, сейчас нет поддерживаемых ОС, подпадающих под эту категорию.
Есть ли в Python аналог C-функции onexit()?
Модуль atexit предоставляет функцию register, аналогичную 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, но этот довольно большой модуль придётся изучить.
Потоки
Как программировать с использованием потоков?
Обязательно используйте модуль 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.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 предоставляет широкий набор возможностей.
Какие виды изменения глобальных значений потокобезопасны?
Внутри Python используется глобальная блокировка интерпретатора (GIL), которая обеспечивает выполнение в виртуальной машине Python только одного потока одновременно. Как правило, Python переключается между потоками только между байткод-инструкциями; частоту переключений можно задать с помощью sys.setswitchinterval(). Поэтому каждая байткод-инструкция и весь вызываемый ею код на C атомарны с точки зрения программы Python.
В теории для точного учёта операций нужно точно понимать реализацию байткода PVM. На практике это означает, что операции с общими переменными встроенных типов данных (int, list, dict и т. д.), которые «выглядят атомарными», действительно являются атомарными.
Например, следующие операции атомарны (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.
После одобрения PEP 703 началась работа по удалению GIL из реализации Python CPython. Сначала эта возможность будет реализована в виде необязательного флага компилятора при сборке интерпретатора, поэтому будут доступны отдельные сборки с GIL и без него. В долгосрочной перспективе предполагается перейти к единой сборке, когда последствия удаления GIL для производительности будут полностью изучены. Вероятно, Python 3.13 станет первым выпуском, содержащим эту работу, хотя в этом выпуске она может быть реализована не полностью.
Текущая работа по удалению GIL основана на форке Python 3.9 с удалённым GIL, созданном Сэмом Гроссом. До этого, во времена Python 1.5, Грег Стайн реализовал обширный набор исправлений («исправления для свободной многопоточности»), удалявший GIL и заменявший его блокировками с мелкой гранулярностью. Адам Олсен провёл аналогичный эксперимент в своём проекте python-safethread. К сожалению, в обоих этих ранних экспериментах наблюдалось резкое снижение производительности в однопоточных сценариях (как минимум на 30%) из-за количества мелкозернистых блокировок, необходимых для компенсации удаления GIL. Форк Python 3.9 — первая попытка удалить GIL с приемлемым влиянием на производительность.
Наличие GIL в текущих выпусках Python не означает, что Python нельзя эффективно использовать на компьютерах с несколькими процессорами! Просто нужно творчески подойти к распределению работы между несколькими процессами, а не потоками. Класс ProcessPoolExecutor из нового модуля concurrent.futures позволяет легко это сделать; модуль multiprocessing предоставляет низкоуровневый API, если вам нужен больший контроль над распределением задач.
Также помогут разумно используемые расширения на C: если такое расширение выполняет длительную задачу, оно может освободить GIL на время выполнения потока в коде C и позволить другим потокам заняться работой. Некоторые модули стандартной библиотеки, например zlib и hashlib, уже делают это.
Ещё один способ уменьшить влияние GIL — сделать блокировку отдельной для каждого состояния интерпретатора, а не по-настоящему глобальной. Это было впервые реализовано в Python 3.12 и доступно в C API. Ожидается, что интерфейс Python для этой возможности появится в Python 3.13. В настоящее время основным ограничением, вероятно, являются сторонние модули расширения: их необходимо писать с учётом нескольких интерпретаторов, чтобы их можно было использовать в такой конфигурации, поэтому многие старые модули расширения окажутся несовместимыми.
Ввод и вывод
Как удалить файл? (И другие вопросы о файлах…)
Используйте 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); по умолчанию offset равен текущей позиции поиска. Для файлов, открытых с помощью os.open(), также существует os.ftruncate(fd, offset), где fd — файловый дескриптор (небольшое целое число).
Модуль shutil также содержит ряд функций для работы с файлами, включая copyfile(), copytree() и rmtree().
Как скопировать файл?
Модуль shutil содержит функцию copyfile(). Обратите внимание: на томах NTFS в Windows она не копирует альтернативные потоки данных, а на томах HFS+ в macOS — ресурсные вилки, хотя сейчас оба механизма используются редко. Также функция не копирует разрешения и метаданные файла, но если вместо неё использовать shutil.copy2(), большая часть этих данных (хотя и не все) будет сохранена.
Как читать (или записывать) двоичные данные?
Для чтения или записи сложных форматов двоичных данных лучше всего использовать модуль struct. Он позволяет преобразовывать строку с двоичными данными (обычно числами) в объекты Python и наоборот.
Например, следующий код считывает из файла два двухбайтовых целых числа и одно четырёхбайтовое целое число в формате с порядком байтов от старшего к младшему:
import struct
with open(filename, "rb") as f:
s = f.read(8)
x, y, z = struct.unpack(">hhl", s)
Символ «>» в строке формата задаёт порядок байтов от старшего к младшему; буква «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:
Для 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 удаляется сборщиком мусора.
Но Python обрабатывает stdin, stdout и stderr особым образом, поскольку в 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.
Какой модуль использовать для создания HTML?
Подборку полезных ссылок можно найти на вики-странице о веб-программировании.
Как отправить почту из скрипта 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 connect не блокировался, можно перевести сокет в неблокирующий режим. Тогда при вызове connect() соединение либо установится немедленно (что маловероятно), либо возникнет исключение, содержащее номер ошибки в виде .errno. errno.EINPROGRESS означает, что подключение выполняется, но ещё не завершено. Разные ОС возвращают разные значения, поэтому нужно проверить, какое значение возвращается в вашей системе.
Чтобы не создавать исключение, можно использовать метод connect_ex(). Он просто возвращает значение errno. Чтобы проверить состояние подключения, можно позднее снова вызвать connect_ex(): значения 0 или errno.EISCONN означают, что соединение установлено. Либо можно передать этот сокет в select.select(), чтобы проверить, доступен ли он для записи.
Базы данных
Есть ли в Python интерфейсы к пакетам для работы с базами данных?
Да.
В стандартную поставку Python также входят интерфейсы к дисковым хеш-таблицам, таким как DBM и GDBM. Кроме того, модуль 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, чтобы получить несколько независимых генераторов случайных чисел.
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/faq/library.html