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

  • Таблица 17.1.

  • Отраженные числовые dunder-методы

  • Таблица 17.2.

  • Dunder-методы присваивания на месте (in-place)

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


    Скачать 7.85 Mb.
    НазваниеЧистыйкод дляпродолжающи х
    Дата13.05.2023
    Размер7.85 Mb.
    Формат файлаpdf
    Имя файлаPython_Chisty_kod_dlya_prodolzhayuschikh_2022_El_Sveygart.pdf
    ТипДокументы
    #1127485
    страница39 из 40
    1   ...   32   33   34   35   36   37   38   39   40

    import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10)
    >>> tipJar = wizcoin.WizCoin(0, 0, 37)
    >>> purse + tipJar
    Traceback (most recent call last):
    File "", line 1, in
    TypeError: unsupported operand type(s) for +: 'WizCoin' and 'WizCoin'
    Вместо того чтобы писать метод addWizCoin()
    для класса
    WizCoin
    , вы можете ис- пользовать dunder-метод
    __add__()
    и заставить объекты
    WizCoin работать с опера- тором
    +
    . Добавьте следующий фрагмент в конец файла wizcoin.py
    :

    370
    Глава 17.ООП в Python: свойства и dunder-методы
    --snip-- def __add__(self, other):

    """Суммирует денежные величины двух объектов WizCoin."""
    if not isinstance(other, WizCoin):

    return NotImplemented return WizCoin(other.galleons + self.galleons, other.sickles +

    self.sickles, other.knuts + self.knuts)
    Когда объект
    WizCoin стоит в левой части оператора
    +
    , Python вызывает метод
    __add__()

    и передает значение в правой части оператора
    +
    в параметре other
    (Параметру можно назначить любое имя, но традиционно используется имя other
    .)
    Помните, что методу
    __add__()
    можно передать объект любого типа, поэтому в ме- тод необходимо включить проверку типа

    . Например, бессмысленно прибавлять целое число или число с плавающей точкой к объекту
    WizCoin
    , потому что мы не знаем, к какому атрибуту его следует прибавить — galleons
    , sickles или knuts
    Метод
    __add__()
    создает новый объект
    WizCoin с атрибутами, равными сумме galleons
    , sickles и knuts объектов self и other

    . Так как все три атрибута содержат целые числа, их можно суммировать оператором
    +
    . Теперь оператор
    +
    для класса
    WizCoin перегружен, и его можно использовать с объектами
    WizCoin
    Перегрузка оператора
    +
    позволяет создавать более понятный код. Например, вве- дите следующий фрагмент в интерактивной оболочке:
    >>> import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10) # Создает объект WizCoin.
    >>> tipJar = wizcoin.WizCoin(0, 0, 37) # Создает другой объект WizCoin.
    >>> purse + tipJar # Создает новый объект WizCoin с суммой.
    WizCoin(2, 5, 47)
    Если в other передается объект неправильного типа, dunder-метод должен не вы- давать исключение, а возвращать встроенное значение
    NotImplemented
    . Например, в следующем фрагменте в other передается целое число:
    >>> import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10)
    >>> purse + 42 # Объекты WizCoin и целые числа не могут суммироваться.
    Traceback (most recent call last):
    File "", line 1, in
    TypeError: unsupported operand type(s) for +: 'WizCoin' and 'int'
    Возвращение
    NotImplemented приказывает Python попробовать вызвать другие ме- тоды для выполнения этой операции (за подробностями обращайтесь к подразделу
    «Отраженные числовые dunder-методы» этой главы). Во внутренней реализации
    Python вызывает метод
    __add__()
    со значением
    42
    для параметра other
    , но этот метод тоже возвращает
    NotImplemented
    , что заставляет Python выдать исключение
    TypeError

    Dunder-методы Python
    371
    И хотя операции прибавления или вычитания целых чисел из объектов
    WizCoin не имеют смысла, будет разумно разрешить умножение объектов
    WizCoin на положи- тельные целые числа; для этого определяется dunder-метод
    __mul__()
    . Добавьте следующий фрагмент в конец файла wizcoin.py
    :
    --snip-- def __mul__(self, other):
    """Умножает количество монет на неотрицательное целое число."""
    if not isinstance(other, int):
    return NotImplemented if other < 0:
    # Умножение на отрицательное целое число приведет
    # к отрицательному количеству монет, что недопустимо.
    raise WizCoinException('cannot multiply with negative integers')
    return WizCoin(self.galleons * other, self.sickles * other, self.knuts * other)
    Этот метод
    __mul__()
    позволяет умножать объекты
    WizCoin на положительные це- лые числа. Если other является целым числом, то это тип данных, которые ожидает получить метод
    __mul__()
    , и возвращать
    NotImplemented не нужно. Но если целое число — отрицательное, то умножение объекта
    WizCoin на него приведет к отрица- тельному количеству монет в объекте
    WizCoin
    . Так как это противоречит нашему подходу к проектированию класса, мы выдаем исключение
    WizCoinException с со- держательным сообщением об ошибке.
    ПРИМЕЧАНИЕ
    Не изменяйте объект self в математическом dunder-методе. Метод всегда должен созда- вать и возвращать новый объект. Оператор + и другие числовые операторы всегда должны давать в результате новый объект, вместо того чтобы изменять значение объекта на месте.
    Чтобы увидеть dunder-метод
    __mul__()
    в действии, введите следующий фрагмент в интерактивной оболочке:
    >>> import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10) # Создать объект WizCoin.
    >>> purse * 10 # Умножить объект WizCoin на целое число.
    WizCoin(20, 50, 100)
    >>> purse * -2 # Умножение на отрицательное целое число приводит к ошибке.
    Traceback (most recent call last):
    File "", line 1, in
    File "C:\Users\Al\Desktop\wizcoin.py", line 86, in __mul__
    raise WizCoinException('cannot multiply with negative integers')
    wizcoin.WizCoinException: cannot multiply with negative integers
    В табл. 17.1 приведен полный список числовых dunder-методов. Как правило, вам не требуется полный список этих методов для ваших классов. Вы сами решаете, какие методы для вас актуальны.

    372
    Глава 17.ООП в Python: свойства и dunder-методы
    Таблица 17.1. Числовые dunder-методы
    Dunder-метод
    Операция
    Оператор или встроенная
    функция
    __add__()
    Сложение
    +
    __sub__()
    Вычитание
    -
    __mul__()
    Умножение
    *
    __matmul__()
    Матричное умножение
    (в Python 3.5 и выше)
    @
    __truediv__()
    Деление
    /
    __floordiv__()
    Целочисленное деление
    //
    __mod__()
    Остаток
    %
    __divmod__()
    Деление с остатком divmod()
    __pow__()
    Возведение в степень
    **, pow()
    __lshift__()
    Сдвиг влево
    >>
    __rshift__()
    Сдвиг вправо
    <<
    __and__()
    Поразрядная операция AND
    &
    __or__()
    Поразрядная операция OR
    |
    __xor__()
    Поразрядная исключающая опе- рация OR
    ^
    __neg__()
    Отрицание
    Унарный
    -
    , как в
    -42
    __pos__()
    Тождество
    Унарный
    +
    , как в
    +42
    __abs__()
    Абсолютное значение (модуль)
    abs()
    __invert__()
    Поразрядное инвертирование


    __complex__()
    Комплексная форма числа complex()
    __int__()
    Целая форма числа int()
    __float__()
    Форма числа с плавающей точкой float()
    __bool__()
    Логическая форма bool()
    __round__()
    Округление round()
    __trunc__()
    Целая часть math.trunc()
    __floor__()
    Округление в меньшую сторону math.floor()
    __ceil__()
    Округление в большую сторону math.ceil()

    Dunder-методы Python
    373
    Некоторые из этих методов актуальны для нашего класса
    WizCoin
    . Попробуйте напи- сать собственные реализации методов
    __sub__()
    ,
    __pow__()
    ,
    __int__()
    ,
    __float__()
    и
    __bool__()
    . Пример реализации доступен на https://autbor.com/wizcoinfull. Полное описание числовых dunder-методов в документации Python доступно на https://
    docs.python.org/3/reference/datamodel.html#emulating-numeric-types.
    Числовые dunder-методы позволяют использовать объекты ваших классов со встроенными математическими операторами Python. Если вы пишете методы с именами вида multiplyBy()
    , convertToInt()
    и т. д., описывающими задачу, которая выполняется существующим оператором или встроенной функцией, используйте числовые dunder-методы (а также отраженные dunder-методы и dunder-методы присваивания на месте, описанные в следующих двух разделах).
    Отраженные числовые dunder-методы
    Python вызывает числовые dunder-методы, когда объект находится в левой части математического оператора. Но если объект располагается в правой части мате- матического оператора, то вызываются отраженные числовые dunder-методы (их также называют обратными числовыми dunder-методами).
    Отраженные числовые dunder-методы полезны, потому что программисты, ис- пользующие ваш класс, не всегда будут записывать объект в левой части оператора, что может привести к непредвиденному поведению. Для примера рассмотрим, что произойдет, если purse содержит объект
    WizCoin
    , а Python вычисляет выражение
    2
    *
    purse
    , когда purse находится в правой части оператора.
    1. Так как
    2
    является целым числом, вызывается метод
    __mul__()
    класса int
    , которому в параметре other передается purse
    2. Метод
    __mul__()
    класса int не знает, как обрабатывать объекты
    WizCoin
    , по- этому он возвращает
    NotImplemented
    3. Пока Python не выдает ошибку
    TypeError
    . Так как purse содержит объект
    WizCoin
    , вызывается метод
    __rmul__()
    класса
    WizCoin
    , которому в параметре other передается
    2 4. Если
    __rmul__()
    возвращает
    NotImplemented
    , Python выдает ошибку
    TypeError
    . В противном случае
    __rmul__()
    возвращает объект, полученный в результате вычисления выражения
    2
    *
    purse
    А вот выражение purse
    *
    2
    , в котором purse находится в левой части оператора, работает иначе.
    1. Так как purse содержит объект
    WizCoin
    , вызывается метод
    __mul__()
    класса
    WizCoin
    , которому в параметре other передается
    2

    374
    Глава 17.ООП в Python: свойства и dunder-методы
    2. Метод
    __mul__()
    создает новый объект
    WizCoin и возвращает его.
    3. Этот возвращенный объект — то, что возвращает выражение purse
    *
    2
    Числовые dunder-методы и отраженные числовые dunder-методы имеют одина- ковый код, если они обладают свойством коммутативности. Коммутативные операции (такие как сложение) дают одинаковый результат при записи в прямом и обратном направлении:
    3
    +
    2
    — то же самое, что
    2
    +
    3
    Но существуют и другие операции, которые коммутативными не являются:
    3

    2
    и
    2

    3
    дают разные результаты. Любая коммутативная операция может просто вы- зывать исходный числовой dunder-метод каждый раз, когда вызывается отраженный числовой dunder-метод. Например, добавьте следующий фрагмент в конец файла wizcoin.py
    , чтобы определить отраженный числовой dunder-метод для операции умножения:
    --snip-- def __rmul__(self, other):
    """Умножает количество монет на неотрицательное целое число."""
    return self.__mul__(other)
    Умножение целого числа на объект
    WizCoin коммутативно:
    2
    *
    purse
    — то же самое, что purse
    *
    2
    . Вместо того чтобы копировать и вставлять код из
    __mul__()
    , мы про- сто вызываем self.__mul__()
    и передаем параметр other
    После обновления файла wizcoin.py проверьте, как работает отраженный dunder- метод умножения. Для этого введите следующий фрагмент в интерактивную обо- лочку:
    >>> import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10)
    >>> purse * 10 # Вызывает __mul__() с параметром `other`, равным 10.
    WizCoin(20, 50, 100)
    >>> 10 * purse # Вызывает __rmul__() с параметром `other`, равным 10.
    WizCoin(20, 50, 100)
    Помните, что в выражении
    10
    *
    purse
    Python сначала вызывает метод
    __mul__()
    класса int
    , чтобы узнать, могут ли целые числа умножаться на объекты
    WizCoin
    Конечно, встроенный класс int ничего не знает о создаваемых нами классах, по- этому он возвращает
    NotImplemented
    . Это значение сигнализирует Python о том, чтобы следующим для обработки операции был вызван метод
    __rmul__()
    класса
    WizCoin
    (если он существует). Если вызовы
    __mul__()
    класса int и
    __rmul__()
    класса
    WizCoin вернут
    NotImplemented
    , Python выдает исключение
    TypeError
    Только объекты
    WizCoin способны суммироваться друг с другом. Это гарантирует, что метод
    __add__()
    первого объекта
    WizCoin обработает операцию, поэтому реали- зовать
    __radd__()
    не нужно. Например, в выражении purse
    +
    tipJar метод
    __add__()

    Dunder-методы Python
    375
    для объекта purse вызывается с передачей tipJar в параметре other
    . Так как этот вызов не возвращает
    NotImplemented
    , Python не пытается вызвать метод
    __radd__()
    объекта tipJar с передачей purse в параметре other
    В табл. 17.2 приведен полный список доступных отраженных dunder-методов.
    Таблица 17.2. Отраженные числовые dunder-методы
    Dunder-метод
    Операция
    Оператор или встроенная
    функция
    __radd__()
    Сложение
    +
    __rsub__()
    Вычитание
    -
    __rmul__()
    Умножение
    *
    __rmatmul__()
    Матричное умножение
    (в Python 3.5 и выше)
    @
    __rtruediv__()
    Деление
    /
    __rfloordiv__()
    Целочисленное деление
    //
    __rmod__()
    Остаток
    %
    __rdivmod__()
    Деление с остатком divmod()
    __rpow__()
    Возведение в степень
    **, pow()
    __rlshift__()
    Сдвиг влево
    >>
    __rrshift__()
    Сдвиг вправо
    <<
    __rand__()
    Поразрядная операция AND
    &
    __ror__()
    Поразрядная операция OR
    |
    __rxor__()
    Поразрядная исключающая операция OR
    ^
    Полное описание отраженных dunder-методов доступно в документации Python на
    https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types.
    Dunder-методы присваивания на месте (in-place)
    Числовые и отраженные dunder-методы всегда создают новые объекты (вместо из- менения объектов на месте). Dunder-методы присваивания на месте, вызываемые расширенными операторами присваивания (такими как
    +=
    и
    *=
    ), изменяют объект на месте без создания нового объекта. (У этого правила есть исключение, о котором я расскажу в конце раздела.) Имена таких dunder-методов начинаются с i
    (напри- мер,
    __iadd__()
    и
    __imul__()
    для операторов
    +=
    и
    *=
    соответственно).

    376
    Глава 17.ООП в Python: свойства и dunder-методы
    Например, при выполнении кода Python purse
    *=
    2
    мы не предполагаем, что метод
    __imul__()
    класса
    WizCoin создаст и вернет новый объект
    WizCoin с вдвое большим количеством монет, а затем присвоит его переменной purse
    . Вместо этого метод
    __imul__()
    изменяет существующий объект
    WizCoin в purse
    , чтобы он содержал вдвое большее количество монет. Это тонкое, но важное отличие, которое необхо- димо учитывать, если ваши классы должны перегружать расширенные операторы присваивания.
    Наши объекты
    WizCoin уже перегружают операторы
    +
    и
    *
    , поэтому определим dunder-методы
    __iadd__()
    и
    __imul__()
    , чтобы они также перегружали операторы
    +=
    и
    *=
    . В выражениях purse
    +=
    tipJar и purse
    *=
    2
    вызываются методы
    __iadd__()
    и
    __imul__()
    , при этом в параметре other передаются tipJar и
    2
    соответственно.
    Добавьте следующий фрагмент в конец файла wizcoin.py
    :
    --snip-- def __iadd__(self, other):
    """Прибавляет монеты из другого объекта WizCoin к этому объекту."""
    if not isinstance(other, WizCoin):
    return NotImplemented
    # Объект `self` изменяется на месте:
    self.galleons += other.galleons self.sickles += other.sickles self.knuts += other.knuts return self # Dunder-методы присваивания на месте
    # почти всегда возвращают self.
    def __imul__(self, other):
    """Умножает galleons, sickles и knuts этого объекта на неотрицательное целое число."""
    if not isinstance(other, int):
    return NotImplemented if other < 0:
    raise WizCoinException('cannot multiply with negative integers')
    # Класс WizCoin создает изменяемые объекты. НЕ СОЗДАВАЙТЕ новый
    # объект, как в следующем закомментированном коде:
    # return WizCoin(self.galleons * other, self.sickles * other,
    # self.knuts * other)
    # Объект `self` изменяется на месте:
    self.galleons *= other self.sickles *= other self.knuts *= other return self # Dunder-методы присваивания на месте
    # почти всегда возвращают self.
    Объекты
    WizCoin могут использовать оператор
    +=
    с другими объектами
    WizCoin и оператор
    *=
    с положительными целыми числами. Заметим, что после проверки

    Dunder-методы Python
    377
    параметра other методы присваивания на месте изменяют объект self
    , вместо того чтобы создавать новый объект
    WizCoin
    . Чтобы увидеть, как расширенные опера- торы присваивания изменяют объекты
    WizCoin на месте, введите следующий код в интерактивной оболочке:
    >>> import wizcoin
    >>> purse = wizcoin.WizCoin(2, 5, 10)
    >>> tipJar = wizcoin.WizCoin(0, 0, 37)
    >>> purse + tipJar

    WizCoin(2, 5, 46)

    >>> purse
    WizCoin(2, 5, 10)
    >>> purse += tipJar

    >>> purse
    WizCoin(2, 5, 47)
    >>> purse *= 10

    >>> purse
    WizCoin(20, 50, 470)
    Оператор
    +

    вызывает dunder-методы
    __add__()
    или
    __radd__()
    для создания и воз- вращения новых объектов

    . Исходные объекты, с которыми работал оператор
    +
    , остаются без изменений. Dunder-методы присваивания на месте

    и

    должны изменять объект на месте, при условии что объект является изменяемым (то есть это объект, значение которого может изменяться). Исключение делается для неиз- меняемых объектов, так как такие объекты по определению не могут изменяться.
    В таком случае dunder-метод присваивания на месте должен создать и вернуть новый объект, как и числовые и отраженные dunder-методы.
    Мы не сделали атрибуты galleons
    , sickles и knuts доступными только для чтения; это означает, что они могут изменяться. Таким образом, объекты
    WizCoin являются изменяемыми. Большинство классов, которые вы напишете, будут создавать изме- няемые объекты, поэтому вам стоит проектировать dunder-методы присваивания на месте, так чтобы они изменяли объект на месте.
    Если вы не реализуете dunder-метод присваивания на месте, Python вызовет число- вой dunder-метод. Например, если в классе
    WizCoin отсутствует метод
    __imul__()
    , выражение purse
    *=
    10
    вызовет
    __mul__()
    и присвоит возвращаемое значение пере- менной purse
    . Так как объекты
    WizCoin являются изменяемыми, это неожиданное поведение способно порождать коварные ошибки.
    1   ...   32   33   34   35   36   37   38   39   40


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