Обработка данных. Часть 1#

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

Строки в Python хранятся в Unicode.

В Python нет отдельного типа для одного символа. Символ это str длинной 1.

Индексация#

str индексируются как перечисляемые типы. К ним можно применять срезы и перечислять по символам.

s = "привет"
s[1] == "p" # True
for ss in s:
    print(ss)
п
р
и
в
е
т

Изменение#

str нельзя изменить.

Чтобы изменить строку надо создать новую из старой или превратить ее в список.

s = "привет"
s = s[0:1] + "Ф" + s[2:] # Успешно

s = "привет"
vs = list(s)
vs[1] = "Ф"
s = ''.join(vs) # Успешно

Некоторые методы#

find#

Поиск подстроки.

s = "hello world"
x = s.find("world") # x == 6
x = s.find("code") # x == -1

startswith и endswith#

Проверка, что строка начинается или заканчивается на подстроку.

s = "hello world"
x = s.startswith("hello") # x == True
x = s.endswith("code") # x == False

Конкатенация строк#

a = "hello"
b = "world"
c = a + " " + b #c == "hello world"

split#

Разбивает строку на список строк с использованием разделителя (по умолчанию пробел)

s = "goodbye|blue|sky"
d = s.split("|") # d == ['goodbye', 'blue', 'sky']

join#

Собирает строку из списка строк с использованием разделителя.

jp = ["goodbye", "blue", "sky"]
sep = "_"
s = sep.join(jp) # s == "goodbye_blue_sky"

strip#

Удаляет из начала и конца строки пробельные символы

s = "\t  goodbye blue  sky  \n \n"
d = s.strip() # d == "goodbye blue  sky"

replace#

Выполняет замену в строке.

s = "goodbye blue sky"
d = s.replace("blue", "red") # d == "goodbye red sky"

encode и decode#

Превращают str в bytes и обратно в заданной кодировке (по умолчанию utf8).

s = "привет мир"
d = s.encode("cp866")
# d == b'\xaf\xe0\xa8\xa2\xa5\xe2 \xac\xa8\xe0'
q = d.decode("cp866")
# q == "привет мир"

ljust и rjust#

Догоняет строку заданным символом до нужной длинны.

s = "привет мир"
w = s.ljust(15,"_")
# w == 'привет мир_____'
q = s.rjust(15,"_")
# q == '_____привет мир'

Подстановка в строки#

Методы подстановки в Python:

  • В стиле printf: "Привет %s!" % ("мир",)

  • В стиле format: "Привет {}!".format("мир")

  • В стиле f-строк: m = "мир"; f"Привет {m}!"

Формирование строк в стиле printf#

У типа str предусмотрен оператор % который принимает вторым аргументом кортеж и подставляет его элементы на указанные в шаблоне места.

t = "%s = %.2f"
s = t % ("A", 42)
# s == 'A = 42.00'

Популярные форматы#

Формат

Ожидаемый тип

Эффект

Варианты с модификаторами

%s

str

Подстановка строки

%d

int

Подстановка целого числа

%06d, %#4d, %+02d

%x

int

Подстановка числа в шестнадцатеричной системе

%04x, %#x, %+06x

%f

float

Подстановка числа с плавающей точкой

%0.4f, %8.4f, %+8.2f, %+08.2f

%e

float

Экспоненциальный формат

%1.6e

Примеры с использованием модификаторов#

print("%6d" % 42)
print("%06d" % 42)
print("%.2f" % 3.14159)
print("%06.2f" % 3.14159)
print("%#6.2f" % 3.14159)
print("%+6.3f" % 3.14159)
print("%+1.4e" % 3.14159)
    42
000042
3.14
003.14
  3.14
+3.142
+3.1416e+00

Формирование строк в стиле format#

У типа str есть специальный метод format:

s = 'Привет, {}!'
w = s.format('мир')
print(w)
Привет, мир!

Преимущества:

  • Автоматическое приведение типов

  • Возможность передачи параметров по имени и по номеру (в том числе повторение)

Форматы аналогичны % но указываются после символа : перед которым может стоять имя аргумента.

s = 'Power {power:.2f} State {state}'
w = s.format(state = "Ready", power = 3.14159)
print(w)
Power 3.14 State Ready

Формирование строк в стиле f-строк#

Строки могут быть созданы со специальным модификатором f который позволяет напрямую вставлять переменные в строку:

q = 'мир'
w = f'Привет, {q}!'
print(w)
Привет, мир!

Преимущества:

  • Автоматическое приведение типов

  • Отсутствие дублирования имен переменных в шаблоне и в программе

Форматы аналогичны % но указываются после символа :, как и в подстановках через format.

power = 3.14159
state = "Ready"
w = f'Power {power:.2f} State {state}'
print(w)
Power 3.14 State Ready

Модификатор f можно сочетать с модификатором r, чтобы не обрабатывать спецсимволы.

filename = 'test'
fullpath = fr'c:\test\{filename}.txt'

Разбор строк#

Базовые разбор строк часто можно выполнить комбинацией методов split, strip и приведением полученных элементов строки к нужным типам. Но иногда этого бывает мало или получаемый код выходит слишком громоздким.

scanf#

Пакет scanf предоставляет метод разбора строк аналогичный C функции scanf.

from scanf import scanf

template = 'Power: %f [%], %s, Stemp: %f'
text = 'Power:   0.0 [%], Cool, Stemp: 23.73'
s = scanf(template, text)
print(s)
(0.0, 'Cool', 23.73)

Шаблон

Значение

%c

Один символ

%5c

Пять символов

%d, %i

Целое число

%7d, %7i

Целое число в 7 символов

%f

Число с плавающей запятой

%X, %x

Целое число в шестнадцатеричной системе

%s

Строка до первого пробела

Поддерживаемые модификаторы: ширина поля %12d и пропуск поля %*d.

Дополнительно имеется тип %c — один или нескольких символов, например %12c, а %s работает до первого пробела.

Подробное описание форматов scanf.

Регулярные выражения#

Регулярные выражения — специальный язык программирования. В стандартной библиотеке Python есть модуль для работы с ними — re. Перед использованием регулярные выражения следует компилировать методом re.compile, это повышает производительность.

Основные применения:

  • Проверка формальной корректности строки

  • Поиск/замена по шаблону

  • Разделение строки по правилам

Проверить свои регулярные выражения можно, например, на сайте http://www.pyregex.com/.

Использование длинных и сложных регулярных выражений считается плохим стилем. Для разбора сложного синтаксиса следует использовать другие инструменты.

Некоторые компоненты регулярных выражений:

Выражение

Значение

^

Начало строки

$

Конец строки

.

Один любой символ

[abcd]

Один из символов abcd

[a-z0-9A-Z]

Один из алфовитноцифровых символов

\w

Один символ из набора [a-zA-Z0-9_] + Unicode letters

\s

Один пробельный символ

( )

Группа из одного или нескольких шаблонов к которой можно обращаться

+

Модификатор «один или несколько»

*

Модификатор «ноль или несколько»

Чтобы избежать проблем с экранированием регулярные выражения записываются, как строки без экранирования, например r'\w+'.

Метод match#

Метод предназначен для проверки соответствие строки регулярному выражению.

import re

r = re.compile(r"\w+ = \w+")
print(r.match("Привет = мир"))
print(r.match("Привет ="))
print(r.match("Привет = мир проверка"))
<re.Match object; span=(0, 12), match='Привет = мир'>
None
<re.Match object; span=(0, 12), match='Привет = мир'>
r = re.compile(r"^\w+\s+=\s+\w+$")
print(r.match("Привет = мир проверка"))
print(r.match("Привет = мир\n"))
None
<re.Match object; span=(0, 12), match='Привет = мир'>
r = re.compile(r"^(\w+)\s+=\s+(\w+)$")
wd = r.match("Привет = мир")

print(wd.group(0))
print(wd.group(1))
print(wd.group(2))
Привет = мир
Привет
мир

Методы search и findall#

Поиск в строке подстрок(-и) удовлетворяющей выражению.

import re

r = re.compile(r"[а-я]+")
wd = r.search("Если я чешу в затылке не беда!")
s = wd.group(0) # s == 'сли'

t = r.findall("Если я чешу в затылке не беда!")
print(t)
['сли', 'я', 'чешу', 'в', 'затылке', 'не', 'беда']

Метод sub#

Замена подстрок(-и) удовлетворяющей выражению.

import re

r = re.compile(r"\s+")
s = r.sub("-","begin      begin       begin   end    end   end")
print(s)
begin-begin-begin-end-end-end

Метод split#

Разбиение строки по разделителю в виде регулярного выражения.

import re

r = re.compile(r"\s+")
s = r.split("Если   я    чешу  в   затылке   не беда!")
print(s)
['Если', 'я', 'чешу', 'в', 'затылке', 'не', 'беда!']

Дата и время#

В Python дата и время обычно используется в следующих формах:

  • Модуль datetime:

    • datatime — основной класс объектов времени. Объект datatime может быть по отношению к часовому поясу «naive» или «aware».

    • timedelta — класс интервала времени.

  • Модуль time — работа с временем в стиле Unix time_t (время выражается числом секунд с определенного момента, но имеет тип float — целая часть — число секунд, дробная — доли секунды).

  • Модуль calendar — функции календаря.

  • Модуль zoneinfo — данные по часовым поясам.

Дата и время (TZ naive)#

from datetime import datetime, timedelta
from time import time

t1 = time() # t1 == 1509532912.9569173

t2 = datetime.now()
# t2 == datetime.datetime(2017, 11, 1, 13, 42, 55, 774096)

t3 = datetime.fromtimestamp(t1)
# t3 == datetime.datetime(2017, 11, 1, 13, 44, 26, 259667)

t4 = datetime(year=2011,month=2,day=3,hour=12,minute=22,second=34)
# t4 == datetime.datetime(2011, 2, 3, 12, 22, 34)

t5 = t4.timestamp() # t5 == 1296724954.0

Дата и время (UTC)#

import datetime
import time

t7 = datetime.datetime.now()
print(t7)

print(t7.timestamp())

t8 = time.time() + time.timezone
print(t8)

t9 = datetime.datetime.fromtimestamp(t8)
print(t9)
2024-11-14 06:50:42.551622
1731556242.551622
1731545442.5517058
2024-11-14 03:50:42.551706

Дата и время (TZ aware)#

from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

t_msk = datetime.now(ZoneInfo('Europe/Moscow'))
# t_msk == datetime.datetime(2017, 11, 1, 14, 52, 39, 297530, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+3:00:00 STD>)
t_ber = datetime.now(ZoneInfo('Europe/Berlin'))
# t_ber == datetime.datetime(2017, 11, 1, 12, 52, 50, 681447, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)

(t_msk - t_ber).total_seconds() < 0.001 # True

t_msk.astimezone(ZoneInfo('Europe/Berlin'))
# datetime.datetime(2017, 11, 1, 12, 55, 21, 842909, tzinfo=<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>)

datetime.now().replace(tzinfo=ZoneInfo('Europe/Berlin'))
# datetime.datetime(2017, 11, 1, 15, 0, 59, 63396, tzinfo=<DstTzInfo 'Europe/Berlin' LMT+0:53:00 STD>)
datetime.datetime(2024, 11, 14, 6, 50, 42, 556834, tzinfo=zoneinfo.ZoneInfo(key='Europe/Berlin'))

Работа с интервалами#

Разность двух объектов datetime дает объект timedelta. Также его можно сконструировать через конструктор timedelta. timedelta можно складывать и вычитать, а также умножать на число.

from datetime import datetime, timedelta
from time import time

t1 = datetime(year=2013,month=7,day=15,hour=12,minute=22,second=34)
t2 = datetime.now()

td = t2 - t1
#td == datetime.timedelta(1570, 6567, 748425)

td.total_seconds() # 135654567.748425

t1 + timedelta(days=15)
# datetime.datetime(2013, 7, 30, 12, 22, 34)

timedelta(days=15) *2 
# datetime.timedelta(30)
datetime.timedelta(days=30)

Для модификации datetime к нему нужно или прибавить/отнять соответствующий timedelta или обновить конкретные поля методом replace.

Даты и время и строки#

Для приведения datetime к строке и разбора строки в datetime имеются функции strftime и strptime соответственно.

from datetime import datetime

t1 = datetime(year=2013,month=7,day=15,hour=12,minute=22,second=34)
t1.strftime("%d.%m.%Y %H:%M:%S")
print(t1)

t2 = datetime.strptime('15.07.2013 12:22:34', "%d.%m.%Y %H:%M:%S")
print(t2 == t1)
2013-07-15 12:22:34
True

Некоторые популярные форматы#

Формат

Описание

format

Пример

%d

День месяца

%02d

01

%m

Месяц числом

%02d

08

%y

Год (2 знака)

%02d

05

%Y

Год (4 знака)

%04d

2005

%H

Час

%02d

08

%M

Минута

%02d

05

%S

Секунда

%02d

03

%j

День года

%d

25

Подробное описание форматов даты и времени.

Метод sleep#

Метод time.sleep выполняет приостановку выполнения скрипта на заданное время в секундах и их долях.

import time

time.sleep(3)
time.sleep(0.23)

Работа с аргументами командной строки#

  • Аргументы командной строки — основной способ передачи параметров консольным приложениям

  • Интерпретатор Python рассматривает все аргументы переданные после имени скрипта, как аргументы скрипта

  • По традиции 0-вой аргумент — имя скрипта

  • Доступ к аргументам напрямую — список argv модуля sys.

import sys

print(sys.argv)
['/usr/lib/python3.12/site-packages/ipykernel_launcher.py', '-f', '/tmp/tmpa3fhi2le.json', '--HistoryManager.hist_file=:memory:']

Пара слов о традициях обработки аргументов#

  • По традиции аргументы делятся на ключи/флаги и собственно аргументы. По традиции ключи/флаги начинаются с символа -

  • По традиции ключи имеют собственные аргументы (-x 258), а флаги — нет (-x).

  • По традиции любой ключ/флаги программы может быть передан в короткой и длинной форме:

-o file.txt
--output-file file.txt
-v
--verbose

По традиции собственно аргументы передаются после ключей/флагов по порядку

python3 script.py -v -o file.txt Arg1 Arg2

Работа с модулем argparse#

Для разбора аргументов командной строки в стандартной библиотеке есть модуль argparse:

  • Создать объект — парсер

  • Описать возможные аргументы с помощью вызовов функций парсера

  • Запустить паресер

  • Парсер сконструирует объект в полях которого будут значения соответствующих аргументов

Описание аргументов#

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("-v", "--verbose", action="store_true",
                    default=False, help="Подробный вывод")

parser.add_argument("-i", "--input", action="store", required=True,
                    help="Входной файл (обязательный аргумент)")

parser.add_argument("-o", "--output", action="store", default="out.dat",
                    help="Выходной файл (по умолчанию out.dat)")

parser.add_argument('var', type=int,
                    help='Первый обязательный аргумент')

parser.add_argument('livar', nargs='+',
                    help='Один или более обязательных аргументов');

Автоматическая генерация справки#

> python3 test.py 
usage: test.py [-h] [-v] -i INPUT [-o OUTPUT] var livar [livar ...]
test.py: error: the following arguments are required: -i/--input, var, livar
> python3 test.py -h
usage: test.py [-h] [-v] -i INPUT [-o OUTPUT] var livar [livar ...]

positional arguments:
  var                   Первый обязательный аргумент
  livar                 Один или более обязательных аргументов

optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         Подробный вывод
  -i INPUT, --input INPUT
                        Входной файл (обязательный аргумент)
  -o OUTPUT, --output OUTPUT
                        Выходной файл (по умолчанию out.dat)