C++ и Python

Язык Python

Стандартные модули python, часть I

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

Работа с операционной системой

Модуль sys

Модуль sys обеспечивает доступ к параметрам и функциям операционной системы.

Список sys.argv хранит имя запущенного скрипта и аргументы командной строки, переданные при его запуске:

# test.py

import sys
for idx, item in enumerate(sys.argv):
    print(f'Arg {idx}: {item:8} {type(item)}')
user@host:~$ python test.py arg1 arg2 345
Arg 0: test.py  <class 'str'>
Arg 1: arg1     <class 'str'>
Arg 2: arg2     <class 'str'>
Arg 3: 345      <class 'str'>

Переменная sys.executable позволяет узнать какой именно интерпретатор python используется:

print(sys.executable)
# /home/vitaly/miniconda3/envs/tf2/bin/python

Функция sys.exit позволяет завершить выполнение программы. Эта функция принимает один аргумент — код выхода, который по умолчанию равен нулю. Большинство систем будет считать код 0 признаком успешного завершения программы, а любое другое число от 1 до 127 будет считать признаком ненормального завершения. Если передан объект другого типа, то он будет выведен в стандартный поток вывода, а код выхода будет равен 1. Функция sys.exit всегда генерирует исключение SystemExit, поэтому не стоит рассматривать ее как стандартный способ завершения программы. Используйте ее только в подходящих случаях, которые чаще всего связаны с невозможностью продолжения работы программы.

Переменная sys.path обеспечивает доступ к переменной окружения PYTHONPATH. Эта переменная содержит список путей, в которых выполняется поиск модулей. Если необходимый модуль расположен в директории, которая не входит в PYTHONPATH, то перед подключением этого модуля необходимо добавить эту директорию в переменную sys.path:

# 'path/to/my/facorite/module/dir/mymodule.py'
sys.path.append('path/to/my/facorite/module/dir')
import mymodule

Можно указывать абсолютный или относительный путь. Модуль sys имеет еще много инструментов, которые описаны в документации.

Модуль os

Модуль os предоставляет инструменты для работы с операционной системой и файловой системой.

Функции os.getenv и os.putenv позволяют получать и изменять значения переменных окружения. Функция os.system позволяет выполнять консольные команды, запуская при этом дочерний процесс. Рассмотрим следующий скрипт:

# test.py
import os
import sys

print(f'  HOME: {os.getenv("HOME")}')

os.putenv('NEWENV', 'value')
print(f'NEWENV: {os.getenv("NEWENV")}')
if os.getenv('NEWENV') is not None:
    sys.exit(0)
os.system('python test.py')

При работе скрипта можно получить вывод, подобный такому:

HOME: /home/vitaly
NEWENV: None
  HOME: /home/vitaly
NEWENV: value

Разберемся с тем что произошло. Переменная окружения HOME содержит путь к домашней директории пользователя. Мы получили значение этой переменной с помощью os.genenv (в данном случае /home/vitaly) и вывели его в консоль. Затем, c помощью sys.putenv, мы задали значение value новой переменной окружения NEWENV и сразу прочитали его. Функция os.getenv вернула None, поскольку функция sys.putenv оказывает влияние только на окружение дочерних процессов. Чтобы это проверить, мы снова запустили интерпретатор python с нашим скриптом test.py, используя os.system. В дочернем процессе снова были выведены переменные окружения HOME и NEWENV. В дочернем процессе переменная NEWENV определена, поэтому сработало условие для выхода из программы с помощью sys.exit(0).

Функция os.listdir возвращает список названий объектов, лежащий в заданной директории.

Модуль os.path

Модуль os.path содержит полезные инструменты для работы с путями файловой системы. Функция os.path.exists проверяет указывает ли путь на существующий объект в файловой системе. Функция os.path.isfile имеет схожий смысл, но возвращает True только в том случае, если объект является обычным файлом (не директория и не ссылка):

os.path.exists('/home/vitaly')  # True
os.path.exists('/home/david')   # False
os.path.isfile('/home/vitaly')  # False

Функции os.path.join, os.path.split и os.path.splitext выполняют часто встречающиеся манипуляции со строками путей:

path = os.path.join('/home', 'vitaly', 'test.py')
# /home/vitaly/test.py
head, tail = os.path.split(path)  # ['/home/vitaly', 'test.py']
root, ext = os.path.splitext(path)  # ['/home/vitaly/test', '.py']

Модуль shutil

Модуль shutil предоставляет высокоуровневые инструменты для операций с файлами. Вот несколько примеров:

# копирование файла в директорию
shutil.copy('filename', 'path/to/dir')
# копирование файла в файл с другим именем
shutil.copyfile('filename1', 'filename2')
# рекурсивное копирование директории dir1 в директорию dir2
shutil.copytree('path/to/dir1', 'path/to/dir2')
# рекурсивное удаление содержимого директории dir
shutil.rmtree('path/to/dir')  
# рекурсивное перемещение файла или директории
shutil.move(src, dst)

Модуль glob

Модуль glob позволяет выполнять поиск объектов в файловой системе, имена которых удовлетворяют заданному паттерну:

# список текстовых файлов в текущей директории
text_files = glob.glob('./*.txt')
# рекурсивный поиск файлов с расширением .py,
# начиная с текущей директории
text_files_all = glob.glob('./**/*.py', recursive=True)

Работа со строками

Модуль string

Модуль string содержит различные инструменты для работы со строками, многие из которых дублируют возможности стандартного типа str. Модуль string содержит набор констант, которые часто оказываются полезны:

string.ascii_lowercase  # 'abcdefghijklmnopqrstuvwxyz'
string.ascii_uppercase  # 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
string.ascii_letters
# 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
string.digits           # '0123456789'
string.punctuation      # !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

Модуль re

Модуль re содержит инструменты для работы с регулярными выражениями. Обсуждение регулярных выражений выходит за рамки этого курса. Мы рекомендуем читателю самостоятельно изучить базовые приемы работы с регулярными выражениями.

Вычисления с произвольной точностью

Модуль decimal

Модуль decimal позволяет выполнять арифметические операции с плавающей точной с фиксированной точностью:

from decimal import *

getcontext().prec = 6
Decimal(1) / Decimal(7)  # Decimal('0.142857')

getcontext().prec = 28
Decimal(1) / Decimal(7)  # Decimal('0.1428571428571428571428571429')

Объекты Decimal представлены в памяти точно. Это значит, что числа Decimal можно сравнивать с помощью операторов == и !=, не опасаясь погрешности из-за округления двоичного представления (как это происходит в случае с типом float).

В модуле decimal доступны некоторые математические функции:

getcontext().prec = 28
Decimal(2).sqrt()      # Decimal('1.414213562373095048801688724')
Decimal(1).exp()       # Decimal('2.718281828459045235360287471')
Decimal('10').ln()     # Decimal('2.302585092994045684017991455')
Decimal('10').log10()  # Decimal('1')

Модуль fractions

Тип Fraction из модуля fractions описывает рациональные числа — числа, которые можно представить в виде обыкновенной дроби:

from fractions import Fraction
from math import pi, cos
Fraction(16, -10)  # Fraction(-8, 5)
Fraction('3.1415926535897932').limit_denominator(1000)
# Fraction(355, 113)
Fraction(cos(pi/3))  # Fraction(4503599627370497, 9007199254740992)

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

Продвинутые структуры данных и эффективное итерирование

Модуль queue

Модуль queue содержит реализацию нескольких структур данных, среди которых FIFO-очередь queue.Queue и очередь с приоритетом queue.PriorityQueue. Очередь с приоритетом возвращает не первый добавленный элемент, а наименьший:

from queue import Queue, PriorityQueue
arr = [3, 6, 1, 9, 4, 7, 2, 5, 8, 1]

q = Queue()
for i in arr:
    q.put_nowait(i)

while not q.empty():
    print(q.get_nowait(), end=' ')
# 3 6 1 9 4 7 2 5 8 1

pq = PriorityQueue()
for i in arr:
  pq.put_nowait(i)

while not pq.empty():
    print(pq.get_nowait(), end=' ')
# 1 1 2 3 4 5 6 7 8 9

Типы модуля queue созданы для работы в многопоточной среде исполнения. С особенностями многопоточной работы, которые мы не будем обсуждать, связано использование методов get_nowait и put_nowait вместо get и put.

Модуль collections

Модуль collections расширяет набор стандартных контейнеров python. Рассмотрим три типа данных из этого модуля.

Функция namedtuple() позволяет создавать типы данных с именованными полями:

from collections import namedtuple
Vector = namedtuple('Vector', ['x', 'y', 'z'])
v1 = Vector(1., 0.5, 0.6)
v1.x  # 1.
v1.y  # 0.5
v1.z  # 0.6

Тип deque реализует контейнер двусторонняя очередь, или дек. Это последовательный контейнер, который позволяет эффективно добавлять и удалять элементы в начало и в конец:

from collections import deque
deq = deque([1, 3, 5])
deq.append('a')        # [1, 3, 5, 'a']
deq.appendleft(False)  # [False, 1, 3, 5, 'a']
a = deq.pop()          # a = 'a', deq = ['False', 1, 3, 5]
b = deq.popleft()      # b = False, deq = [1, 3, 5]

Тип Counter является подклассом типа dict и позволяет удобно подсчитывать количество вхождений элементов в контейнере, например:

from collections import Counter
the_longest_word_in_english\
    = 'pneumonoultramicroscopicsilicovolcanoconiosis'
cnt = Counter(the_longest_word_in_english)
for key, val in cnt.items():
    print(f'{key}: {val}', end=', ')
# p: 2, n: 4, e: 1, u: 2, m: 2, o: 9, l: 3, t: 1, r: 2, a: 2, i: 6, c: 6, s: 4, v: 1
cnt.most_common(1)  # [('o', 9)]

Модуль itertools

Модуль itertools содержит множество инструментов для эффективного итерирования по элементам контейнеров. Эффективное в данном случае означает, что необходимое следующее значение генерируется на лету, без хранения всех значений, количество которых может быть очень большое. Рассмотрим несколько инструментов из этого модуля:

import itertools
arr = list(range(5))  # arr = [0, 1, 2, 3, 4]

for item in itertools.accumulate(arr):
    print(item, end=' ')
# 0 1 3 6 10

for perm in itertools.permutations(arr):
    print(''.join(map(str, perm)), end=' ')
# 01234 01243 01324 01342 01423 01432 ...

for comb in itertools.combinations(arr, 3):
    print(''.join(map(str, comb)), end=' ')
# 012 013 014 023 024 034 123 124 134 234

Время и дата с модулем datetime

Модуль datetime позволяет полноценно работать с объектами даты и времени:

from datetime import date, timedelta
d1 = date.fromisoformat('2019-12-04')
d2 = date(2002, 12, 31)
d1 < d2  # False
delta = d1 - d2
delta.days  # 6182
type(delta)  # <class 'datetime.timedelta'>

delta2 = timedelta(days=15)
d3 = d1 + delta2  # 2019-12-19
d3.weekday()  # 3 (Среда)
d4 = date.today()  # 2020-07-15
import time
from datetime import datetime
dt1 = datetime.fromtimestamp(time.time())  # 2020-07-15 21:47:09.036145
dt2 = datetime.fromisoformat('2020-07-15 21:47:09.036145')
dt2.timestamp() # 1594824429.036145 (секунд прошло с 1970-01-01)

Резюме

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

Источники