Spec-Zone.ru › Python 3.13

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

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

    • Как найти модуль или приложение для выполнения задачи X?
    • Где находится исходный файл math.py (socket.py, regex.py и т. д.)?
    • Как сделать скрипт Python исполняемым на Unix?
    • Существует ли пакет curses/termcap для Python?
    • Существует ли эквивалент C-функции onexit() в Python?
    • Почему не работают мои обработчики сигналов?
  • Общие задачи

    • Как протестировать программу или компонент Python?
    • Как создать документацию из строк документации?
    • Как получить один нажатие клавиши за раз?
  • Потоки

    • Как программировать с использованием потоков?
    • Ни один из моих потоков, похоже, не запускается: почему?
    • Как распределить работу между группой потоков-работников?
    • Какие типы изменений глобальных значений являются потокобезопасными?
    • Можно ли избавиться от глобальной блокировки интерпретатора?
  • Ввод и вывод

    • Как удалить файл? (И другие вопросы о файлах…)
    • Как скопировать файл?
    • Как читать (или записывать) двоичные данные?
    • Не могу использовать os.read() для канала, созданного с помощью os.popen(); почему?
    • Как получить доступ к последовательному (RS232) порту?
    • Почему закрытие sys.stdout (stdin, stderr) не приводит к его фактическому закрытию?
  • Программирование сетей/Интернета

    • Какие инструменты WWW доступны для Python?
    • Какой модуль следует использовать для генерации HTML?
    • Как отправить почту из скрипта Python?
    • Как избежать блокировки в методе connect() сокета?
  • Базы данных

    • Существуют ли интерфейсы для пакетов баз данных в Python?
    • Как реализовать сохраняемые объекты в Python?
  • Математика и численные вычисления

    • Как генерировать случайные числа в Python?

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

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

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

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

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

Если вы не можете найти исходный файл для модуля, возможно, это встроенный или динамически загружаемый модуль, реализованный на C, 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 вообще нет. В этом случае вы можете попробовать следующий хак (принадлежащий 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 находит примеры в docstring модуля и выполняет их, сравнивая вывод с ожидаемым выводом, указанным в docstring.

Модуль 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.

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

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

Простое решение — добавить задержку в конце программы, достаточную для завершения всех потоков:

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

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

Внутри используется глобальная блокировка интерпретатора (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.

С одобрением 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-расширений также поможет; если вы используете C-расширение для выполнения трудоемкой задачи, расширение может освободить GIL во время выполнения кода C, чтобы позволить другим потокам выполнить некоторые задачи. Некоторые модули стандартной библиотеки, такие как zlib и hashlib, уже делают это.

Альтернативный подход к уменьшению влияния GIL заключается в том, чтобы сделать 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); смещение по умолчанию равно текущей позиции поиска. Также есть os.ftruncate(fd, offset) для файлов, открытых с помощью os.open(), где fd - дескриптор файла (маленькое целое число).

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

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

Модуль shutil содержит функцию copyfile(). Обратите внимание, что на томах Windows NTFS он не копирует альтернативные потоки данных ни ресурсные вилки на 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 байта) из строки.

Для более регулярных данных (например, однородного списка целых чисел или чисел с плавающей точкой) вы также можете использовать модуль 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:

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.

Какой модуль следует использовать для генерации 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(), вы либо подключаетесь сразу (что маловероятно), либо получаете исключение, содержащее номер ошибки в виде .errno. errno.EINPROGRESS указывает, что соединение выполняется, но ещё не завершено. Разные операционные системы возвращают разные значения, поэтому вам придётся проверить, что возвращает ваша система.

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

Примечание

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

Базы данных

Существуют ли интерфейсы к пакетам баз данных в Python?

Да.

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

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

Как реализовать постоянные объекты в 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/faq/library.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API