Главная страница
Навигация по странице:

  • СРАВНЕНИЯ ПОСЛЕДОВАТЕЛЬНОСТЕЙ

  • Таблица 17.3.

  • Python. Чистый код для продолжающих

  • Чистыйкод дляпродолжающи х


    Скачать 7.85 Mb.
    НазваниеЧистыйкод дляпродолжающи х
    Дата13.05.2023
    Размер7.85 Mb.
    Формат файлаpdf
    Имя файлаPython_Chisty_kod_dlya_prodolzhayuschikh_2022_El_Sveygart.pdf
    ТипДокументы
    #1127485
    страница40 из 40
    1   ...   32   33   34   35   36   37   38   39   40
    Dunder-методы сравнения
    Метод Python sort()
    и функция sorted()
    используют эффективный алгоритм сортировки, для выполнения которого достаточно простого вызова. Но если вы хотите сравнивать и сортировать объекты создаваемого вами класса, необходимо сообщить Python, как должны сравниваться два таких объекта — для этого нужно

    378
    Глава 17.ООП в Python: свойства и dunder-методы реализовать dunder-методы сравнения. За кулисами Python вызывает dunder- методы сравнения каждый раз, когда ваши объекты используются в выражениях с операторами сравнения
    <
    ,
    >
    ,
    <=
    ,
    >=
    ,
    ==
    и
    !=
    Прежде чем исследовать dunder-методы сравнения, рассмотрим шесть функций модуля operator
    , которые выполняют те же операции, что и шесть операций срав- нения. Наши dunder-методы сравнения будут вызывать эти функции. Введите следующий фрагмент в интерактивной оболочке:
    >>> import operator
    >>> operator.eq(42, 42) # "Равно"; то же, что 42 == 42
    True
    >>> operator.ne('cat', 'dog') # "Не равно"; то же, что 'cat' != 'dog'
    True
    >>> operator.gt(10, 20) # "Больше"; то же, что 10 > 20
    False
    >>> operator.ge(10, 10) # "Больше или равно"; то же, что 10 >= 10
    True
    >>> operator.lt(10, 20) # "Меньше"; то же, что 10 < 20
    True
    >>> operator.le(10, 20) # "Меньше или равно"; то же, что 10 <= 20
    True
    Модуль operator предоставляет версии операторов сравнения в виде функций.
    Их реализации очень просты. Например, можно написать собственную функцию operator.eq()
    всего в две строки:
    def eq(a, b):
    return a == b
    Реализация оператора сравнения в виде функции полезна, потому что, в отличие от операторов, функции можно передавать как аргументы при вызове других функций.
    Мы воспользуемся этой возможностью и реализуем вспомогательный метод для наших dunder-методов сравнения.
    Сначала добавьте следующий фрагмент в начало файла wizcoin.py
    . Эти команды импортирования открывают доступ к функциям модуля operator и позволяют проверить, является ли аргумент other нашего метода последовательностью, для чего он сравнивается с collections.abc.Sequence
    :
    import collections.abc import operator
    Затем добавьте следующий фрагмент в конец файла wizcoin.py
    :
    --snip-- def _comparisonOperatorHelper(self, operatorFunc, other):

    """A helper method for our comparison dunder methods."""

    Dunder-методы Python
    379
    if isinstance(other, WizCoin):

    return operatorFunc(self.total, other.total)
    elif isinstance(other, (int, float)):

    return operatorFunc(self.total, other)
    elif isinstance(other, collections.abc.Sequence):

    otherValue = (other[0] * 17 * 29) + (other[1] * 29) + other[2]
    return operatorFunc(self.total, otherValue)
    elif operatorFunc == operator.eq:
    return False elif operatorFunc == operator.ne:
    return True else:
    return NotImplemented def __eq__(self, other): # "Равно"
    return self._comparisonOperatorHelper(operator.eq, other)

    def __ne__(self, other): # "Не равно"
    return self._comparisonOperatorHelper(operator.ne, other)

    def __lt__(self, other): # "Меньше"
    return self._comparisonOperatorHelper(operator.lt, other)

    def __le__(self, other): # "Меньше или равно"
    return self._comparisonOperatorHelper(operator.le, other)

    def __gt__(self, other): # "Больше"
    return self._comparisonOperatorHelper(operator.gt, other)

    def __ge__(self, other): # "Больше или равно"
    return self._comparisonOperatorHelper(operator.ge, other)

    Наши dunder-методы сравнения вызывают метод
    _comparisonOperatorHelper()

    и передают соответствующую функцию из модуля operator для параметра operatorFunc
    . При вызове operatorFunc()
    вызывается функция, которая была передана в операторе operatorFunc
    — eq()

    , ne()

    , lt()

    , le()

    , gt()

    или ge()

    — из модуля operator
    . В противном случае нам пришлось бы продублировать код в
    _comparisonOperatorHelper()
    в каждом из шести dunder-методов сравнения.
    ПРИМЕЧАНИЕ
    Функции (или методы), получающие другие функции в аргументах (как _compa- risonOperatorHelper()), называются функциями высшего порядка.
    Теперь наши объекты
    WizCoin можно сравнивать с другими объектами
    WizCoin

    , с целыми числами и числами с плавающей точкой

    , а также со значениями-по- следовательностями из трех чисел, представляющими значения galleons
    , sickles и knuts

    . Чтобы увидеть эту возможность в действии, введите следующий фрагмент в интерактивной оболочке:
    >>> import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10) # Создать объект WizCoin.
    >>> tipJar = wizcoin.WizCoin(0, 0, 37) # Создать другой объект WizCoin.
    >>> purse.total, tipJar.total # Проверить значение в кнатах.

    380
    Глава 17.ООП в Python: свойства и dunder-методы
    (1141, 37)
    >>> purse > tipJar # Сравнение объектов WizCoin при помощи оператора.
    True
    >>> purse < tipJar
    False
    >>> purse > 1000 # Сравнение с целым числом.
    True
    >>> purse <= 1000
    False
    >>> purse == 1141
    True
    >>> purse == 1141.0 # Сравнение с числом с плавающей точкой.
    True
    >>> purse == '1141' # Объект WizCoin не равен никакому строковому значению.
    False
    >>> bagOfKnuts = wizcoin.WizCoin(0, 0, 1141)
    >>> purse == bagOfKnuts
    True
    >>> purse == (2, 5, 10) # Возможно сравнение с кортежем из 3 целых чисел.
    True
    >>> purse >= [2, 5, 10] # Возможно сравнение со списком из 3 целых чисел.
    True
    >>> purse >= ['cat', 'dog'] # Должно привести к ошибке.
    Traceback (most recent call last):
    File "", line 1, in
    File "C:\Users\Al\Desktop\wizcoin.py", line 265, in __ge__
    return self._comparisonOperatorHelper(operator.ge, other)
    File "C:\Users\Al\Desktop\wizcoin.py", line 237, in _
    comparisonOperatorHelper otherValue = (other[0] * 17 * 29) + (other[1] * 29) + other[2]
    IndexError: list index out of range
    Наш вспомогательный метод вызывает isinstance(other,
    collections.abc.
    Sequence)
    , чтобы проверить, имеет ли other тип данных последовательности, на- пример кортеж или список. Обеспечив возможность сравнения объектов
    WizCoin с последовательностями, можно писать код вида purse
    >=
    [2,
    5,
    10]
    для быстрого сравнения.
    Не существует отраженных dunder-методов сравнения (таких как
    __req__()
    или
    __rne__()
    ), которые вам было бы необходимо реализовать. Вместо этого
    __lt__()
    и
    __gt__()
    отражают друг друга,
    __le__()
    и
    __ge__()
    отражают друг друга, а
    __eq__()
    и
    __ne__()
    отражают сами себя. Дело в том, что следующие отношения остаются истинными независимо от значений в левой и правой части оператора:
    purse
    >
    [2,
    5,
    10]
    то же, что
    [2,
    5,
    10]
    <
    purse purse
    >=
    [2,
    5,
    10]
    то же, что
    [2,
    5,
    10]
    <=
    purse purse
    ==
    [2,
    5,
    10]
    то же, что
    [2,
    5,
    10]
    ==
    purse purse
    !=
    [2,
    5,
    10]
    то же, что
    [2,
    5,
    10]
    !=
    purse

    Dunder-методы Python
    381
    СРАВНЕНИЯ ПОСЛЕДОВАТЕЛЬНОСТЕЙ
    При сравнении двух объектов встроенных типов последовательностей (таких, как строки, списки или кортежи) Python назначает более высокий приори- тет более ранним элементам последовательности. Другими словами, более поздние элементы сравниваются только в том случае, если более ранние элементы имеют равные значения. Например, введите следующий фрагмент в интерактивной оболочке:
    >>> 'Azriel' < 'Zelda'
    True
    >>> (1, 2, 3) > (0, 8888, 9999)
    True
    Строка 'Azriel' предшествует строке 'Zelda' (то есть меньше нее), потому что 'A' предшествует 'Z'. Кортеж (1, 2, 3) следует после (0, 8888, 9999) (то есть больше него), потому что 1 больше 0. А теперь введите следующий фрагмент в интерактивной оболочке:
    >>> 'Azriel' < 'Aaron'
    False
    >>> (1, 0, 0) > (1, 0, 9999)
    False
    Строка 'Azriel' не предшествует 'Aaron', потому что, хотя элемент 'A' в 'Azriel' равен 'A' в 'Aaron', следующий элемент 'z' в 'Azriel' не предшествует 'a' в 'Aaron'. То же относится к кортежам (1, 0, 0) и (1, 0, 9999): первые два элемента каждого кортежа равны, поэтому третий элемент (0 и 9999 соот- ветственно) определяет, что (1, 0, 0) предшествует (1, 0, 9999).
    Таким образом, нам приходится принять проектировочное решение относи- тельно класса WizCoin. Должен ли объект WizCoin(0, 0, 9999) предшествовать объекту WizCoin(1, 0, 0) или же следовать после него? Если количество галле- онов более значимо, чем количество сиклей или кнатов, то объект WizCoin(0,
    0, 9999) должен предшествовать WizCoin(1, 0, 0). А может, объекты должны сравниваться на основании их значений кнатов? Тогда объект WizCoin(0, 0,
    9999) (равно 9999 кнатам) должен следовать после WizCoin(1, 0, 0) (равно
    493 кнатам). В программе wizcoin.py я решил использовать значение объекта в кнатах, потому что такое поведение соответствует принципам сравнения объектов WizCoin с целыми числами и числами с плавающей точкой. Вам придется неоднократно принимать подобные решения при проектировании собственных классов.

    382
    Глава 17.ООП в Python: свойства и dunder-методы
    После того как вы реализуете dunder-методы сравнения, функция Python sort()
    автоматически использует их для сортировки объектов. Введите следующий фраг- мент в интерактивной оболочке:
    >>> import wizcoin
    >>> oneGalleon = wizcoin.WizCoin(1, 0, 0) # 493 кната.
    >>> oneSickle = wizcoin.WizCoin(0, 1, 0) # 29 кнатов.
    >>> oneKnut = wizcoin.WizCoin(0, 0, 1) # 1 кнат.
    >>> coins = [oneSickle, oneKnut, oneGalleon, 100]
    >>> coins.sort() # Отсортировать по возрастанию.
    >>> coins
    [WizCoin(0, 0, 1), WizCoin(0, 1, 0), 100, WizCoin(1, 0, 0)]
    В табл. 17.3 приведен полный список доступных dunder-методов сравнения.
    Таблица 17.3. Dunder-методы сравнения и функции модулей operator
    Dunder-метод
    Операция
    Оператор сравнения Функция модуля
    operator
    __eq__()
    Равно
    ==
    operator.eq()
    __ne__()
    Не равно
    !=
    operator.ne()
    __lt__()
    Меньше
    <
    operator.lt()
    __le__()
    Меньше или равно
    <=
    operator.le()
    __gt__()
    Больше
    >
    operator.gt()
    __ge__()
    Больше или равно
    >=
    operator.ge()
    Реализация этих методов доступна на https://autbor.com/wizcoinfull. Полное опи- сание dunder-методов сравнения доступно в документации Python на https://docs.
    python.org/3/reference/datamodel.html#object.__lt__.
    Dunder-методы сравнения позволяют использовать операторы сравнения Python с объектами вашего класса (вместо того, чтобы создавать ваши собственные мето- ды). Если вы создаете методы с именами вида equals()
    или isGreaterThan()
    , это не питонический стиль и признак того, что вам стоит использовать dunder-методы сравнения.
    Итоги
    В языке Python объектно-ориентированные средства реализуются не так, как в других языках (например, Java или C++). Вместо явного определения

    Итоги
    383
    getter- и setter-методов в Python используются свойства, которые позволяют про- верять атрибуты или делать их доступными только для чтения.
    Python также позволяет перегружать операторы при помощи dunder-методов, имена которых начинаются и заканчиваются двойными символами подчеркивания
    __
    Основные математические операторы перегружаются числовыми и отраженными числовыми dunder-методами. Эти методы дают возможность использовать встро- енные операторы Python для работы с объектами созданных вами классов. Если методы не способны обработать тип данных объекта в другой части оператора, они возвращают встроенное значение
    NotImplemented
    . Такие методы создают и возвра- щают новые объекты, тогда как dunder-методы присваивания на месте (которые перегружают расширенные операторы присваивания) изменяют объект на месте.
    Dunder-методы сравнения не только реализуют шесть операторов сравнения Python для объектов, но и позволяют функции Python sort()
    сортировать объекты ваших классов. Для реализации этих dunder-методов можно воспользоваться функциями eq()
    , ne()
    , lt()
    , le()
    , gt()
    и ge()
    модуля operator
    Свойства и dunder-методы помогают писать классы, которые хорошо читаются и по- следовательно работают. Они избавят вас от необходимости писать значительный объем шаблонного кода, что обязательно в других языках (таких как Java). Если вам захочется больше узнать о написании питонического кода, более подробно о некоторых концепциях этой главы (и не только) рассказано в паре докладов
    Рэймонда Хеттингера (Raymond Hettinger) на PyCon: «Transforming Code into
    Beautiful, Idiomatic Python» (https://youtu.be/OSGv2VnC0go/) и «Beyond PEP 8 —
    Best Practices for Beautiful, Intelligible Code» (https://youtu.be/wf-BqAjZb8M/).
    Об эффективном использовании Python еще можно сказать очень много. В кни- гах «Fluent Python» (O’Reilly Media, 2021) Лучано Рамальо (Luciano Ramalho)
    1
    и «Effective Python» (Addison-Wesley Professional, 2019) Бретта Слаткина (Brett
    Slatkin)
    2
    более подробно рассказывается о синтаксисе и передовых практиках
    Python. Эти книги стоит прочитать каждому, кто хочет больше знать о Python.
    1
    Рамальо Л. Python. К вершинам мастерства.
    2
    Слаткин Б. Секреты Python. 59 рекомендаций по написанию эффективного кода.

    Эл Свейгарт
    Python. Чистый код для продолжающих
    Перевел с английского Е. Матвеев
    Руководитель дивизиона
    Ю. Сергиенко
    Ведущий редактор
    Е. Строганова
    Литературный редактор
    Ю. Леонова
    Художественный редактор
    В. Мостипан
    Корректоры
    С. Беляева, М. Молчанова
    Верстка
    Л. Егорова
    Изготовлено в России. Изэготовитель: ООО «Прогресс книга».
    Место нахождения и фактический адрес: 194044, Россия, г. Санкт-Петербург,
    Б. Сампсониевский пр., д. 29А, пом. 52. Тел.: +78127037373.
    Дата изготовления: 07.2022. Наименование: книжная продукция. Срок годности: не ограничен.
    Налоговая льгота — общероссийский классификатор продукции ОК 034-2014, 58.11.12 — Книги печатные профессиональные, технические и научные.
    Импортер в Беларусь: ООО «ПИТЕР М», 220020, РБ, г. Минск, ул. Тимирязева, д. 121/3, к. 214, тел./факс: 208 80 01.
    Подписано в печать 27.05.22. Формат 70×100/16. Бумага офсетная. Усл. п. л. 30,960. Тираж 1000. Заказ 0000.
    1   ...   32   33   34   35   36   37   38   39   40


    написать администратору сайта