среда, 1 июля 2020 г.

ЛМНты Python, 66 - 70

>>> Ещё множества могут

С помощью расширенных операторов присваивания (или соответствующих им методов) можно присваивать левому множеству результат операции над ним и правым множеством:

>>> a, b = ({1, 2, 3}, {3, 4, 5})
>>> # объединение
... a |= b
>>> a.update(b)
>>> a
{1, 2, 3, 4, 5}
>>>
>>> a, b = ({1, 2, 3}, {3, 4, 5})
>>> # пересечение
... a &= b
>>> a.intersection_update(b)
>>> a
{3}
>>>
>>> a, b = ({1, 2, 3}, {3, 4, 5})
>>> # разность
... a -= b
>>> a.difference_update(b)
>>> a
{1, 2}
>>>
>>> a, b = ({1, 2, 3}, {3, 4, 5})
>>> # симметрическая разность
... a ^= b
>>> a.symmetric_difference_update(b)
>>> a
{1, 2, 3}

См. также лмнт Множества могут.

>>> Операторы как синтаксический сахар

Все операторы в Python реализуются через dunder-методы (от double underscore) с предопределенными именами. Например, арифметические операторы +, -, * и // (целочисленное деление) реализуются методами __add__, __sub__, __mul__ и __floordiv__, соответственно.

>>> 3 + 4
7
>>> (3).__add__(4)
7
>>> 5 - 1
4
>>> (5).__sub__(1)
4
>>> 3 * 3
9
>>> (3).__mul__(3)
9
>>> 5//3
1
>>> (5).__floordiv__(3)
1

Если вы хотите "складывать" объекты вашего класса при помощи оператора +, реализуйте метод __add__:

>>> class A(object):
...     def __init__(self, name):
...         self.name = name
...    
...     def __add__(self, other):
...         if not isinstance(other, A):
...             raise ValueError('неправильный операнд')
...         return self.name + '+' + other.name
...
>>> A('привет') + A('python')
'привет+python'
>>> A('привет') + 3.14
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __add__
ValueError: неправильный операнд

>>> Как множества

Те же операторы, что выполняют операции над множествами, выполняют побитовые (bitwise) операции над целыми числами. Аналогия между множествами и битовыми полями возникает, если принять, что единица в n-ном бите означает присутствие элемента n в множестве, а ноль – его отсутствие.

>>> # побитовое И
... 10 & 3
2
>>> bin(0b1010 & 0b0011)
'0b10'
>>> {3, 1} & {1, 0}
{1}
>>>
>>> # побитовое ИЛИ
... 10 | 3
11
>>> bin(0b1010 | 0b0011)
'0b1011'
>>> {3, 1} | {1, 0}
{0, 1, 3}
>>>
>>> # исключающее ИЛИ
... 10 ^ 3
9
>>> bin(0b1010 ^ 0b0011)
'0b1001'
>>> {3, 1} ^ {1, 0}
{0, 3}

И только оператор -, вычисляющий разность множеств, для чисел вычисляет арифметическую разность и не имеет отношения к побитовым операциям. "Побитовую разность" можно получить так:

>>> 10 & ~3
8
>>> bin(0b1010 & ~0b0011)
'0b1000'
>>> {3, 1} - {1, 0}
{3}

>>> Операторы сравнения

Операторы сравнения реализуются следующими dunder-методами:

>>> # равно
... 'a' == 'b'
False
>>> 'a'.__eq__('b')
False
>>>
>>> # не равно
... 'a' != 'b'
True
>>> 'a'.__ne__('b')
True
>>>
>>> # меньше
... 'a' < 'b'
True
>>> 'a'.__lt__('b')
True
>>>
>>> # меньше или равно
... 'a' <= 'b'
True
>>> 'a'.__le__('b')
True
>>>
>>> # больше
... 'a' > 'b'
False
>>> 'a'.__gt__('b')
False
>>>
>>> # больше или равно
... 'a' >= 'b'
False
>>> 'a'.__ge__('b')
False

Реализация метода __eq__ позволяет сравнивать ваши объекты между собой с помощью операторов == и !=:

>>> class A(object):
...     def __init__(self, name):
...         self.name = name
...    
...     def __eq__(self, other):
...         if not isinstance(other, A):
...             return NotImplemented
...         return self.name == other.name
...
>>> A('привет') == A('python')
False
>>> A('привет') != A('python')
True
>>> A('привет') == A('привет')
True

Но метода __eq__ недостаточно для сортировки ваших объектов – для этого нужно реализовать также другие методы сравнения.

>>> total_ordering

Вместо того, чтобы реализовывать в вашем классе все методы сравнения __eq__, __ne__, __le__, __lt__, __gt__, __ge__, достаточно реализовать только два из них и снабдить класс декоратором total_ordering из модуля functools. Методы, которые нужно реализовать: __eq__ и один из __gt__, __ge__, __lt__, __le__.

>>> from functools import total_ordering
>>>
>>> @total_ordering
... class A(object):
...     def __init__(self, name):
...         self.name = name
...
...     def __eq__(self, other):
...         if not isinstance(other, A):
...             return NotImplemented
...         return self.name == other.name
...
...     def __gt__(self, other):
...         if not isinstance(other, A):
...             return NotImplemented
...         return self.name > other.name
...
...     def __repr__(self):
...         return 'A(\'%s\')' % self.name
...

Декоратор добавил остальные методы сравнения:

>>> A('алоха') >= A('салют')
False
>>> A('алоха') <= A('салют')
True
>>> A('алоха') < A('салют')
True
>>> A('алоха') != A('салют')
True

Теперь можно отсортировать список объектов класса A:

>>> a = [A('ох'), A('эй'), A('ай')]
>>> b = list(sorted(a))
>>> b
[A('ай'), A('ох'), A('эй')]

Комментариев нет:

Отправить комментарий