Создание объекта dict, принимающего только целые и плавающие числа в качестве значений
В этом сценарии мы реализуем класс, который создает объекты-словари, принимающие только целые и плавающие значения.
При добавлении других типов данных, таких как строки, списки и кортежи, будет появляться исключение, указывающее пользователю, что пользовательский объект dict может принимать только целые и плавающие числа в качестве значений.
Для реализации этого процесса используются следующие методы:
__int__, __setitem__ и __str __
Для начала создаем пользовательский класс CustomIntFloat и передаем dict в список наследования аргументов. Это означает, что созданный объект будет вести себя как словарь, за исключением тех мест, в которых это поведение будет выборочно изменено.
Затем создаем метод __init__, чтобы сконструировать объект dict CustomIntFloat, который принимает ключ и значение в список аргументов, установленный в тип None по умолчанию. Таким образом, если пользователь создает объект класса CustomIntFloat без передачи ключа или значения, будет сгенерирован пустой dict. Данное условие гласит: если ключ не передан, то параметру ключа по умолчанию присваивается аргумент None, а пустой dict создается путем ссылки на объект CustomIntFloat с атрибутом класса empty_dict.
Если пользователь указывает ключ length и соответствующее значение, которое является экземпляром класса int или float, то ключ и значение будут установлены в объекте.
Наконец, если пользователь указывает несколько ключей и значений в качестве итерируемых в операторе else, то они будут заархивированы функцией zip и им будет присвоено имя переменной zipped. Выполняем цикл на zipped, чтобы проверить, имеет ли значение тип int или float. Если нет, то будет сгенерировано пользовательское исключение CustomIntFloatError.
Класс исключения CustomIntFloatError и метод __str__
При генерации исключения CustomIntFloatError создается экземпляр класса CustomIntFloatError.
Таким образом, этот пользовательский класс исключений нуждается в помощи magic-методов __init__ и __str__. Созданный экземпляр принимает переданное значение и устанавливает его в качестве значения атрибута в классе CustomIntFloatError.
Это означает, что при появлении сообщения об ошибке значение, переданное в __init__ объекта CustomIntFloat, может быть установлено как атрибут (self.value) в классе CustomIntFloatError и с легкостью проверено.
Если ввод неверный, то появляется исключение CustomIntFloatError, а объект не создается. Сообщение об ошибке информирует пользователя о том, что допустимыми являются только целые и плавающие значения.
Аналогичным образом при попытке создать экземпляр объекта z (который был закомментирован) с несколькими ключами и значениями, возникает то же исключение, информирующее пользователя о том, что ‘three’ не является допустимым вводом.
# z = CustomIntFloat(key=['a', 'b', 'c'], value=[1, 2, 'three'])
z = CustomIntFloat(key=['a', 'b', 'c'], value=[1, 2, 3])
Метод __setitem__
__setitem__ — это magic-метод, который вызывается при установке ключа и значения в словаре. Если после создания объекта CustomIntFloat пользователь попытается добавить значение, которое не относится к типу int или float, появится то же исключение CustomIntFloatError. Ниже показано, как установить ключ и значение:
x = CustomIntFloat('a', 1)
print(type(x))
x['b'] = 2.1
print(x)
# x['c'] = 'Three'
В результате недопустимого ввода возникает исключение CustomIntFloatError:
Исходный код:
class CustomIntFloatError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return self.value + ' is not valid\nOnly Integers and floats are valid values \nin CustomIntFloat(dict) '
class CustomIntFloat(dict):
empty_dict = {}
def __init__(self, key=None, value=None):
if key is None:
CustomIntFloat.empty_dict = {}
elif len(key) == 1 and isinstance(value, (int, float)):
dict.__setitem__(self, key, value)
else:
zipped = zip(key, value)
for tup in zipped:
if isinstance(tup[1], (int, float)):
dict.__setitem__(self, tup[0], tup[1])
else:
raise CustomIntFloatError(tup[1])
def __setitem__(self, key, value):
if not isinstance(value, (int, float)):
raise CustomIntFloatError(value)
return dict.__setitem__(self, key, value)
Обзор класса CustomIntFloat
С помощью наследования через такие встроенные классы, как dict, можно настраивать поведение через повторную реализацию magic-методов. У этого подхода есть множество преимуществ.
Стоит отметить, что пользователю не нужно изучать новый синтаксис. Он может добавить ключ и значение к объекту dict CustomIntFloat привычным образом. Единственным отличием является выбор допустимых значений типа int и float. Если пользователь указывает любой другой тип, то сообщение об ошибке информирует его об этом и указывает допустимые типы значений.
Сразу несколько методов
Примеры с использованием математических операторов
__sub__, __add__ и __mul__ (с пользовательским __repr__)
С помощью magic-методов можно также воспользоваться математическими операторами в Python. Рассмотрим на примере таких методов, как __add__, __sub__ и __mul__ в созданном нами пользовательском объекте.
Такие операторы, как +, -, / и *, являются полиморфными методами. Как показано ниже, знак плюса (+) является полиморфным и может использоваться для объединения строк, суммирования целых чисел и комбинирования списков. Это возможно благодаря тому, что такие типы, как str, list и int, обладают методом add в соответствующих классах. Python просто преобразует знак + в вызов метода __add__ для объекта, который его вызвал (см. примеры ниже).
Это означает, что при включении метода __add__ в класс можно воспользоваться знаком + в объектах.
Применение magic-методов оператора в классе
Создаем класс NumOperations, который генерирует объекты NumOperations. Когда пользователь этого класса передает список в список аргументов __init__, он устанавливается в качестве атрибута в объекте NumOperations и получает название .math_list.
После создания объекта(ов) NumOperations можно с легкостью использовать magic-методы для работы с ними и передачи математической операции.
Например, magic-метод __sub__ принимает 2 объекта NumOperations, объединяет их списки и просматривает другие соответствующие им списки кортежей. Второй элемент в кортеже вычитается из первого, и это значение добавляется в новый список minuslst и передается в качестве аргумента в конструктор NumOperations.
Теперь он возвращает новый объект NumOperations.
Эта передача выполняется по методу __sub__. Это означает, что можно воспользоваться оператором минус (-).
Magic-метод __repr__ реализуется повторно, чтобы возвращать представление строки списка, установленное в новом объекте. Он был изменен, поэтому когда пользователь печатает выходные данные двух объектов NumOperations, результат будет соответствовать ожиданиям.
Ниже представлен список, где элементы были вычтены друг из друга:
[90, 81, 72, 63, 54].
Методы __add__ и __mul__ реализуются аналогично __sub__, однако используют списковое включение для сокращения количества строк кода.
Такое поведение при передаче аналогично таким пакетам анализа данных, как Pandas и Numpy.
Методы __add__ и __mul__ также предназначены для работы с двумя объектами NumOperations. Это означает, что пользователь может воспользоваться оператором плюс + и умножением *. Как видно из приведенного ниже примера, q является результатом x * y, который возвращает новый объект NumOperations. При вводе q мы получаем представление строки операции передачи в виде списка.
Исходный код доступен по ссылке на GitHub gist:
class NumOperations(object):
def __init__(self, math_list):
self.math_list = math_list
def __sub__(self, other):
minuslst = []
zipped = zip(self.math_list, other.math_list)
for tup in zipped:
minuslst.append(tup[0] - tup[1])
return NumOperations(minuslst)
def __add__(self, other):
addlst = [x + y for x, y in zip(self.math_list, other.math_list)]
return NumOperations(addlst)
def __mul__(self, other):
mullst = [x * y for x, y in zip(self.math_list, other.math_list)]
return NumOperations(mullst)
def __repr__(self):
return str(self.math_list)
x = NumOperations([100, 90, 80, 70, 60])
y = NumOperations([10, 9, 8, 7, 6])
p = x - y
z = x + y
q = x * y
print('Subtraction: ' + str(p))
print('Addition: ' + str(z))
print('Multiplication: ' + str(q))
Читайте также:
- Анализ аудиоданных с помощью глубокого обучения и Python (часть 1)
- Максимальная производительность Pandas Python
- Продвинутые методы и техники списков в Python
Перевод статьи Stephen Fordham: Using Magic Methods in Python