Числа — это гораздо больше, чем просто их значения.
Если вы когда-нибудь программировали на 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 поставляется с четырьмя дополнительными абстрактными типами, иерархия которых, начиная с наиболее общего числового типа, выглядит следующим образом:
- Класс
Complex
используется для представления комплексных чисел. Имеется один встроенный бетонныйComplex
тип:complex
. - Класс
Real
используется для представления действительных чисел. Имеется один встроенный бетонReal
типа:float
. - Класс
Rational
используется для представления рациональных чисел. Имеется один встроенный бетонRational
типа:Fraction
. - Класс
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:
- У чисел есть методы, как и у любого другого объекта в Python.
- Числа имеют иерархию, даже если
Decimal
иfloat
немного злоупотребляют этой иерархией. - Вы можете создавать свои собственные числа, которые вписываются в иерархию чисел Python.
Надеюсь, вы узнали что-то новое!
Подпишитесь на мой бесплатный еженедельный информационный бюллетень Любопытно о коде, чтобы каждую пятницу получать на почту список ресурсов по программированию Python и Julia.
Еженедельный информационный бюллетень «Любопытно о коде
Любопытно о коде — это бесплатный еженедельный информационный бюллетень, посвященный темам языков программирования Python и Julia…davidamos.dev»
А если вы хотите поднять свое программирование на новый уровень, я предлагаю индивидуальную тренировку. Нажмите здесь, чтобы получить дополнительную информацию.
Спасибо, что прочитали!
Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Получите эксклюзивный доступ к возможностям написания и советам в нашем сообществе Discord.