Spec-Zone.ru › Python 3.8

Вопросы и ответы по библиотекам и расширениям

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

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

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

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

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

    • Какие инструменты WWW есть для Python?
    • Как смоделировать отправку CGI формы (METHOD=POST)?
    • Какой модуль следует использовать для генерации 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, но, похоже, в настоящее время нет поддерживаемых ОС, которые попадают в эту категорию.

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

Существует ли аналог 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.

У Ааха есть набор слайдов из его обучающего курса по потокам, которые могут быть полезны; см. http://www.pythoncraft.com/OSCON2001/.

Ни один из моих потоков, похоже, не выполняется: почему?

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

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

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)  # <---------------------------!

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

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

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 Грег Штейн на самом деле реализовал полную наборы исправлений (исправления «бесплатных потоков»), которые удалили GIL и заменили его мелкозернистой блокировкой. Адам Олсен недавно провёл похожий эксперимент в своём проекте python-safethread. К сожалению, оба эксперимента показали резкое падение производительности одного потока (по крайней мере, на 30% медленнее) из-за количества мелкозернистой блокировки, необходимого для компенсации удаления GIL.

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

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

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

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

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

Ввод и вывод

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

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

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

Для переименования файла используйте os.rename(old_path, new_path).

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

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

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

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

Примечание

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

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

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

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

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

http://pyserial.sourceforge.net

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

https://groups.google.com/groups?selm=34A04430.CF9@ohioee.com

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

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

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

Но stdin, stdout и stderr обрабатываются Python особо, из-за специального статуса, присвоенного им C. Выполнение sys.stdout.close() помечает объект файла на уровне Python как закрытым, но не закрывает связанный дескриптор файла 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 (МЕТОД=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, вы можете установить сокет в режим без блокировки. Затем, когда вы выполните connect(), вы либо подключаетесь сразу (маловероятно), либо получаете исключение, которое содержит номер ошибки как .errno. errno.EINPROGRESS указывает, что подключение происходит, но еще не завершено. Разные ОС возвращают разные значения, поэтому вам нужно будет проверить, что возвращает ваша система.

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

Примечание

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

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/faq/library.html

Spec-Zone.ru

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