Числа — это гораздо больше, чем просто их значения.

Если вы когда-нибудь программировали на Python, велика вероятность, что вы использовали число в одной из своих программ. Например, вы могли использовать целое число для указания индекса значения в списке.

Но числа в Python — это гораздо больше, чем просто их необработанные значения. Давайте рассмотрим три вещи, связанные с числами в Python, о которых вы могли не знать.

1. У чисел есть методы

Почти все в Python является объектом. Один из первых объектов, о котором вы узнаете в Python, — это объект str для представления строк. Возможно, вы видели, как у строк есть методы, такие как метод .lower(), который возвращает новую строку со всеми символами нижнего регистра:

Числа в Python также являются объектами и, как и строки, имеют свои собственные методы. Например, вы можете преобразовать целое число в байтовую строку с помощью метода .to_bytes():

Параметр length указывает количество байтов, используемых в строке байтов, а параметр byteorder определяет порядок байтов. Например, установка byteorder на "big" возвращает строку байтов со старшим значащим байтом впереди, а установка byteorder на "little" помещает младший значащий байт первым.

255 — это наибольшее целое число, которое может быть представлено как 8-битное целое, поэтому вы можете без проблем установить length=1 в .to_bytes():

Однако если вы установите length=1 в .to_bytes() для 256, вы получите OverflowError :

Вы можете преобразовать строку байтов в целое число, используя метод класса .from_bytes():

Методы класса вызываются из имени класса, а не из экземпляра класса, поэтому метод .from_bytes() вызывается в int выше.

🤓 Ботанический самородок: 1729 — это наименьшее положительное целое число, которое можно записать в виде суммы двух положительных кубов двумя разными способами — факт, анекдотически приписываемый индийскому математику Шринивасе Рамануджану, который обнаружил это. имущество своему наставнику Г. Х. Харди:

«Я [Харди] помню, как однажды пошел навестить его [Рамануджана], когда он был болен в Путни. Я ездил в такси номер 1729 и заметил, что номер показался мне довольно скучным и что я надеюсь, что это не было неблагоприятным предзнаменованием. «Нет, — ответил он, — это очень интересное число; это наименьшее число, которое можно представить в виде суммы двух кубов двумя разными способами».

Один из способов выразить 1729 в ​​виде суммы двух кубов: ¹³ + 1²³. Можете ли вы найти другой путь?

У чисел с плавающей запятой также есть методы. Возможно, наиболее полезным методом для чисел с плавающей запятой является .is_integer(), который используется для проверки того, не имеет ли число с плавающей запятой дробную часть:

Одним из забавных методов с плавающей запятой является метод .as_integer_ratio(), который возвращает кортеж, содержащий числитель и знаменатель дроби, представляющей значение с плавающей запятой:

Однако из-за ошибки представления с плавающей запятой этот метод может возвращать некоторые неожиданные значения:

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

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

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

2. У чисел есть иерархия

В математике числа имеют естественную иерархию. Например, все натуральные числа являются целыми числами, все целые числа являются рациональными числами, все рациональные числа являются действительными числами, а все действительные числа являются комплексными числами.

То же самое верно и для чисел в Python. Эта числовая башня выражается через абстрактные типы, содержащиеся в numbers модуле.

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

Каждое число в Python является экземпляром класса Number:

Если вам нужно проверить, является ли значение в Python числовым, но вам все равно, какого типа числовое значение, используйте isinstance(value, Number).

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

  1. Класс Complex используется для представления комплексных чисел. Имеется один встроенный бетонный Complex тип: complex.
  2. Класс Real используется для представления действительных чисел. Имеется один встроенный бетон Real типа: float.
  3. Класс Rational используется для представления рациональных чисел. Имеется один встроенный бетон Rational типа: Fraction.
  4. Класс Integral используется для представления целых чисел. Есть два встроенных бетона Integral типов: int и bool.

Ждать. bool значения - это числа?! Да! Вы можете проверить все это в своем REPL:

На первый взгляд, здесь все кажется правильным — за исключением, возможно, значений bool, которые являются числами.

🐍 Особенность Python: Поскольку тип bool — это Integral (фактически, bool наследуется напрямую от int), вы можете делать довольно странные вещи с True и False.

Например, вы можете использовать True в качестве индекса для получения второго значения итерации. Если вы разделите число на False, вы получите ошибку ZeroDivisionError.

Попробуйте запустить "False"[True] и 1 / False в своем REPL!

Однако присмотритесь повнимательнее: в числовой иерархии Python есть несколько странных моментов.

Десятичные дроби не подходят

Существует четыре конкретных числовых типа, соответствующих четырем абстрактным типам в числовой башне Python: complex, float, Fraction и int. Но в Python есть пятый числовой тип, класс Decimal, который используется для точного представления десятичных чисел и преодоления ограничений арифметики с плавающей запятой.

Вы можете догадаться, что Decimal числа равны Real, но вы ошибетесь:

На самом деле единственный тип, от которого наследуются числа Decimal, — это класс Python Number:

Имеет смысл, что Decimal не наследуется от Integral. В какой-то степени имеет смысл и то, что Decimal не наследуется от Rational. Но почему Decimal не наследуется от Real или Complex?

Ответ кроется в исходном коде CPython:

Decimal имеет все методы, указанные в Real abc, но его не следует регистрировать как Real, поскольку десятичные числа не взаимодействуют с двоичными числами с плавающей запятой (т. е. Decimal('3.14') + 2.71828 не определено). Но ожидается, что абстрактные вещественные числа будут взаимодействовать (т. е. R1 + R2 должны работать, если R1 и R2 оба являются вещественными).

Все упирается в реализацию.

Поплавки странные

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

Числа с плавающей запятой хранятся в памяти как двоичные дроби, но это вызывает некоторые проблемы. Точно так же, как дробь 1/3 не имеет конечного десятичного представления — после запятой бесконечно много троек — дробь 1/10 не имеет конечного представления двоичной дроби.

Другими словами, вы не можете хранить 0,1 на компьютере с точной точностью — если только этот компьютер не имеет бесконечной памяти.

Со строго математической точки зрения все числа с плавающей запятой рациональны, кроме float("inf") и float("nan"). Но программисты используют их для аппроксимации действительных чисел и обращаются с ними по большей части как с действительными числами.

🐍 Особенность Python: float("nan") — это специальное значение с плавающей запятой, представляющее «нечисловые» значения — часто обозначаемые аббревиатурой NaN значений. Но поскольку float является числовым типом, isinstance(float("nan"), Number) возвращает True.

Правильно: не числовые значения - это числа.

Поплавки странные.

3. Числа расширяемы

Абстрактные числовые базовые типы Python позволяют создавать собственные абстрактные и конкретные числовые типы.

В качестве примера рассмотрим следующий класс ExtendedInteger, реализующий числа вида a + b√p, где a и b — целые числа, а p является простым (обратите внимание, что класс не обеспечивает простоту):

Вам нужно реализовать множество dunder-методов, чтобы убедиться, что конкретный тип реализует интерфейс Real. Вы также должны учитывать, как такие методы, как .__add__() и .__mul__(), взаимодействуют с другими типами Real.

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

С реализованным ExtendedInteger теперь вы можете делать такие вещи:

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

В документах есть несколько советов для разработчиков типов, которые вы должны прочитать перед реализацией пользовательских числовых типов. Также полезно ознакомиться с реализацией Fraction.

Заключение

Итак, у вас есть это. Три вещи (плюс, возможно, намного больше), о которых вы, возможно, не знали о числах в Python:

  1. У чисел есть методы, как и у любого другого объекта в Python.
  2. Числа имеют иерархию, даже если Decimal и float немного злоупотребляют этой иерархией.
  3. Вы можете создавать свои собственные числа, которые вписываются в иерархию чисел Python.

Надеюсь, вы узнали что-то новое!

Подпишитесь на мой бесплатный еженедельный информационный бюллетень Любопытно о коде, чтобы каждую пятницу получать на почту список ресурсов по программированию Python и Julia.



Еженедельный информационный бюллетень «Любопытно о коде
Любопытно о коде — это бесплатный еженедельный информационный бюллетень, посвященный темам языков программирования Python и Julia…davidamos.dev»



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

Спасибо, что прочитали!

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Получите эксклюзивный доступ к возможностям написания и советам в нашем сообществе Discord.