numbers — Числовые абстрактные базовые классы

Исходный код: Lib/numbers.py


Модуль numbers (PEP 3141) определяет иерархию числовых абстрактных базовых классов, которые прогрессивно определяют больше операций. None типов, определенных в этом модуле, можно создать.

class numbers.Number

Корень числовой иерархии. Если вы просто хотите проверить, является ли аргумент x числом, не заботясь, какого рода, используйте isinstance(x, Number).

Числовая башня

class numbers.Complex

Подклассы этого типа описывают комплексные числа и включают операции, которые работают со встроенным типом complex. Это: преобразования в complex и bool, real, imag, +, -, *, /, abs(), conjugate(), == и !=. Все, кроме - и !=, абстрактны.

real

Абстрактный. Извлекает действительный компонент этого числа.

imag

Абстрактный. Извлекает мнимый компонент этого числа.

abstractmethod conjugate()

Абстрактный. Возвращает комплексный конъюгат. Например, (1+3j).conjugate() == (1-3j).

class numbers.Real

К Complex Real добавляет операции та работа над действительными числами.

Короче говоря, это: преобразование в float, math.trunc(), round(), math.floor(), math.ceil(), divmod(), //, %, <, <=, > и >=.

Real также предоставляет значения по умолчанию для complex(), real, imag и conjugate().

class numbers.Rational

Подтипы Real и добавляет свойства numerator и denominator, которые должны быть в самых низких сроках. С их помощью он предоставляет значение по умолчанию для float().

numerator

Абстрактный.

denominator

Абстрактный.

class numbers.Integral

Подтипы Rational и добавляет преобразование в int. Содержит значения по умолчанию для float(), numerator и denominator. Добавляет абстрактные методы для операций ** и битовую-строку: <<, >>, &, ^, |, ~.

Примечания для реализаторов типов

Реализаторы должны быть осторожны, чтобы сделать равные числа равными и хэшировать их в одном и том же значения. Это может быть тонким, если существуют два различных расширения вещественных чисел. Например, fractions.Fraction реализует hash() следующим образом:

def __hash__(self):
    if self.denominator == 1:
        # Получить целые числа правильно.
        return hash(self.numerator)
    # Дорогая проверка, но однозначно правильный.
    if self == float(self):
        return hash(float(self))
    else:
        # Использовать хэш кортежа, чтобы избежать высокой частоты коллизий на простых
        # дробях.
        return hash((self.numerator, self.denominator))

Добавление дополнительных числовых ABC

Есть, конечно, более возможные ABC для чисел, и это было бы бедной иерархией, если бы она устранила возможность добавления их. Можно добавить MyFoo между Complex и Real с помощью:

class MyFoo(Complex): ...
MyFoo.register(Real)

Реализация арифметических операций

Мы хотим реализовать арифметические операции так, чтобы операции смешанного режима либо вызывали реализацию, автор которой знал о типах обоих аргументов, либо преобразовывали оба в ближайший встроенный тип и делали операцию там. Для подтипов Integral это означает, что __add__() и __radd__() должны быть определены как:

class MyIntegral(Integral):

    def __add__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(self, other)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(self, other)
        else:
            return NotImplemented

    def __radd__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(other, self)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(other, self)
        elif isinstance(other, Integral):
            return int(other) + int(self)
        elif isinstance(other, Real):
            return float(other) + float(self)
        elif isinstance(other, Complex):
            return complex(other) + complex(self)
        else:
            return NotImplemented

Существует 5 различных случаев операции смешанного типа на подклассы Complex. Я буду ссылаться на все вышеперечисленные код, которые не называют MyIntegral и OtherTypeIKnowAbout как «плита». a будет сущность A, который является подтипом Complex (a : A <: Complex), и b : B <: Complex. Я подумаю о a + b:

  1. Если A определяет __add__(), который принимает b, все хорошо.
  2. Если бы A отступает к газетному материалу код, и это был к возвращает значение от __add__(), мы пропустили бы возможность, что B определяет более интеллектуальный __radd__(), таким образом, газетный материал должен возвращает NotImplemented от __add__(). (Или A может вообще не реализовывать __add__().)
  3. Тогда __radd__() B получит шанс. Если оно принимает a, все хорошо.
  4. Если он падает обратно на цоколь, больше нет возможных методов, чтобы попробовать, так что это то, где должна жить реализация по умолчанию.
  5. Если B <: A, Python пытается B.__radd__ перед A.__add__. Это нормально, потому что он был реализован со знанием A, так что он может обрабатывать те сущности перед делегированием Complex.

Если A <: Complex и B <: Real, не делясь никакими другими знаниями, то соответствующая общая операция - та, вовлекающая построенный в complex и обе земли __radd__() s там, таким образом, a+b == b+a.

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

def _operator_fallbacks(monomorphic_operator, fallback_operator):
    def forward(a, b):
        if isinstance(b, (int, Fraction)):
            return monomorphic_operator(a, b)
        elif isinstance(b, float):
            return fallback_operator(float(a), b)
        elif isinstance(b, complex):
            return fallback_operator(complex(a), b)
        else:
            return NotImplemented
    forward.__name__ = '__' + fallback_operator.__name__ + '__'
    forward.__doc__ = monomorphic_operator.__doc__

    def reverse(b, a):
        if isinstance(a, Rational):
            # Включает в себя ints.
            return monomorphic_operator(a, b)
        elif isinstance(a, numbers.Real):
            return fallback_operator(float(a), float(b))
        elif isinstance(a, numbers.Complex):
            return fallback_operator(complex(a), complex(b))
        else:
            return NotImplemented
    reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
    reverse.__doc__ = monomorphic_operator.__doc__

    return forward, reverse

def _add(a, b):
    """a + b"""
    return Fraction(a.numerator * b.denominator +
                    b.numerator * a.denominator,
                    a.denominator * b.denominator)

__add__, __radd__ = _operator_fallbacks(_add, operator.add)

# ...