Работа с модулем оператора Python
Оглавление
- Использование основных функций модуля оператора Python
- Использование функций более высокого порядка модуля оператора Python
- Часто задаваемые вопросы
Всякий раз, когда вы выполняете вычисления в Python, вы используете встроенные операторы, такие как +, %, и **. Знаете ли вы, что Python также предоставляет модуль operator? Хотя может показаться, что цель operator - предоставить альтернативу этим существующим операторам Python, на самом деле модуль имеет гораздо более специализированное назначение, чем это.
Модуль Python operator предоставляет вам набор функций, многие из которых соответствуют встроенным операторам, но не заменяют их. Модуль также предоставляет дополнительные функциональные возможности, как вы скоро узнаете.
В этом руководстве вы узнаете, как:
- Используйте любую из основных функций, эквивалентных оператору
- Передавать
operatorфункций в качестве аргументов - Сериализовать
operatorфункции для последующего использования - Оценка
operatorфункция производительность по сравнению с обычными альтернативами - Используйте более высокий порядок
operatorфункции в ряде интересных примеров
Чтобы извлечь максимальную пользу из этого руководства, вы должны быть знакомы с основными операторами Python и функциональным программированием идеей одного из функция, возвращающая другую функцию обратно вызывающему объекту.
Обладая этими знаниями, вы готовы погрузиться в работу и научиться использовать модуль, который часто неправильно понимают operator, в своих интересах.
Получите свой код: Нажмите здесь, чтобы загрузить бесплатный пример кода, который operator показывает, как использовать модуль <<<5> Python>>.
Использование основных функций модуля Python operator
В этом разделе вы узнаете о функциях operator модуля , эквивалентных операторам, которые имитируют встроенные операторы, и передадите их в качестве аргументов вышестоящим командам.функции упорядочения. Вы также узнаете, как сохранить их для последующего использования. Наконец, вы изучите производительность функций, эквивалентных операторам, и поймете, почему вам никогда не следует использовать их там, где вместо них подойдет встроенный оператор Python.
Изучение принципов работы основных функций
Модуль Python operator содержит более сорока функций, многие из которых эквивалентны операторам Python, с которыми вы уже знакомы. Вот пример:
>>> import operator >>> operator.add(5, 3) # 5 + 3 8 >>> operator.__add__(5, 3) # 5 + 3 8предварительно> кодовый блок>Здесь вы добавляете
5и3вместе, используя какadd(), так и__add__(). Оба результата одинаковы. На первый взгляд, эти функции предоставляют вам ту же функциональность, что и оператор Python+, но их назначение не в этом.Примечание: Большинство функций модуля
operatorсодержат два названия: версия с расширением и версия без расширения. В предыдущем примереoperator.__add__(5, 3)- это более простая версия, поскольку в ней присутствуют двойные подчеркивания. С этого момента вы будете использовать только версии без подчеркивания, такие какoperator.add(5, 3). Версии dunder предназначены для обратной совместимости с версией Python 2 дляoperator.Если вы взглянете на список функций, который предоставляет вам
operator, то обнаружите, что они охватывают не только арифметические операторы, но и равенство, идентификационные, логические и даже побитовые операторы. Попробуйте выбрать их случайным образом:>>> operator.truediv(5, 2) # 5 / 2 2.5 >>> operator.ge(5, 2) # 5 >= 2 True >>> operator.is_("X", "Y") # "X" is "Y" False >>> operator.not_(5 < 3) # not 5 < 3 True >>> bin(operator.and_(0b101, 0b110)) # 0b101 & 0b110 '0b100'предварительно> кодовый блок>В приведенном выше коде вы работаете с выбором из пяти основных категорий. Сначала вы используете эквивалент арифметического оператора, а затем вы пробуете использовать операторы равенства и тождества во втором и третьем примерах соответственно. В четвертом примере вы используете Логический оператор, в то время как в последнем примере используется побитовый оператор В комментариях показаны эквивалентные операторы Python.
Прежде чем читать остальную часть этого руководства, не стесняйтесь потратить некоторое время на то, чтобы поэкспериментировать с набором функций, эквивалентных операторам, которые предоставляет вам модуль Python
operator. Далее вы узнаете, как ими пользоваться.Передача операторов в качестве аргументов в Функции Более Высокого порядка
Вы чаще всего используете функции, эквивалентные операторам, в качестве аргументов для функций более высокого порядка. Вы могли бы написать функцию более высокого порядка, которая выполняет ряд различных задач в зависимости от переданной ей функции
operator. Предположим, например, что вам нужна одна функция, которая могла бы выполнять сложение, вычитание, умножение и деление. Одним из запутанных способов сделать это было бы использовать следующую инструкциюif...elif:>>> def perform_operation(operator_string, operand1, operand2): ... if operator_string == "+": ... return operand1 + operand2 ... elif operator_string == "-": ... return operand1 - operand2 ... elif operator_string == "*": ... return operand1 * operand2 ... elif operator_string == "/": ... return operand1 / operand2 ... else: ... return "Invalid operator." ...предварительно> кодовый блок>В вашей функции
perform_operation()первым параметром является строка, представляющая одну из четырех основных арифметических операций. Чтобы протестировать функцию, вы вводите каждый из четырех операторов. Результаты будут такими, как вы и ожидали:>>> number1 = 10 >>> number2 = 5 >>> calculations = ["+", "-", "*", "/"] >>> for op_string in calculations: ... perform_operation(op_string, number1, number2) ... 15 5 50 2.0предварительно> кодовый блок>Этот код не только запутан, но и ограничен четырьмя операторами, определенными в предложениях
elif. Попробуйте, например, ввести оператор по модулю (%), и функция вернет сообщение"Invalid operator"вместо результата деления по модулю, на который вы рассчитывали.Именно здесь вы можете эффективно использовать функции
operator. Передача их в функцию дает вам несколько преимуществ:>>> def perform_operation(operator_function, operand1, operand2): ... return operator_function(operand1, operand2) ...предварительно> кодовый блок>На этот раз вы улучшили свою функцию
perform_operation()таким образом, что первый параметр может принимать любые изoperatorфункций модуля, которые принимают ровно два аргумента . Второй и третий параметры являются этими аргументами.Пересмотренный тестовый код аналогичен тому, что вы делали ранее, за исключением того, что вы передаете
operatorфункции для вашейperform_operation()функции для использования:>>> from operator import add, sub, mul, truediv >>> number1 = 10 >>> number2 = 5 >>> calculations = [add, sub, mul, truediv] >>> for op_function in calculations: ... perform_operation(op_function, number1, number2) ... 15 5 50 2.0предварительно> кодовый блок>На этот раз ваш список
calculationsсодержит ссылки на сами функции. Обратите внимание, что вы передаете в функции имена, а не вызовы функции . Другими словами, вы передаетеaddвperform_operation(), а неadd(). Вы передаете объект функции, а не результат ее выполнения. Помните, что имя функции на самом деле является ссылкой на ее код. Функция вызывается с использованием синтаксиса().У использования обновленной версии
perform_operation()есть два преимущества. Первое - это возможность расширения. Вы можете использовать обновленный код с любыми другими функциямиoperator, для которых требуется ровно два аргумента. Действительно, вы могли бы поэкспериментировать, передав функции модуляoperatormod(),pow(), иrepeat()в обе версии вашей функции. Ваша обновленная версия работает должным образом, в то время как исходная версия возвращается"Invalid operator".Вторым преимуществом является удобство чтения. Взгляните на обе версии вашей функции
perform_operation(), и вы заметите, что ваша вторая версия не только значительно короче, но и более удобочитаема, чем оригинальная.Передача функций в качестве аргументов другим функциям - это функция, которую вы часто будете использовать в функциональном программировании. Это одно из основных назначений модуля
operator. Позже вы изучите другие примеры этого.Сериализация
operatorФункций модуляОдним из способов сохранения объектов, включая функции, на диске является их сериализация. Другими словами, ваш код преобразует их в потоки байтов и сохраняет на диске для последующего использования. И наоборот, когда вы считываете сериализованные объекты обратно с диска, вы десериализуете их, позволяя считывать их с диска в программу для использования.
Существует несколько причин, по которым вы можете сериализовать функции, в том числе для сохранения их для дальнейшего использования в другой программе или для передачи их между различными процессами, запущенными на одном или нескольких компьютерах.
Распространенным способом сериализации функций в Python является использование модуля
pickle. Это, наряду с его словарной оболочкойshelve,, обеспечивает один из наиболее эффективных способов хранения данных. Однако, когда вы сериализуете функцию, используяpickle, вы сериализуете только ее полное полное имя, а не код в теле функции. Когда вы десериализуете функцию, среда должна предоставить доступ к коду функции. В противном случае функция не сможет работать.Чтобы ознакомиться с примером, вы вернетесь к предыдущему примеру
perform_operation(). Вы будете вызывать различные функцииoperatorдля выполнения различных операций. Следующий код добавляет словарь, который вы будете использовать для сопоставления строкового оператора с соответствующей функцией:>>> import operator >>> operators = { ... "+": operator.add, ... "-": operator.sub, ... "*": operator.mul, ... "/": operator.truediv, ... } >>> def perform_operation(op_string, number1, number2): ... return operators[op_string](number1, number2) ... >>> perform_operation("-", 10, 5) 5предварительно> кодовый блок>Операции, поддерживаемые
perform_operation(), определены вoperators. В качестве примера, вы запускаете операцию"-", которая вызываетoperator.sub()в фоновом режиме.Одним из способов совместного использования поддерживаемых операторов между процессами является сериализация словаря
operatorsна диск. Это можно сделать с помощьюpickleследующим образом:>>> import pickle >>> with open("operators.pkl", mode="wb") as f: ... pickle.dump(operators, f) ...предварительно> кодовый блок>Вы открываете двоичный файл для записи. Чтобы сериализовать
operators, вы вызываетеpickle.dump()и передаете структуру, которую вы сериализуете, и дескриптор целевого файла.При этом в вашем локальном рабочем каталоге будет создан файл
operators.pkl. Чтобы продемонстрировать, как повторно использоватьoperatorsв другом процессе, перезапустите оболочку Python и загрузите выбранный файл:>>> import pickle >>> with open("operators.pkl", mode="rb") as f: ... operators = pickle.load(f) ... >>> operators {'+': <built-in function add>, '-': <built-in function sub>, '*': <built-in function mul>, '/': <built-in function truediv>}предварительно> кодовый блок>Сначала вы снова импортируете
pickleи снова открываете двоичный файл для чтения. Чтобы прочитать структуруoperator, вы используетеpickle.load()и передаете дескриптор файла. Затем ваш код считывает сохраненное определение и присваивает его переменной с именемoperators. Это имя не обязательно должно совпадать с вашим исходным именем. Эта переменная указывает на словарь, который ссылается на различные функции, при условии, что они доступны.Обратите внимание, что вам не нужно явно импортировать
operator, хотя модуль должен быть доступен для импорта Python в фоновом режиме.Вы можете определить
perform_operation()еще раз, чтобы убедиться, что он может ссылаться на восстановленный файл и использовать егоoperators:>>> def perform_operation(op_string, number1, number2): ... return operators[op_string](number1, number2) ... >>> perform_operation("*", 10, 5) 50предварительно> кодовый блок>Отлично! Ваш код обрабатывает умножение так, как вы и ожидали.
В том, что
operatorподдерживает сортировку функций, нет ничего особенного. Вы можете сортировать и распаковывать любую функцию верхнего уровня, при условии, что Python способен импортировать ее в среду, в которую вы загружаете обработанный файл.Однако вы не можете сериализовать анонимные лямбда-функции подобным образом. Если бы вы реализовали этот пример без использования модуля
operator, то вы, вероятно, определили бы словарь следующим образом:>>> operators = { ... "+": lambda a, b: a + b, ... "-": lambda a, b: a - b, ... "*": lambda a, b: a * b, ... "/": lambda a, b: a / b, ... }предварительно> кодовый блок>Конструкция
lambda- это быстрый способ определения простых функций, и они могут быть весьма полезны. Однако, посколькуpickleне сериализует тело функции, а только имя функции, вы не можете сериализовать безымянные лямбда-функции:>>> import pickle >>> with open("operators.pkl", mode="wb") as f: ... pickle.dump(operators, f) ... Traceback (most recent call last): ... PicklingError: Can't pickle <function <lambda> at 0x7f5b946cfba0>: ...предварительно> кодовый блок>Если вы попытаетесь сериализовать лямбда-функции с помощью
pickle, то получите сообщение об ошибке. Это тот случай, когда вы часто можете использовать функцииoperatorвместо лямбда-функций.Оглянитесь на свой код сериализации и обратите внимание, что вы импортировали как
operator, так иpickle, в то время как ваш код десериализации импортировал толькоpickle. Вам не нужно было импортироватьoperator, потому чтоpickleсделал это автоматически за вас, когда вы вызвали его функциюload(). Это работает, потому что встроенный модульoperatorлегко доступен.Исследование эффективности
operatorФункций в сравнении с альтернативными вариантамиТеперь, когда у вас есть представление о том, как использовать функции, эквивалентные операторам, вы можете задаться вопросом, следует ли вам использовать их вместо операторов Python или лямбда-функций. В первом случае ответ отрицательный, а во втором - положительный. Встроенные операторы Python всегда работают значительно быстрее, чем их аналоги в модуле
operator. Однако функции модуляoperatorработают быстрее, чем лямбда-функции, и к тому же они более удобочитаемы.Если вы хотите синхронизировать функции модуля
operatorс их встроенными или лямбда-эквивалентами, то вы можете использоватьtimeitмодуль. Лучший способ сделать это - запустить его непосредственно из командной строки:PS> python -m timeit "(lambda a, b: a + b)(10, 10)" 5000000 loops, best of 5: 82.3 nsec per loop PS> python -m timeit -s "from operator import add" "add(10, 10)" 10000000 loops, best of 5: 24.5 nsec per loop PS> python -m timeit "10 + 10" 50000000 loops, best of 5: 5.19 nsec per loop PS> python -m timeit "(lambda a, b: a ** b)(10, 10)" 1000000 loops, best of 5: 226 nsec per loop PS> python -m timeit -s "from operator import pow" "pow(10, 10)" 2000000 loops, best of 5: 170 nsec per loop PS> python -m timeit "10 ** 10" 50000000 loops, best of 5: 5.18 nsec per loopпредварительно> кодовый блок>В приведенном выше сеансе PowerShell используется модуль
timeitдля сравнения производительности различных реализаций сложения и возведения в степень. Ваши результаты показывают, что для обеих операций встроенный оператор работает быстрее, а модульная функцияoperatorпревосходит только лямбда-функцию. Сами значения фактического времени зависят от машины, но их относительные различия значительны.Примечание: Модуль Python
timeitпозволяет вам определять время выполнения небольших фрагментов вашего кода. Обычно вы вызываетеtimeitиз командной строки, используяpython -m timeit, за которым следует строка, содержащая команду, которую вы хотите измерить. Вы используете переключатель-s, чтобы указать код, который вы хотите запустить один раз непосредственно за до начала отсчета времени. В приведенном выше примере вы использовали-sдля импортаpow()иadd()из модуляoperatorперед синхронизацией вашего кода.Продолжайте и попробуйте другие функции
operatorсами. Хотя точные значения времени будут варьироваться от машины к машине, их относительные различия все равно будут показывать, что встроенные операторы всегда работают быстрее, чем аналоги модуляoperator, которые всегда быстрее, чем лямбда-функции.Теперь вы знакомы с функциями, эквивалентными операторам, из модуля
operator, но, возможно, вам захочется потратить некоторое время на изучение остальных этих функций. Как только вы будете готовы двигаться дальше, продолжайте читать, чтобы изучить некоторые другие способы использованияoperator.Использование функций более высокого порядка модуля Python
operatorВ этом разделе вы узнаете о трех функциях более высокого порядка, которые доступны в модуле
operatorPython:itemgetter(),attrgetter()иmethodcaller(). Вы узнаете, как они позволяют вам работать с коллекциями Python различными полезными способами, которые поддерживают функциональный стиль программирования на Python.Выбор Значений Из Многомерных Коллекций С Помощью
itemgetter()Первая функция, о которой вы узнаете, - это
operator.itemgetter(). В ее базовой форме вы передаете ей единственный параметр, который представляет собой индекс. Затемitemgetter()возвращает функцию, которая при передаче коллекции возвращает элемент с этим индексом.Для начала вы создаете список из словарей:
>>> musician_dicts = [ ... {"id": 1, "fname": "Brian", "lname": "Wilson", "group": "Beach Boys"}, ... {"id": 2, "fname": "Carl", "lname": "Wilson", "group": "Beach Boys"}, ... {"id": 3, "fname": "Dennis", "lname": "Wilson", "group": "Beach Boys"}, ... {"id": 4, "fname": "Bruce", "lname": "Johnston", "group": "Beach Boys"}, ... {"id": 5, "fname": "Hank", "lname": "Marvin", "group": "Shadows"}, ... {"id": 6, "fname": "Bruce", "lname": "Welch", "group": "Shadows"}, ... {"id": 7, "fname": "Brian", "lname": "Bennett", "group": "Shadows"}, ... ]предварительно> кодовый блок>В каждом словаре содержится запись о музыканте, принадлежащем к одной из двух групп: Beach Boys или Shadows. Чтобы узнать, как работает
itemgetter(), предположим, вы хотите выбрать один элемент изmusician_dicts:>>> import operator >>> get_element_four = operator.itemgetter(4) >>> get_element_four(musician_dicts) {"id": 5, "fname": "Hank", "lname": "Marvin", "group": "Shadows"}предварительно> кодовый блок>Когда вы передаете
itemgetter()индекс4, он возвращает функцию, на которую ссылаетсяget_element_four, которая возвращает элемент в позиции индекса4в коллекции. Другими словами,get_element_four(musician_dicts)возвращаетmusician_dicts[4]. Помните, что элементы списка индексируются, начиная с0, а не с1. Это означает, чтоitemgetter(4)фактически возвращает пятый элемент в списке.Далее предположим, что вы хотите выбрать элементы из позиций
1,3, и5. Для этого вы передаетеitemgetter()несколько значений индекса:>>> get_elements_one_three_five = operator.itemgetter(1, 3, 5) >>> get_elements_one_three_five(musician_dicts) ({"id": 2, "fname": "Carl", "lname": "Wilson", "group": "Beach Boys"}, {"id": 4, "fname": "Bruce", "lname": "Johnston", "group": "Beach Boys"}, {"id": 6, "fname": "Bruce", "lname": "Welch", "group": "Shadows"})предварительно> кодовый блок>Здесь
itemgetter()создает функцию, которую вы используете для поиска всех трех элементов. Ваша функция возвращает кортеж, содержащий результаты.Теперь предположим, что вы хотите вывести только значения имени и фамилии из словарей в позициях индекса
1,3, и5. Для этого вы передаетеitemgetter()ключи"fname"и"lname":>>> get_names = operator.itemgetter("fname", "lname") >>> for musician in get_elements_one_three_five(musician_dicts): ... print(get_names(musician)) ... ("Carl", "Wilson") ("Bruce", "Johnston") ("Bruce", "Welch")предварительно> кодовый блок>На этот раз
itemgetter()предоставляет функцию для получения значений, связанных с ключами"fname"и"lname". Ваш код выполняет итерацию по набору словарей, возвращаемыхget_elements_one_three_five(), и передает каждый из них вашей функцииget_names(). Каждый вызовget_names()возвращает кортеж, содержащий значения, связанные с ключами словаря"fname"и"lname"словарей в позициях1,3, и5изmusician_dicts.Две функции Python, о которых вы, возможно, уже знаете, это
min()иmax(). Вы можете использовать их для поиска самого низкого и самого высокого элементов в списке:>>> prices = [100, 45, 345, 639] >>> min(prices) 45 >>> max(prices) 639предварительно> кодовый блок>В приведенном выше коде вы получили наименьшее и наибольшее значения:
min()возвращает самый дешевый товар, в то время какmax()возвращает самый дорогой.Функции
min()иmax()содержат параметрkey, который принимает функцию. Если вы создадите функцию с помощьюitemgetter(), то вы можете использовать ее для указанияmin()иmax()анализировать определенные элементы в списке списков или словарях. Чтобы изучить это, вы сначала создаете список списков музыкантов:>>> musician_lists = [ ... [1, "Brian", "Wilson", "Beach Boys"], ... [2, "Carl", "Wilson", "Beach Boys"], ... [3, "Dennis", "Wilson", "Beach Boys"], ... [4, "Bruce", "Johnston", "Beach Boys"], ... [5, "Hank", "Marvin", "Shadows"], ... [6, "Bruce", "Welch", "Shadows"], ... [7, "Brian", "Bennett", "Shadows"], ... ]предварительно> кодовый блок>Содержимое
musician_listsидентичноmusician_dicts, за исключением того, что каждая запись находится в списке. На этот раз предположим, что вы хотите найти элементы списка с наименьшим и наибольшим значениямиid:>>> get_id = operator.itemgetter(0) >>> min(musician_lists, key=get_id) [1, "Brian", "Wilson", "Beach Boys"] >>> max(musician_lists, key=get_id) [7, "Brian", "Bennett", "Shadows"]предварительно> кодовый блок>Сначала вы создаете функцию, используя
itemgetter(), чтобы выбрать первый элемент из списка. Затем вы передаете это какkeyпараметр дляmin()иmax(). Функцииmin()иmax()вернут вам списки с наименьшими и наибольшими значениями в их индексе0позиций соответственно.То же самое можно проделать со списком словарей, используя
itemgetter()для создания функции, которая выбирает ключевые имена. Предположим, вам нужен словарь, содержащий музыканта, фамилия которого стоит первой в алфавите:>>> get_lname = operator.itemgetter("lname") >>> min(musician_dicts, key=get_lname) {"id": 7, "fname": "Brian", "lname": "Bennett", "group": "Shadows"}предварительно> кодовый блок>На этот раз вы настраиваете функцию
itemgetter(), которая выбирает ключ словаря"lname". Затем вы передаете это значение в качестве параметра функцииmin()key. Функцияmin()возвращает словарь с наименьшим значением"lname". Результатом является запись"Bennett". Почему бы не повторить это с помощьюmax()? Попробуйте предсказать, что произойдет, прежде чем запускать свой код для проверки.Сортировка многомерных Коллекций С помощью
itemgetter()Помимо выбора определенных элементов, вы можете использовать функцию из
itemgetter()в качестве параметраkeyдля сортировки данных. Одной из распространенных функций Python, которую вы, возможно, уже использовали, являетсяsorted(). Функцияsorted()создает новый отсортированный список:>>> star_wars_movies_release_order = [4, 5, 6, 1, 2, 3, 7, 8, 9] >>> sorted(star_wars_movies_release_order) [1, 2, 3, 4, 5, 6, 7, 8, 9]предварительно> кодовый блок>Теперь элементы нового списка расположены в порядке возрастания. Если вы хотите отсортировать список на месте, то вместо этого вы можете использовать метод .sort(). Возможно, вам захочется попробовать это в качестве упражнения.
Также можно использовать
itemgetter()для сортировки списков. Это позволяет сортировать многомерные списки по определенным элементам. Чтобы сделать это, вы передаете функциюitemgetter()в параметрsorted()функцииkey.Подумайте еще раз о своем
musician_lists:>>> musician_lists = [ ... [1, "Brian", "Wilson", "Beach Boys"], ... [2, "Carl", "Wilson", "Beach Boys"], ... [3, "Dennis", "Wilson", "Beach Boys"], ... [4, "Bruce", "Johnston", "Beach Boys"], ... [5, "Hank", "Marvin", "Shadows"], ... [6, "Bruce", "Welch", "Shadows"], ... [7, "Brian", "Bennett", "Shadows"], ... ]предварительно> кодовый блок>Теперь вы отсортируете этот список, используя
itemgetter(). Для начала вы решите отсортировать элементы списка в порядке убывания по значениюid:>>> import operator >>> get_id = operator.itemgetter(0) >>> sorted(musician_lists, key=get_id, reverse=True) [[7, "Brian", "Bennett", "Shadows"], [6, "Bruce", "Welch", "Shadows"], [5, "Hank", "Marvin", "Shadows"], [4, "Bruce", "Johnston", "Beach Boys"], [3, "Dennis", "Wilson", "Beach Boys"], [2, "Carl", "Wilson", "Beach Boys"], [1, "Brian", "Wilson", "Beach Boys"] ]предварительно> кодовый блок>Чтобы сделать это, вы используете
itemgetter()для создания функции, которая будет выбирать позицию индекса0, позицию музыкантаid. Затем вы вызываетеsorted()и передаете емуmusician_listsплюс ссылку на функцию изitemgetter()в качестве ееkey. Чтобы обеспечить сортировку по убыванию, вы задаетеreverse=True. Теперь ваш список будет отсортирован в порядке убывания поid.Также возможно выполнить более сложную сортировку многомерных списков. Предположим, вы хотите отсортировать каждый список в обратном порядке в пределах
musician_lists. Сначала выполняется сортировка по фамилии, затем все списки с одинаковой фамилией сортируются по имени. Другими словами, вы выполняете сортировку по имени внутри фамилии:>>> get_elements_two_one = operator.itemgetter(2, 1) >>> sorted(musician_lists, key=get_elements_two_one, reverse=True) [[3, "Dennis", "Wilson", "Beach Boys"], [2, "Carl", "Wilson", "Beach Boys"], [1, "Brian", "Wilson", "Beach Boys"], [6, "Bruce", "Welch", "Shadows"], [5, "Hank", "Marvin", "Shadows"], [4, "Bruce", "Johnston", "Beach Boys"], [7, "Brian", "Bennett", "Shadows"]]предварительно> кодовый блок>На этот раз вы передаете
itemgetter()два аргумента, позиции2и1. Ваш список отсортирован в обратном алфавитном порядке, сначала по фамилии (2), затем по имени (1), где это применимо. Другими словами, сначала отображаются три записиWilson, а затемDennis,Carl, иBrianв порядке убывания. Не стесняйтесь повторно запустить этот код и использовать его для выбора других полей. Попробуйте предсказать, что произойдет, прежде чем запускать свой код, чтобы проверить свое понимание.Те же принципы применимы и к словарям, при условии, что вы указываете ключи, значения которых хотите отсортировать. Опять же, вы можете использовать
musician_dictsсписок словарей:>>> musician_dicts = [ ... {"id": 1, "fname": "Brian", "lname": "Wilson", "group": "Beach Boys"}, ... {"id": 2, "fname": "Carl", "lname": "Wilson", "group": "Beach Boys"}, ... {"id": 3, "fname": "Dennis", "lname": "Wilson", "group": "Beach Boys"}, ... {"id": 4, "fname": "Bruce", "lname": "Johnston", "group": "Beach Boys"}, ... {"id": 5, "fname": "Hank", "lname": "Marvin", "group": "Shadows"}, ... {"id": 6, "fname": "Bruce", "lname": "Welch", "group": "Shadows"}, ... {"id": 7, "fname": "Brian", "lname": "Bennett", "group": "Shadows"}, ... ] >>> get_names = operator.itemgetter("lname", "fname") >>> sorted(musician_dicts, key=get_names, reverse=True) [{"id": 3, "fname": "Dennis", "lname": "Wilson", "group": "Beach Boys"}, {"id": 2, "fname": "Carl", "lname": "Wilson", "group": "Beach Boys"}, {"id": 1, "fname": "Brian", "lname": "Wilson", "group": "Beach Boys"}, {"id": 6, "fname": "Bruce", "lname": "Welch", "group": "Shadows"}, {"id": 5, "fname": "Hank", "lname": "Marvin", "group": "Shadows"}, {"id": 4, "fname": "Bruce", "lname": "Johnston", "group": "Beach Boys"}, {"id": 7, "fname": "Brian", "lname": "Bennett", "group": "Shadows"} ]предварительно> кодовый блок>На этот раз вы передаете
itemgetter()ключи"fname"и"lname". Результат аналогичен тому, что вы получали ранее, за исключением того, что теперь он содержит словари. В то время как в предыдущем примере вы создали функцию, которая выбирала элементы индекса2и1, на этот раз ваша функция выбирает ключи словаря"lname"и"fname".Получение атрибутов из Объектов с помощью attrgetter()
Далее вы узнаете о функции модуля
operatorattrgetter(). Функцияattrgetter()позволяет получать атрибуты объекта. Функция принимает один или несколько атрибутов, которые должны быть извлечены из объекта, и возвращает функцию, которая вернет эти атрибуты из любого объекта, который вы ей передадите. Объекты, переданные вattrgetter(), не обязательно должны быть одного типа. Они должны содержать только тот атрибут, который вы хотите получить.Чтобы понять, как работает
attrgetter(), вам сначала нужно создать новый класс :>>> from dataclasses import dataclass >>> @dataclass ... class Musician: ... id: int ... fname: str ... lname: str ... group: str ...предварительно> кодовый блок>Вы создали класс данных с именем
Musician. Основное назначение вашего класса данных - хранить данные о различных объектах musician, хотя, как вы узнаете позже, он также может содержать методы. Программа@dataclassdecorator позволяет вам напрямую определять атрибуты, указывая их имена и подсказку по типу для их типов данных. Ваш класс содержит четыре признака, которые описывают музыканта.Примечание: Возможно, вам интересно, куда делся
.__init__(). Одно из преимуществ использования класса данных заключается в том, что больше нет необходимости в явном инициализаторе. Чтобы создать объект, вы передаете значения для каждого из атрибутов класса. В приведенном выше классеMusicianпервому атрибуту присваивается значение.id, второму - значение.fnameи так далее.Далее вам понадобится список объектов для работы. Вы повторно используете
musician_listsиз предыдущего списка и используете его для создания списка объектов с именамиgroup_members:>>> musician_lists = [ ... [1, "Brian", "Wilson", "Beach Boys"], ... [2, "Carl", "Wilson", "Beach Boys"], ... [3, "Dennis", "Wilson", "Beach Boys"], ... [4, "Bruce", "Johnston", "Beach Boys"], ... [5, "Hank", "Marvin", "Shadows"], ... [6, "Bruce", "Welch", "Shadows"], ... [7, "Brian", "Bennett", "Shadows"], ... ] >>> group_members = [Musician(*musician) for musician in musician_lists]предварительно> кодовый блок>Вы заполняете
group_membersобъектамиMusician, преобразуяmusician_listsс помощью списка..Примечание: Возможно, вы заметили, что использовали
*musicianдля передачи каждого списка при создании объектов класса. Звездочка указывает Python распаковать список на отдельные элементы при создании объектов. Другими словами, первый объект будет иметь атрибут.id, равный1, атрибут.fname, равный"Brian", и так далее.Теперь у вас есть
group_membersсписок, содержащий семьMusicianобъектов, четыре из которых принадлежат the Beach Boys и три из the Shadows. Далее вы узнаете, как вы можете использовать их сattrgetter().Предположим, вы хотите получить атрибут
.fnameиз каждого элементаgroup_members:>>> import operator >>> get_fname = operator.attrgetter("fname") >>> for person in group_members: ... print(get_fname(person)) ... Brian Carl Dennis Bruce Hank Bruce Brianпредварительно> кодовый блок>Сначала вы вызываете
attrgetter()и указываете, что его выходные данные будут содержать атрибут.fname. Затем функцияattrgetter()возвращает функцию, которая вернет вам атрибут.fnameлюбого объекта, который вы ей передадите. Когда вы перебираете свою коллекцию изMusicianобъектов,get_fname()возвращает атрибуты.fname.Функция
attrgetter()также позволяет настроить функцию, которая может возвращать сразу несколько атрибутов. Предположим, что на этот раз вы хотите вернуть как.id, так и.lnameатрибуты каждого объекта:>>> get_id_lname = operator.attrgetter("id", "lname") >>> for person in group_members: ... print(get_id_lname(person)) ... (1, "Wilson") (2, "Wilson") (3, "Wilson") (4, "Johnston") (5, "Marvin") (6, "Welch") (7, "Bennett")предварительно> кодовый блок>На этот раз, когда вы вызываете
attrgetter()и запрашиваете оба атрибута.idи.lname, вы создаете функцию, способную считывать оба атрибута для любого объекта. При запуске ваш код возвращает как.id, так и.lnameиз списка переданных ему объектовMusician. Конечно, вы можете применить эту функцию к любому объекту, будь то встроенному или пользовательскому, при условии, что объект содержит как атрибут.id, так и атрибут.lname.Сортировка и поиск в списках объектов по атрибуту с помощью
attrgetter()Функция
attrgetter()также позволяет сортировать коллекцию объектов по их атрибутам. Вы можете попробовать это, отсортировавMusicianобъектов вgroup_membersпо.idдля каждого объекта в обратном порядке.Во-первых, убедитесь, что у вас есть доступ к
group_members, как указано в предыдущем разделе руководства. Затем вы можете использовать возможностиattrgetter()для выполнения пользовательской сортировки:>>> get_id = operator.attrgetter("id") >>> for musician in sorted(group_members, key=get_id, reverse=True): ... print(musician) ... Musician(id=7, fname='Brian', lname='Bennett', group='Shadows') Musician(id=6, fname='Bruce', lname='Welch', group='Shadows') Musician(id=5, fname='Hank', lname='Marvin', group='Shadows') Musician(id=4, fname='Bruce', lname='Johnston', group='Beach Boys') Musician(id=3, fname='Dennis', lname='Wilson', group='Beach Boys') Musician(id=2, fname='Carl', lname='Wilson', group='Beach Boys') Musician(id=1, fname='Brian', lname='Wilson', group='Beach Boys')предварительно> кодовый блок>В этом фрагменте кода вы настраиваете функцию
attrgetter(), которая может возвращать атрибут.id. Чтобы отсортировать список в обратном порядке по.id, вы присваиваете ссылкуget_idпараметруsorted()методаkeyи устанавливаете значениеreverse=True. Когда вы печатаете объектыMusician, вывод показывает, что ваша сортировка действительно сработала.Если вы хотите отобразить объект с наибольшим или наименьшим значением
.id, то вы используете функциюmin()илиmax()и передаете ей ссылку на вашget_id()функционировать в качестве своегоkey:>>> min(group_members, key=get_id) Musician(id=1, fname='Brian', lname='Wilson', group='Beach Boys') >>> max(group_members, key=get_id) Musician(id=7, fname='Brian', lname='Bennett', group='Shadows')предварительно> кодовый блок>Сначала вы создаете функцию
attrgetter(), которая может найти атрибут.id. Затем вы передаете его в функцииmin()иmax(). В этом случае ваш код возвращает объекты, содержащие наименьшее и наибольшее значения атрибутов.id. В данном случае это объекты с.idзначениями1и7. Возможно, вы захотите поэкспериментировать с этим дальше, выполнив сортировку по другим атрибутам.Вызов методов для объектов с
methodcaller()Последняя функция, о которой вы узнаете, - это
methodcaller(). Концептуально она похожа наattrgetter(), за исключением того, что она работает с методами. Чтобы использовать его, вы передаете имя метода вместе с любыми параметрами, которые ему требуются. Он вернет функцию, которая вызовет метод для любого объекта, который вы ей передадите. Объекты, передаваемые вmethodcaller(), необязательно должны быть одного типа. Они должны содержать только вызываемый вами метод.Чтобы узнать о
methodcaller(), вам сначала нужно расширить существующий класс данныхMusicianс помощью метода:>>> from dataclasses import dataclass >>> @dataclass ... class Musician: ... id: int ... fname: str ... lname: str ... group: str ... ... def get_full_name(self, last_name_first=False): ... if last_name_first: ... return f"{self.lname}, {self.fname}" ... return f"{self.fname} {self.lname}" ...предварительно> кодовый блок>Вы добавляете метод
.get_full_name()вMusician, который принимает единственный параметр с именемlast_name_firstи значением по умолчаниюFalse. Это позволяет вам указать порядок, в котором возвращаются имена.предположим, вы хотите вызвать
.get_full_name()на каждый объект в предварительно определеннойgroup_membersсписок:>>> import operator >>> first_last = operator.methodcaller("get_full_name") >>> for person in group_members: ... print(first_last(person)) ... Brian Wilson Carl Wilson Dennis Wilson Bruce Johnston Hank Marvin Bruce Welchпредварительно> кодовый блок>Здесь вы используете
methodcaller()для создания функции с именемfirst_last(), которая будет вызывать метод.get_full_name()любого объекта, который вы ей передадите. Обратите внимание, что вы не передаете никаких дополнительных аргументов вfirst_last(), поэтому вы получаете обратно список имен, за которыми следуют фамилии всех объектовMusician.Если вы хотите, чтобы имена следовали за фамилиями, то вы можете ввести значение
Trueдляlast_name_first:>>> last_first = operator.methodcaller("get_full_name", True) >>> for person in group_members: ... print(last_first(person)) ... Wilson, Brian Wilson, Carl Wilson, Dennis Johnston, Bruce Marvin, Hank Welch, Bruce Bennett, Brianпредварительно> кодовый блок>На этот раз вы используете
methodcaller()для создания функции с именемlast_first(), которая будет вызывать.get_full_name()метод любого переданного ей объекта, но также будет передаватьTrueк параметруlast_name_first. Теперь вы получите список фамилий, а затем имена всех объектовMusician.Точно так же, как при использовании
attrgetter()для получения атрибутов, объекты, передаваемые вmethodcaller(), могут быть как встроенными, так и пользовательскими. Они должны содержать только метод, который вы хотите вызвать.Часто задаваемые вопросы
Теперь вы знакомы с назначением и использованием модуля Python
operator. Вы уже многое изучили, и здесь вы найдете несколько вопросов и ответов, которые обобщают наиболее важные концепции, рассмотренные в этом руководстве.Вы можете использовать эти вопросы, чтобы проверить свое понимание или обобщить и закрепить то, что вы только что узнали. После каждого вопроса вы найдете краткое объяснение, спрятанное в разборном разделе. Нажмите на переключатель Показать/скрыть, чтобы открыть ответ. Пора переходить к делу!
Функции модуля
operatorне заменяют встроенные операторы Python. Вместо этого вы используете многие функции, эквивалентные операторамoperatorв функциональном программировании, передавая их функциям более высокого порядка. Это невозможно с помощью встроенных операторов, поскольку вы не можете легко получить ссылку на них.
Модуль
operatorтакже предоставляет функции более высокого порядкаattrgetter(),itemgetter()иmethodcaller(). Вы можете передать функции, которые возвращают эти три параметра, многим обычным функциям, включаяsorted(),max(), иmin().
В отличие от встроенных операторов, вы можете сериализовать функцию модуля
operator, что замечательно, посколькуpickleмодуль является одним из наиболее эффективных способов сохранения данных. Недостатком является то, что он обычно не справляется с функциями. Однако вы можете использовать его сoperatorфункциями модуля.
Встроенные операторы Python будут превосходить как версии модуля
operator, так и лямбда-выражения, в то время как функцииoperatorбудут превосходить лямбда-выражения. Если вы сделаете один вызов лямбда-кода или эквивалентного модуляoperator, то вы не заметите большой разницы. Однако при повторных вызовах разница становится существенной. Функции модуляoperatorне заменяют встроенных операторов, когда это все, что вам нужно.
Использование функций
operatorможет сделать ваш код более читабельным, чем иногда приводящие в замешательство эквиваленты лямбда-функций.У вас есть интересный пример использования модуля
operator? Если у вас есть продуманный вариант использования, почему бы не поделиться им с коллегами-программистами? Не стесняйтесь добавить его в качестве комментария ниже.Получите свой код: Нажмите здесь, чтобы загрузить бесплатный пример кода, который
<статус завершения article-slug="python-operator-module" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-operator-module/bookmark/" данные-api-article-завершение-status-url="/api/версия 1/статьи/python-оператор-модуль/завершение_статуса/"> статус завершения> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0A Работа с модулем Python operator" email-subject="Статья о Python для вас" twitter-text="Интересная статья о Python от @realpython:" url="https://realpython.com/python-operator-module /" url-title="Работа с модулем Python operator"> кнопка поделиться> Back to Topoperatorпоказывает, как использовать модуль <<<5> Python>>.