Рубрики
Без рубрики

Неизвестные особенности модуля оператора Python

На первом взгляде модуль оператора Python может показаться не очень интересным. Он включает в себя много … Tagged с Python.

На первом взгляде Python’s оператор Модуль может показаться не очень интересным. Он включает в себя множество функций оператора для арифметических и бинарных операций, а также пару удобных и вспомогательных функций. Они могут показаться не такими полезными, но с помощью лишь нескольких из этих функций вы можете сделать свой код более быстрым, более кратким, более читаемым и более функциональным. Таким образом, в этой статье мы рассмотрим этот великий модуль Python и максимально используют каждую функцию, включенную в него.

Сценарии использования

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

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

def apply(op, x, y):
    return op(x, y)

from operator import mul
apply(mul, 3, 7)
# 21

Причина, по которой мы должны сделать это, заключается в том, что операторы Python ( + , - , …) не функции, поэтому вы не можете передавать их непосредственно к функциям. Вместо этого вы можете пройти в версии из оператор модуль. Вы можете легко реализовать функцию обертки, которая делает это для вас, но никто не хочет создавать функцию для каждого арифметического оператора, верно? Кроме того, в качестве бонуса это обеспечивает более функциональный стиль программирования.

Вы также можете подумать, Мне не нужно оператор Модуль для этого, я могу просто использовать Lambda выражение! . Да, но вот вторая причина, почему вы должны использовать этот модуль. Функции в этом модуле быстрее, чем лямбдас. Вы, очевидно, не заметите это с одним исполнением, но если вы запустите его в цикле достаточно раз, то это будет иметь большое значение:

python -m timeit "(lambda x,y: x + y)(12, 15)"
10000000 loops, best of 3: 0.072 usec per loop
python -m timeit -s "from operator import add" "add(12, 15)"
10000000 loops, best of 3: 0.0327 usec per loop

Так что, если вы привыкли писать что -то вроде (Lambda x, y: x + y) (12, 15) , вы можете перейти на operator.add (12, 15) Для небольшого повышения производительности.

Третья и для меня самая важная причина использовать оператор Модуль – это читабельность – это скорее личные предпочтения, и если вы используете Lambda Выражения все время, тогда для вас может быть более естественным использовать их, но, по моему мнению, в целом более читаемо использовать функции в оператор модуль, а не лямбдас, например, рассмотрите следующее:

(lambda x, y: x ^ y)(7, 10)

from operator import xor
xor(7, 10)

Очевидно, что второй вариант более читабелен.

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

Все варианты

Как я уже упоминал, этот модуль имеет функцию для каждой арифметики питона, бить и истины, а также для некоторых дополнений. Полный список отображения между функциями и фактическими операторами см. Таблица в документах Анкет

Наряду со всеми ожидаемыми функциями, этот модуль также включает в себя свои версии на месте, которые реализуют такие операции, как A или A Анкет Если вы хотите использовать их, вы можете просто префикс основные версии с я , например, я добавить или ИМУЛ .

Наконец, в оператор Вы также найдете Дандер Версии всех этих функций, например, __add__ или __mod__ Анкет Они присутствуют там по устаревшим причинам, и версии без подчеркивания должны быть предпочтительны.

Помимо всех фактических операторов, этот модуль имеет еще несколько функций, которые могут пригодиться. Один из них мало знаю Length_hint функция, которая может быть использована для получения нечеткий идея длины итератора:

from operator import length_hint
iterator = iter([2, 4, 12, 5, 18, 7])
length_hint(iterator)
# 6
iterator.__length_hint__()
# 6

Я хочу выделить смутный Ключевое слово здесь – не полагайтесь на это значение, потому что это действительно Подсказка и не дает никаких гарантий точности.

Еще одна удобная функция, которую мы можем получить из этого модуля, – это countof (a, b) который возвращает номера вхождения b в A , Например:

from operator import countOf
countOf([1, 4, 7, 15, 7, 5, 4, 7], 7)
# 3

И последний из этих простых помощников – Indexof (a, b) , который возвращает индекс первого появления b в A :

from operator import indexOf
indexOf([1, 4, 7, 15, 7, 5, 4, 7], 7)
# 2

Ключевые функции

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

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

Допустим, у нас есть список словарей, и мы хотим сортировать их по общему ключу. Вот как мы можем сделать это с itemgetter :

rows = [
    {"name": "John", "surname": "Doe", "id": 2},
    {"name": "Andy", "surname": "Smith", "id": 1},
    {"name": "Joseph", "surname": "Jones", "id": 3},
    {"name": "Oliver", "surname": "Smith", "id": 4},
]

from operator import itemgetter
sorted_by_name = sorted(rows, key=itemgetter("surname", "name"))
# [{"name": "John", "surname": "Doe", "id": 2},
#  {"name": "Joseph", "surname": "Jones", "id": 3},
#  {"name": "Andy", "surname": "Smith", "id": 1},
#  {"name": "Oliver", "surname": "Smith", "id": 4}]

min(rows, key=itemgetter("id"))
# {"name": "Andy", "surname": "Smith", "id": 1}

В этом фрагменте мы используем отсортировано функция, которая принимает итерационную и ключевую функцию. Эта ключевая функция должна быть вызовой, которая берет отдельный элемент из итерационного ( строки ) и извлекает значение, используемое для сортировки. В этом случае мы проходим в itemgetter что создает для нас призыв. Мы также даем ему клавиши словаря от ряды которые затем питаются объектом __getItem__ и результаты поиска затем используются для сортировки. Как вы, наверное, заметили, мы использовали оба Фамилия и имя Таким образом, мы можем одновременно сортировать на нескольких областях.

Последние строки фрагмента также показывают еще одно использование для itemgetter , который является поиском строки с минимальным значением для идентификационного поля.

Далее это аттрактер функция, которая может быть использована для сортировки так же, как itemgetter выше. Более конкретно, мы можем использовать его для сортировки объектов, которые не имеют поддержки нативного сравнения:

class Order:
    def __init__(self, order_id):
        self.order_id = order_id

    def __repr__(self):
        return f"Order({self.order_id})"

orders = [Order(23), Order(6), Order(15) ,Order(11)]
from operator import attrgetter
sorted(orders, key=attrgetter("order_id"))
# [Order(6), Order(11), Order(15), Order(23)]

Здесь мы используем self.order_id Атрибут сортировки заказов по их идентификаторам.

Обе показанные выше функции очень полезны в сочетании с некоторыми функциями из итулс Модуль, так что давайте посмотрим, как мы можем использовать itemgetter группировать элементы по своей области:

orders = [
    {"date": "07/10/2021", "id": 10001},
    {"date": "07/10/2021", "id": 10002},
    {"date": "07/12/2021", "id": 10003},
    {"date": "07/15/2021", "id": 10004},
    {"date": "07/15/2021", "id": 10005},
]

from operator import itemgetter
from itertools import groupby

orders.sort(key=itemgetter("date"))
for date, rows in groupby(orders, key=itemgetter("date")):
    print(f"On {date}:")
    for order in rows:
        print(order)
    print()

# On 07/10/2021:
# {"date": "07/10/2021", "id": 10001}
# {"date": "07/10/2021", "id": 10002}
# On 07/12/2021:
# {"date": "07/12/2021", "id": 10003}
# On 07/15/2021:
# {"date": "07/15/2021", "id": 10004}
# {"date": "07/15/2021", "id": 10005}

Здесь у нас есть список строк ( Orders ), который мы хотим группировать по дата поле. Для этого мы сначала сортируем массив, а затем называем GroupBy создавать группы элементов с тем же дата ценность. Если вам интересно, почему нам нужно сначала сортировать массив, потому что GroupBy Функциональная работа, поиск последовательный записи с таким же значением, Поэтому все записи с той же датой должны быть сгруппированы заранее.

В предыдущих примерах мы работали с массивами словарей, но эти функции также могут быть применены к другим итерам. Мы можем, например, использовать itemgetter Чтобы сортировать словарь по значениям, найдите индекс минимального/максимального значения в массиве или списка сортировки кортежей на основе некоторых из их полей:

# Sort dict by value
from operator import itemgetter
products = {"Headphones": 55.90, "USB drive": 12.20, "Ethernet Cable": 8.12, "Smartwatch": 125.80}

sort_by_price = sorted(products.items(), key=itemgetter(1))
# [('Ethernet Cable', 8.12), ('USB drive', 12.2), ('Headphones', 55.9), ('Smartwatch', 125.8)]

# Find index of maximum value in array
prices = [55.90, 12.20, 8.12, 99.80, 18.30]
index, price = max(enumerate(prices), key=itemgetter(1))
# 3, 99.8

# Sort list of tuples based on their indices
names = [
    ("John", "Doe"),
    ("Andy", "Jones"),
    ("Joseph", "Smith"),
    ("Oliver", "Smith"),
]

sorted(names, key=itemgetter(1, 0))
# [("John", "Doe"), ("Andy", "Jones"), ("Joseph", "Smith"), ("Oliver", "Smith")]

Метод

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

from operator import methodcaller

methodcaller("rjust", 12, ".")("some text")
# "...some text"

column = ["data", "more data", "other value", "another row"]
[methodcaller("rjust", 12, ".")(value) for value in column]
# ["........data", "...more data", ".other value", ".another row"]

В первом примере выше мы по существу используем MethodCaller позвонить "какой -то текст" .rjust (12, ".") которые справа от правильной строки до длины 12 символов с Анкет как наполненный символ.

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

Еще один более практичный пример использования MethodCaller может быть следующий код. Здесь мы подаем строки текстового файла карта функция И мы также передаем ему желаемый метод – в данном случае полоса – которые лишают пробелы из каждой из линий. Кроме того, мы перенесем результат этого в фильтр которые удаляют все пустые линии (пустые линии – пустая строка, которая является фальшивным , поэтому они удаляются фильтром).

from operator import methodcaller

with open(path) as file:
    items = list(filter(None, map(methodcaller("strip"), file.read().splitlines())))
    print(items)

Заключительные мысли

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

Оригинал: “https://dev.to/martinheinz/the-unknown-features-of-python-s-operator-module-23p4”