Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs
Продолжаем
знакомиться со стандартной библиотекой языка Python и следующим
модулем будет operator, предоставляющий
набор функций, эквивалентных существующим операторам языка Python. Эти функции
позволяют писать код в функциональном стиле, используя вместо операторов
обычные функции.
Функции модуля operator можно разбить на
следующие категории:
|
Арифметические операции
|
|
add(a, b)
|
a + b
|
сложение
|
|
sub(a,
b)
|
a - b
|
вычитание
|
|
mul(a,
b)
|
a * b
|
умножение
|
|
truediv(a,
b)
|
a / b
|
обычное
деление
|
|
floordiv(a,
b)
|
a // b
|
целочисленное
деление
|
|
pow(a,
b)
|
a ** b
|
возведение
в степень
|
|
mod(a,
b)
|
a % b
|
вычисление
остатка от деления
|
|
neg(a)
|
-a
|
отрицание
(унарный минус)
|
|
Битовые операции
|
|
and_(a,
b)
|
a & b
|
битовое
И
|
|
or_(a,
b)
|
a | b
|
битовое
ИЛИ
|
|
xor(a,
b)
|
a ^ b
|
исключающее
ИЛИ (XOR)
|
|
invert(a)
|
~a
|
битовое
НЕ
|
|
Логические операции
|
|
truth(obj)
|
obj
|
проверка
на истинность
|
|
not_(a)
|
not a
|
отрицание
|
|
is_(a,
b)
|
a is b
|
идентичность
|
|
is_not(a,
b)
|
a is not b
|
не
идентичность
|
|
lt(a,
b)
|
a < b
|
меньше
|
|
le(a,
b)
|
a <= b
|
меньше
либо равно
|
|
eq(a,
b)
|
a == b
|
равно
|
|
ne(a,
b)
|
a != b
|
не
равно
|
|
ge(a,
b)
|
a >= b
|
больше
либо равно
|
|
gt(a,
b)
|
a > b
|
больше
|
|
Работа с последовательностями
|
|
concat(seq1,
seq2)
|
seq1
+ seq2
|
объединение
|
|
contains(seq,
obj)
|
seq in obj
|
вхождение
|
|
setitem(obj,
k, v)
|
obj[k] = v
|
присвоение
по индексу
|
|
delitem(obj,
k)
|
del obj[k]
|
удаление
по индексу
|
|
getitem(obj,
k)
|
obj[k]
|
получение
значения по индексу
|
|
getitem(seq, slice(i, j))
|
seq[i:j]
|
взятие
среза
|
Существуют и
некоторые другие функции в модуле operator. Со всеми можно
подробно ознакомиться на странице официальной документации:
https://docs.python.org/3/library/operator.html
Зачем же
понадобилось вводить все эти функции, которые дублируют существующие операторы
языка Python? Дело в том,
что иногда в программах вместо явного оператора требуется его аналог в виде
функции. Например, ранее мы с вами рассматривали модули functools и itertools. И в частности
для вычисления суммы последовательностей применяли функцию reduce следующим
образом:
d = [5, 4, 2, 7, 4]
res = functools.reduce(lambda a, b: a + b, d)
print(res) # 22
Здесь
лямбда-функция определяет простую операцию сложения. И это не редкий случай.
Вместо нее удобнее прописать встроенную функцию add() из модуля operator:
import functools
import operator as op
d = [5, 4, 2, 7, 4]
res = functools.reduce(op.add, d)
print(res) # 22
Видите, это и
короче и визуально воспринимается гораздо лучше. Или же нам нужно числа в
последовательности d разделить по модулю 2:
res = itertools.starmap(op.mod, zip(d, itertools.repeat(2)))
print(list(res)) # [1, 0, 0, 1, 0]
Или же выделить
только четные числа:
res = filter(lambda x: op.mod(x, 2) == 0, d) # [4, 2, 4]
И так далее. Встроенные
функции модуля operator в ряде случаев позволяют несколько
упростить написание и понимание текста программы.
Функции доступа к элементам и атрибутам
Помимо функций,
повторяющих работу стандартных операторов, модуль operator также содержит
функции (точнее классы) для извлечения значений атрибутов объектов, создаваемых
непосредственно в процессе работы программы.
Функция itemgetter()
Первая такая
функция itemgetter() служит для удобного извлечения элементов из
последовательностей, таких как списки, кортежи и словари, основываясь на
указанных индексах или ключах.
Пусть в
программе имеется следующий кортеж:
cities = ("Уфа", "Москва", "Рязань")
С помощью itemgetter()
можно создать объект-геттер, который будет возвращать первый элемент любой
индексированной упорядоченной коллекции:
get_first_elem = op.itemgetter(0)
А, затем, через get_first_elem извлекаем первый
элемент:
res = get_first_elem(cities) # "Уфа"
Возможно, это
кажется несколько избыточным. Нам ничто не мешает также получить первый элемент
по индексу:
Объект get_first_elem предполагается
применять, как аналог функции для выбора первого элемента. Например, ранее нам
нужно было отсортировать список имен:
names = ["Антон", "Алексей", "Николай", "Никита", "Андрей", "Сергей", "Афанасий", "Степан"]
по первому
символу. И для этого использовалась конструкция:
names.sort(key=lambda x: x[0])
Так вот, здесь
лямбда-функцию можно заменить на get_first_elem:
names.sort(key=get_first_elem)
Результат будет
тем же самым. Получаем более простую и понятную запись сортировки по первому
элементу.
По аналогии itemgetter можно
определить для выделения какого-либо атрибута объекта или ключа словаря.
Например, возьмем список словарей:
trips = [
{'name': 'Сергей', 'city': 'Москва', 'duration': 10},
{'name': 'Федор', 'city': 'Сочи', 'duration': 7},
{'name': 'Кирилл', 'city': 'Сочи', 'duration': 10},
{'name': 'Алексей', 'city': 'Питер', 'duration': 7},
{'name': 'Елена', 'city': 'Москва', 'duration': 10},
]
и сформируем
геттер для извлечения атрибута 'duration':
get_duration = op.itemgetter('duration')
res = get_duration(trips[0]) # 10
Затем, с его
помощью достаточно удобно можно выполнить сортировку этих словарей по данному
ключу:
s_trips = sorted(trips, key=get_duration)
Функция attrgetter()
Следующая
аналогичная функция attrgetter() позволяет извлекать значения атрибутов
произвольных объектов, а не итерируемых (списки, кортежи, словари), как это делает
itemgetter().
Пусть, например,
имеется следующий класс:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
И создается его
объект:
У объекта p есть атрибуты name и age. Давайте
попробуем получить их значения с помощью itemgetter и attrgetter:
get_age = op.itemgetter('age')
get_name = op.attrgetter('name')
print(get_age(p)) # TypeError
print(get_name(p)) # "Sergey"
Смотрите, вызов
через itemgetter() приводит к
ошибке, тогда как вызов через attrgetter() выдает значение атрибута.
Другой пример.
Пусть у нас имеется словарь:
d = {'name': 'Сергей', 'city': 'Москва', 'age': 10}
и мы попробуем
извлечь значения ключей name и age с помощью
созданных геттеров. Получим:
print(get_age(d)) # 10
print(get_name(d)) # AttributeError
То есть
обращение к ключам словаря через attrgetter() приводит к ошибке, так как
атрибут name у словаря d отсутствует.
Учитывая, что
методы классов также являются атрибутами, и к ним можно обратиться по имени
метода, то вполне допустима следующая программа:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def show_obj(self):
print(f"{self.name}: {self.age}")
p = Person("Sergey", 30)
get_method = op.attrgetter('show_obj')
method = get_method(p)
method() # Sergey: 30
Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs