Как переопределить оператор в python
Перейти к содержимому

Как переопределить оператор в python

  • автор:

Перегрузка операторов в Python

Перегрузка операторов в Python – это возможность с помощью специальных методов в классах переопределять различные операторы языка. Имена таких методов включают двойное подчеркивание спереди и сзади.

Под операторами в данном контексте понимаются не только знаки +, -, *, /, обеспечивающие операции сложения, вычитания и др., но также специфика синтаксиса языка, обеспечивающая операции создания объекта, вызова объекта как функции, обращение к элементу объекта по индексу, вывод объекта и другое.

Мы уже использовали ряд методов перегрузки операторов. Это

  • __init__() – конструктор объектов класса, вызывается при создании объектов Конструктор класса и его вызов
  • __del__() – деструктор объектов класса, вызывается при удалении объектов
  • __str__() – преобразование объекта к строковому представлению, вызывается, когда объект передается функциям print() и str()
  • __add__() – метод перегрузки оператора сложения, вызывается, когда объект участвует в операции сложения будучи операндом с левой стороны __add__ - метод перегрузки оператора сложения
  • __setattr__() – вызывается, когда атрибуту объекта выполняется присваивание __setattr__ - метод перегрузки оператора присваивания

В Python много других методов перегрузки операторов. В этом уроке рассмотрим еще несколько.

На самом деле перегрузка большинства операторов в пользовательских классах используется не так часто. Но сам факт наличия такой особенности объектно-ориентированного программирования требует отдельного рассмотрения темы.

Возможность перегрузки операторов обеспечивает схожесть пользовательского класса со встроенными классами Python. Ведь все встроенные типы данных Питона – это классы. В результате все объекты могут иметь одинаковые интерфейсы. Так если ваш класс предполагает обращение к элементу объекта по индексу, например a[0] , то это можно обеспечить.

Пусть будет класс-агрегат B , содержащий в списке объекты класса A :

class A: def __init__(self, arg): self.arg = arg def __str__(self): return str(self.arg) class B: def __init__(self, *args): self.aList = [] for i in args: self.aList.append(A(i)) group = B(5, 10, 'abc') 

Чтобы получить элемент списка, несомненно, мы можем обратиться по индексу к полю aList :

print(group.aList[1])

Однако куда интереснее извлекать элемент по индексу из самого объекта, а не из его поля:

class B: def __init__(self, *args): self.aList = [] for i in args: self.aList.append(A(i)) def __getitem__(self, i): return self.aList[i] group = B(5, 10, 'abc') print(group.aList[1]) # выведет 10 print(group[0]) # 5 print(group[2]) # abc

Это делает объекты класса B похожими на объекты встроенных в Python классов-последовательностей (списков, строк, кортежей). Здесь метод __getitem__ перегружает операцию извлечения элемента по индексу. Другими словами, этот метод вызывается, когда к объекту применяется операция извлечения элемента: объект[индекс] .

Бывает необходимо, чтобы объект вел себя как функция. Это значит, если у нас есть объект a , то мы можем обращаться к нему в нотации функции, то есть ставить после него круглые скобки и даже передавать в них аргументы:

a = A() a() a(3, 4)

Метод __call__ автоматически вызывается, когда к объекту обращаются как к функции. Например, здесь во второй строке произойдет вызов метода __call__() некогоКласса :

объект = некийКласс() объект([возможные аргументы])
class Changeable: def __init__(self, color): self.color = color def __call__(self, newcolor): self.color = newcolor def __str__(self): return "%s" % self.color canvas = Changeable("green") frame = Changeable("blue") canvas("red") frame("yellow") print(canvas, frame)

В этом примере с помощью конструктора класса при создании объектов устанавливается их цвет. Если требуется его поменять, то достаточно обратиться к объекту как к функции и в качестве аргумента передать новый цвет. Такой обращение автоматически вызовет метод __call__() , который, в данном случае, изменит атрибут color объекта.

В Python кроме метода __str__ есть схожий с ним по поведению, но более «низкоуровневый» метод __repr__ . Оба метода должны возвращать строку.

Если в классе есть только метод __str__() , то при обращении к объекту в интерпретаторе без функции print() , он не будет вызываться:

>>> class A: . def __str__(self): . return "This is object of A" . >>> a = A() >>> print(a) This is object of A >>> a >>> str(a) 'This is object of A' >>> repr(a) ''

Метод __str__() вызывается при попытке преобразования объекта в строку с помощью встроенной функции str() . А функция print() так устроена, что сама вызывает str() для своих аргументов.

В Python есть встроенная функция repr() , которая также как str() преобразует объект в строку. Но «сырую» строку. Что это значит, попробуем понять с помощью примера:

>>> a = '3 + 2' >>> b = repr(a) >>> a '3 + 2' >>> b "'3 + 2'" >>> eval(a) 5 >>> eval(b) '3 + 2' >>> c = "Hello\nWorld" >>> d = repr(c) >>> c 'Hello\nWorld' >>> d "'Hello\\nWorld'" >>> print(c) Hello World >>> print(d) 'Hello\nWorld'

Функция eval() преобразует переданную строку в программный код, который тут же выполняется. Функция print() выполняет переход на новую строку, если встречает символ \n . Функция repr() выполняет действия, направленные на своего рода защиту строки от интерпретации, оставляет ее «сырой», т. е. в исходном виде. Еще раз:

>>> c = "Hello\nWorld" >>> c # аналог print(repr(c)) 'Hello\nWorld' >>> print(c) # аналог print(str(c)) Hello World

Если для вашего класса различия между сырым и обычным строковым представлением экземпляров не важны, желательно определять только метод __repr__ . Он будет вызываться для всех случаев преобразования к строке:

>>> class A: . def __repr__(self): . return "It's obj of A" . >>> a = A() >>> a It's obj of A >>> repr(a) "It's obj of A" >>> str(a) "It's obj of A" >>> print(a) It's obj of A

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

Практическая работа

Напишите класс Snow по следующему описанию.

В конструкторе класса инициируется поле, содержащее количество снежинок, выраженное целым числом.

Класс включает методы перегрузки арифметических операторов: __add__ – сложение, __sub__ – вычитание, __mul__ – умножение, __truediv__ – деление. В классе код этих методов должен выполнять увеличение или уменьшение количества снежинок на число n или в n раз. Метод __truediv__ перегружает обычное / , а не целочисленное // деление. Однако пусть в методе происходит округление значения до целого числа.

Класс включает метод make_snow , который помимо self принимает число снежинок в ряду и возвращает строку вида «*****\n*****\n*****…» , где количество снежинок между ‘\n’ равно переданному аргументу, а количество рядов вычисляется, исходя из общего количества снежинок.

Вызов экземпляра класса Snow в нотации функции с одним аргументом, должен приводить к перезаписи значения поля, в котором хранится количество снежинок, на переданное в качестве аргумента значение.

Курс с примерами решений практических работ:
pdf-версия

X Скрыть Наверх

Объектно-ориентированное программирование на Python

Перегрузка оператора + для сложения своего объекта и чисел

Хочу осуществить возможность перегрузить оператор +, таким способом, что бы возможно было к объекту добавлять объект, и к объекту добавлять число.

 class Fff: def __init__(self,x,y): self.x = x self.y = y def __add__(self,obj): return Fff(self.x + obj.x,self.y + obj.y) def __add__(self,v): return Fff(self.x + v,self.y + v) def show(self): print self.x,' : ',self.y f1 = Fff(1,2) f2 = Fff(5,5) result = f1 + f2 result2 = f1 + 2 result.show() result2.show() 

Отслеживать
52.2k 11 11 золотых знаков 108 108 серебряных знаков 311 311 бронзовых знаков
задан 22 фев 2017 в 21:56
Alabama Funny Alabama Funny
413 4 4 серебряных знака 11 11 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

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

def __add__(self, arg): if isinstance(arg, numbers.Number): return Fff(self.x + arg, self.y + arg) else: return Fff(self.x + arg.x, self.y + arg.y) 

Отслеживать
ответ дан 22 фев 2017 в 22:06
8,552 4 4 золотых знака 29 29 серебряных знаков 53 53 бронзовых знака

можно реализовать метод __add__ как _Fff_add(arg, fff) функцию и применить к ней @functools.singledispatch для разных типов — перегрузка для бедных. Единственное достоинство (что также можно недостатком считать), это то что можно поддержку для дополнительных типов, вне Fff добавлять (даже в другом модуле).

23 фев 2017 в 17:08

вместо type(arg) is Type лучше isinstance(arg, Type) если нет специальных обстоятельств первый вариант предпочитать. К примеру, isinstance(arg, numbers.Number) (что для int, float, итд работает)

Перегрузка операторов в Python

Магические (также называемые dunder как аббревиатура от двойного подчеркивания) методы в Python имеют ту же цель, что и перегрузка операторов в других языках. Они позволяют классу определять свое поведение, когда он используется в качестве операнда в выражениях унарных или бинарных операторов. Они также служат реализациями, вызываемыми некоторыми встроенными функциями.

Рассмотрим эту реализацию двумерных векторов.

import math class Vector(object): # instantiation def __init__(self, x, y): self.x = x self.y = y # unary negation (-v) def __neg__(self): return Vector(-self.x, -self.y) # addition (v + u) def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) # subtraction (v - u) def __sub__(self, other): return self + (-other) # equality (v == u) def __eq__(self, other): return self.x == other.x and self.y == other.y # abs(v) def __abs__(self): return math.hypot(self.x, self.y) # str(v) def __str__(self): return ', >'.format(self) # repr(v) def __repr__(self): return 'Vector(, )'.format(self) 

Теперь можно , естественно , использовать экземпляры Vector класса в различных выражениях.

v = Vector(1, 4) u = Vector(2, 0) u + v # Vector(3, 4) print(u + v) # "" (implicit string conversion) u - v # Vector(1, -4) u == v # False u + v == v + u # True abs(u + v) # 5.0 

Обработка невыполненного поведения

Если ваш класс не реализует конкретный перегруженный оператор для типов аргументов , предоставленных, он должен return NotImplemented (обратите внимание , что это специальная константа , не то же самое , как NotImplementedError ). Это позволит Python попробовать другие методы, чтобы заставить работу работать:

Когда NotImplemented возвращается, интерпретатор будет пытаться отраженную операцию на другой тип или какой — либо другой запасной вариант, в зависимости от оператора. Если все попытки операции возвращают NotImplemented , интерпретатор выдаст соответствующее исключение.

Например, если x + y , если x.__add__(y) возвращает невыполненным, y.__radd__(x) попытка вместо этого.

class NotAddable(object): def __init__(self, value): self.value = value def __add__(self, other): return NotImplemented class Addable(NotAddable): def __add__(self, other): return Addable(self.value + other.value) __radd__ = __add__

Как это отражается метод , который мы должны реализовать __add__ и __radd__ получить ожидаемое поведение во всех случаях; к счастью, так как они оба делают одно и то же в этом простом примере, мы можем воспользоваться ярлыком.

>>> x = NotAddable(1) >>> y = Addable(2) >>> x + x Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'NotAddable' and 'NotAddable' >>> y + y >>> z = x + y >>> z >>> z.value 3

Перегрузка оператора

Ниже приведены операторы, которые могут быть перегружены в классах, а также необходимые определения методов и пример использования оператора в выражении.

NB Использование other в качестве имени переменной не является обязательным, но считается нормой.

Оператора Метод Выражение
Сложение + __add__(self, other) a1 + a2
Вычитание — __sub__(self, other) a1 — a2
Умножение * __mul__(self, other) a1 * a2
Умножение матриц __matmul__(self,other) a1 @ a2 (Python 3.5)
Деление / __div__(self, other) a1 / a2 (Python 2)
Деление / __truediv(self, other) a1 / a2 (Python 3)
Целочисленное деление // __floordiv__(self, other) a1 // a2
Модуло / Остаток __mod__(self, other) a1 % a2
Степень __pow__(self, other[, modulo]) a1 ** a2
Побитовый сдвиг влево

__lshift__(self, other) a1
Побитовый сдвиг вправо >> __rshift__(self, other) a1 >> a2
Побитовое сложение & __add__(self, other) a1 & a2
Побитовое исключающее ИЛИ ^ __xor__(self, other) a1 ^ a2
Побитовое | ИЛИ __or__(self, other) a1 | a2
Отрицание — __neg__(self) -a1
Положительное + __pos__(self) +a1
Побитовое НЕ ~ __invert__(self) ~a1
Меньше чем

__lt__(self, other) a1 < a2
Меньше или равно

__le__(self, other) a1
Равно == __eq__(self, other) a1 == a2
Не равно >= __ne__(self, other) a1 != a2
Больше чем > __gt__(self, other) a1 > a2
Больше или равно => __ge__(self, other) a1 >= a2
Индекс [index] __getitem__(self, index) a1[index]
Оператор в in __contains__(self, other) a2 in a2
Вызов функции (*args, . ) __call__(self, *args, **kwargs) a1(*args, **kwargs)

Необязательный параметр по modulo для __pow__ используется только в pow встроенной функции.

Каждый из методов , соответствующих бинарный оператор имеет соответствующий «правильный» метод , который начать с __r , например __radd__ :

 class A: def __init__(self, a): self.a = a def __add__(self, other): return self.a + other def __radd__(self, other): print("radd") return other + self.a A(1) + 2 # Out: 3 2 + A(1) # prints radd. Out: 3 

а также соответствующая версия Inplace, начиная с __i :

 class B: def __init__(self, b): self.b = b def __iadd__(self, other): self.b += other print("iadd") return self b = B(2) b.b # Out: 2 b += 1 # prints iadd b.b # Out: 3 

Поскольку в этих методах нет ничего особенного, многие другие части языка, части стандартной библиотеки и даже сторонние модули самостоятельно добавляют магические методы, такие как методы для приведения объекта к типу или проверки свойств объекта. Например, встроенная str() вызов функции объекта __str__ метод, если он существует. Некоторые из этих видов использования перечислены ниже.

Id Метод Выражение
Кастинг для int __int__(self) int(a1)
Абсолютная функция __abs__(self) abs(a1)
Кастинг для str __str__(self) str(a1)
Кастинг для unicode __unicode__(self) unicode(a1) python 2
Строковое представление __repr__(self) repr(a1)
Приведение к bool __nonzero__(self) bool(a1)
Форматирование строки __format__(self, formatstr) «Hi «.format(a1)
Хэшировние __hash__(self) hash(a1)
Длина __len__(self) len(a1)
Округление __round__(self) round(a1)
Обратное округление __reversed__(self) reversed(a1)
Floor __floor__(self) math.floor(a1)
Ceiling __ceil__(self) math.ceil(a1)

Есть также специальные методы __enter__ и __exit__ для менеджеров контекста, и многих других.

Перегрузка операторов

Python 3 логотип

Перегрузка операторов — один из способов реализации полиморфизма, когда мы можем задать свою реализацию какого-либо метода в своём классе.

Например, у нас есть два класса:

 В данном примере класс B наследует класс A, но переопределяет метод go, поэтому он имеет мало общего с аналогичным методом класса A.

Однако в python имеются методы, которые, как правило, не вызываются напрямую, а вызываются встроенными функциями или операторами.

Например, метод __init__ перегружает конструктор класса. Конструктор — создание экземпляра класса.

 

__new__(cls[, . ]) — управляет созданием экземпляра. В качестве обязательного аргумента принимает класс (не путать с экземпляром). Должен возвращать экземпляр класса для его последующей его передачи методу __init__.

__init__(self[, . ]) - как уже было сказано выше, конструктор.

__del__(self) - вызывается при удалении объекта сборщиком мусора.

__repr__(self) - вызывается встроенной функцией repr; возвращает "сырые" данные, использующиеся для внутреннего представления в python.

__str__(self) - вызывается функциями str, print и format. Возвращает строковое представление объекта.

__bytes__(self) - вызывается функцией bytes при преобразовании к байтам.

__format__(self, format_spec) - используется функцией format (а также методом format у строк).

__le__(self, other) - x ≤ y вызывает x.__le__(y).

__eq__(self, other) - x == y вызывает x.__eq__(y).

__ne__(self, other) - x != y вызывает x.__ne__(y)

__gt__(self, other) - x > y вызывает x.__gt__(y).

__ge__(self, other) - x ≥ y вызывает x.__ge__(y).

__hash__(self) - получение хэш-суммы объекта, например, для добавления в словарь.

__bool__(self) - вызывается при проверке истинности. Если этот метод не определён, вызывается метод __len__ (объекты, имеющие ненулевую длину, считаются истинными).

__getattr__(self, name) - вызывается, когда атрибут экземпляра класса не найден в обычных местах (например, у экземпляра нет метода с таким названием).

__setattr__(self, name, value) - назначение атрибута.

__delattr__(self, name) - удаление атрибута (del obj.name).

__call__(self[, args. ]) - вызов экземпляра класса как функции.

__len__(self) - длина объекта.

__getitem__(self, key) - доступ по индексу (или ключу).

__setitem__(self, key, value) - назначение элемента по индексу.

__delitem__(self, key) - удаление элемента по индексу.

__iter__(self) - возвращает итератор для контейнера.

__reversed__(self) - итератор из элементов, следующих в обратном порядке.

__contains__(self, item) - проверка на принадлежность элемента контейнеру (item in self).

Перегрузка арифметических операторов

__add__(self, other) - сложение. x + y вызывает x.__add__(y).

__sub__(self, other) - вычитание (x - y).

__mul__(self, other) - умножение (x * y).

__truediv__(self, other) - деление (x / y).

__floordiv__(self, other) - целочисленное деление (x // y).

__mod__(self, other) - остаток от деления (x % y).

__divmod__(self, other) - частное и остаток (divmod(x, y)).

__pow__(self, other[, modulo]) - возведение в степень (x ** y, pow(x, y[, modulo])).

__lshift__(self, other) - битовый сдвиг влево (x

__rshift__(self, other) - битовый сдвиг вправо (x >> y).

__and__(self, other) - битовое И (x & y).

__xor__(self, other) - битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (x ^ y).

__or__(self, other) - битовое ИЛИ (x | y).

__radd__(self, other),

__rsub__(self, other),

__rmul__(self, other),

__rtruediv__(self, other),

__rfloordiv__(self, other),

__rmod__(self, other),

__rdivmod__(self, other),

__rpow__(self, other),

__rlshift__(self, other),

__rrshift__(self, other),

__rand__(self, other),

__rxor__(self, other),

__ror__(self, other) - делают то же самое, что и арифметические операторы, перечисленные выше, но для аргументов, находящихся справа, и только в случае, если для левого операнда не определён соответствующий метод.

Например, операция x + y будет сначала пытаться вызвать x.__add__(y), и только в том случае, если это не получилось, будет пытаться вызвать y.__radd__(x). Аналогично для остальных методов.

__iadd__(self, other) - +=.

__isub__(self, other) - -=.

__imul__(self, other) - *=.

__itruediv__(self, other) - /=.

__ifloordiv__(self, other) - //=.

__imod__(self, other) - %=.

__ipow__(self, other[, modulo]) - **=.

__ilshift__(self, other) -

__irshift__(self, other) - >>=.

__iand__(self, other) - &=.

__ixor__(self, other) - ^=.

__ior__(self, other) - |=.

__neg__(self) - унарный -.

__pos__(self) - унарный +.

__abs__(self) - модуль (abs()).

__invert__(self) - инверсия (~).

__complex__(self) - приведение к complex.

__int__(self) - приведение к int.

__float__(self) - приведение к float.

__round__(self[, n]) - округление.

__enter__(self), __exit__(self, exc_type, exc_value, traceback) - реализация менеджеров контекста.

Рассмотрим некоторые из этих методов на примере двухмерного вектора, для которого переопределим некоторые методы:

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

Для вставки кода на Python в комментарий заключайте его в теги

  • Модуль csv - чтение и запись CSV файлов
  • Создаём сайт на Django, используя хорошие практики. Часть 1: создаём проект
  • Онлайн-обучение Python: сравнение популярных программ
  • Книги о Python
  • GUI (графический интерфейс пользователя)
  • Курсы Python
  • Модули
  • Новости мира Python
  • NumPy
  • Обработка данных
  • Основы программирования
  • Примеры программ
  • Типы данных в Python
  • Видео
  • Python для Web
  • Работа для Python-программистов
  • Сделай свой вклад в развитие сайта!
  • Самоучитель Python
  • Карта сайта
  • Отзывы на книги по Python
  • Реклама на сайте

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *