Создание объекта 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__, однако используют списковое включение для сокращения количества строк кода.

Magic-методы вычитания, сложения и умножения были определены для работы с пользовательскими объектами NumOperation.

Такое поведение при передаче аналогично таким пакетам анализа данных, как 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))

Читайте также:


Перевод статьи Stephen Fordham: Using Magic Methods in Python

Предыдущая статьяБинарное дерево на Go для новичка
Следующая статьяРасширение Python с помощью C