Содержание

Числа в Python — CodeChick

В этой статье вы узнаете, какие типы чисел есть в Python, научитесь приводить один тип к другому и производить с числами математические операции.

Какие числа поддерживаются 

Python поддерживает комплексные (complex), целые числа (integer) и числа с плавающей точкой (float).

Целые числа и числа с плавающей точкой отличаются наличием или отсутствием десятичного разделителя. Например, 5 — целое число, а 5.0 — число с плавающей точкой. 

Целые числа могут быть любой длины. Точность чисел с плавающей точкой обеспечивается только до 15 знака, на 16 могут возникать неточности.

Комплексные числа записываются следующим образом: x + yj. Здесь xдействительная часть, а yмнимая.

Чтобы узнать, к какому типу относится переменная, используйте функцию type(). Если вы хотите узнать, принадлежит ли переменная определенному классу, используйте isinstance().

a = 5
print(type(a))
print(type(5.0))

c = 5 + 3j
print(c + 3)
print(isinstance(c, complex))

Вывод:

<class 'int'>
<class 'float'>
(8+3j)
True

Системы счисления

Числа, с которыми мы ежедневно сталкиваемся, представлены в десятичной системе счисления. Но программисты чащего всего работают с двоичной (основанием 2), шестнадцатеричной (основание 16) и восьмеричной (основание 8) системами счисления.

Эти числа в Python могут быть представлены с помощью соответствующего префикса. Они записаны в таблице ниже.

Система счисления

Префикс

Двоичная

'0b'

или '0B'

Восьмеричная

'0o' или '0O'

Шестнадцатеричная

'0x' или '0X'

Вот как это выглядит:

# выведет 107
print(0b1101011)

# выведет 253, т.  к. 251 + 2) = 253
print(0xFB + 0b10)

# выведет 13
print(0o15)

Вывод:

107
253
13

Приведение типов

В Python один тип числа можно конвертировать в другой. Это называется приведением типов.

Операции вроде сложения или вычитания неявно, то есть автоматически, приводят целочисленный тип к числу с плавающей точкой, если один из операндов является числом с плавающей точкой. 

>>> 1 + 2.0
3.0

Как видите, при сложении 1 (это целое число) приводится к 1.0 (это число с плавающей точкой). Результат тоже будет числом с плавающей точкой.

Чтобы выполнить явное преобразование, вы можете использовать встроенные функции int(), float(), complex(). С помощью них к численным типам можно приводить даже строки.

>>> int(2.3)
2

>>> int(-2.8)
-2

>>> float(5)
5.0

>>> complex('3+5j')
(3+5j)

При приведении числа с плавающей точкой к целому десятичная часть удаляется. Говорят, что она «обрезается».

Модуль decimal

Встроенный класс float порой может выдавать удивительные результаты. Все мы знаем, что 1.1 + 2.2 = 3.3, но Python, кажется, так не думает. 

Введите в IDLE на своем компьютере такую строку:

>>> (1.1 + 2.2) == 3.3
False

Почему False? Что не так?

Оказывается, что числа с плавающей точкой в памяти компьютера реализованы с помощью двоичных дробей — компьютер понимает только их (0 и 1). Именно поэтому большинство десятичных дробей невозможно со всей точностью хранить в памяти компьютера.

Приведем пример. Мы не можем представить дробь ⅓ в виде десятичного числа. ⅓ = 0.33333333… период — бесконечный. Поэтому хранить можно лишь приблизительное значение этого числа.

То есть десятичная дробь 0.1 — это бесконечная двоичная дробь 0.000110011001100110011… и компьютер может хранить в памяти только конечное число цифр после точки. Это лишь приближенное к 0.

1 значение, но не равное ему. Следовательно, это аппаратные ограничения, а не ошибка Python.

>>> 1.1 + 2.2
3.3000000000000003

Для решения этой проблемы мы можем использовать модуль decimal, встроенный в Python. Тип float имеет точность до 15 знаков, а decimal — настраиваемую пользователем.

Разница налицо:

import decimal
print(0.1)
print(decimal.Decimal(0.1))

Вывод:

0.1
0.1000000000000000055511151231257827021181583404541015625

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

Положение точки тоже сохраняется. Мы знаем, что 25.50 кг точнее, чем 25.5 кг, так как у этой дроби два числа после точки.

from decimal import Decimal as D
print(D('1.1') + D('2.2'))
print(D('1.2') * D('2.50'))

Вывод:

3.3
3.000

Обратите внимание на нули в вышеприведенном примере.

Вы можете подумать: почему бы тогда не использовать модуль decimal вместо float постоянно? Справедливый вопрос.

Дело в эффективности. Операции с float происходят гораздо быстрее, чем с decimal

Когда стоит использовать decimal вместо float?
  • При создании приложения для работы с финансами. Здесь нужна точность.
  • Когда нужно держать уровень точности на определенном уровне.
  • Когда нужно реализовать дробь с определенным числом цифр после точки.

Модуль fractions

В Python есть модуль fractions, он производить операции с дробными числами.

У дробей есть числитель и знаменатель — это целые числа. Также этот модуль позволяет производить операции с рациональными числами.

Создать fraction-объект можно разными способами. Давайте их разберем:

import fractions
print(fractions.Fraction(1.5))
print(fractions.Fraction(5))
print(fractions.
Fraction(1,3))

Вывод:

3/2
5
1/3

При создании fraction-объекта из float могут возникнуть неожиданные результаты. Происходит это, как говорилось выше, из-за несовершенного двоичного представления чисел с плавающей точкой. 

К счастью, fraction позволяет создать экземпляр из строки. Настоятельно рекомендуем использовать именно этот способ, когда аргументом является десятичное число. 

import fractions

# Число с плавающей точкой
# Вывод: 2476979795053773/2251799813685248
print(fractions.Fraction(1.1))

# Строка
# Вывод: 11/10
print(fractions.Fraction('1.1'))

Вывод:

2476979795053773/2251799813685248
11/10

Этот тип данных поддерживает все базовые математические операции. 

from fractions import Fraction as F
# со всеми работает
print(F(1, 3) + F(1, 3))
print(1 / F(5, 6))
print(F(-3, 10) > 0)
print(F(-3, 10) < 0)

Вывод:

2/3
6/5
False
True

Модуль math и random

Для решения тригонометрических, логарифмических, вероятностных и статистических задач Python располагает встроенными модулями math и random.

 

import math

print(math.pi)
print(math.cos(math.pi))
print(math.exp(10))
print(math.log10(1000))
print(math.sinh(1))
print(math.factorial(6))

Вывод:

3.141592653589793
-1.0
22026.465794806718
3.0
1.1752011936438014
720

👉Полный список функций и атрибутов модуля math.

import random

print(random.randrange(10, 20))

x = ['а', 'б', 'в', 'г', 'д']

# Получаем случайный набор из х
print(random.choice(x))

# Перемешиваем x
random.shuffle(x)

# Выводим в консоль перемешанный список x
print(x)

# Выводим в консоль случайный элемент
print(random.random())

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

Вывод:

13
а
['г', 'б', 'д', 'а', 'в']
0. 038881285348306704

👉Полный список функция и атрибутов модуля random.

3 особенности чисел в Python, о которых вы, возможно, не знали / Хабр

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

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

№1: у чисел есть методы

В Python практически всё — это объект. Один из первых объектов, о котором узнаёт тот, кто начинает изучать Python — это str, используемый для представления строк. Возможно, вы сталкивались с использованием методов строк, вроде 

.lower(), который возвращает новую строку, все символы которой приведены к нижнему регистру:

>>> "HELLO". lower()
'hello'

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

>>> n = 255
>>> n.to_bytes(length=2, byteorder="big")
b'\x00\xff'

Параметр length указывает на количество байт, которые нужно использовать при составлении байтовой строки, а параметр byteorder определяет порядок байт. Например, установка параметра byteorder в значение «big» приводит к возврату байтовой строки, в которой старший байт расположен первым, а установка этого параметра в значение «little» приводит к тому, что первым идёт младший байт.

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

>>> n.to_bytes(length=1, byteorder="big")
b'\xff'

А вот если записать в n число 256 и вызвать для него . to_bytes() с параметром length=1, будет выдана ошибка OverflowError:

>>> n = 256
>>> n.to_bytes(length=1, byteorder="big")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: int too big to convert

Преобразовать байтовую строку в целое число можно, воспользовавшись методом .from_bytes() класса int:

>>> int.from_bytes(b'\x06\xc1', byteorder="big")
1729

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

Любопытный факт: 1729 — это самое маленькое положительное число, которое можно представить в виде суммы кубов двух положительных чисел двумя способами. Исторический анекдот связывает это число с индийским математиком Сринивасой Рамануджаном, который рассказал о нём своему наставнику Готфри Харолду Харди.

Харди часто навещал Рамануджана, когда тот, умирая, находился в больнице в Патни. Именно в одно из таких посещений произошёл «инцидент» с номером такси. Харди приехал в Патни на такси, воспользовавшись своим излюбленным транспортным средством. Он вошёл в палату, где лежал Рамануджан. Начинать разговор Харди всегда было мучительно трудно, и он произнёс свою первую фразу: «Если не ошибаюсь, то номер такси, на котором я приехал, 1729. Мне кажется, это скучное число». На что Рамануджан тотчас же ответил: «Нет, Харди! О нет! Это очень интересное число. Это самое малое из чисел, представимых в виде суммы двух кубов двумя различными способами».

Один из способов представления числа 1729 в виде суммы двух кубов — это 13 + 123. Можете отыскать второй способ?

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

>>> n = 2. 0
>>> n.is_integer()
True
>>> n = 3.14
>>> n.is_integer()
False

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

>>> n.as_integer_ratio()
(1, 2)

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

>>> n = 0.1
>>> n.as_integer_ratio()
(3602879701896397, 36028797018963968)

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

>>> (255).to_bytes(length=1, byteorder="big")
b'\xff'
>>> (3.14).is_integer()
False

Если обойтись без скобок — при попытке вызова метода на целочисленном литерале будет выдана ошибка SyntaxError. А вот при вызове метода числового литерала с плавающей точкой отсутствие скобок, что странно, не приведёт к ошибке:

>>> 255. 
SyntaxError: invalid syntax
>>> 3.14.is_integer()
False

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

№2: числа обладают иерархией

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

Похожие рассуждения применимы и к представлению чисел в Python. Здесь «числовая башня» выражается через абстрактные типы, содержащиеся в модуле numbers.

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

Все числа в Python являются экземплярами класса Number:

>>> from numbers import Number
>>> # Целые числа являются наследниками Number
>>> isinstance(1729, Number)
True
>>> # Числа с плавающей точкой являются наследниками Number
>>> isinstance(3.14, Number)
True
>>> # Комплексные числа являются наследниками Number
>>> isinstance(1j, Number)
True

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

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

  1. Класс Complex используется для представления комплексных чисел. Тут имеется один встроенный конкретный тип — complex.

  2. Класс Real — это представление вещественных чисел. Его единственный встроенный конкретный тип — float.

  3. Класс Rational представляет рациональные числа. Его единственным встроенным конкретным типом является Fraction.

  4. Класс Integral применяют для представления целых чисел. В нём имеется два встроенных конкретных типа — int и bool.

Так, погодите, а значения типа bool — это разве числа? Да — числа. Можете это проверить, воспользовавшись REPL:

>>> import numbers
>>> # Комплексные числа являются наследниками Complex
>>> isinstance(1j, numbers. Complex)
True
>>> # Комплексные числа не являются наследниками Real
>>> isinstance(1j, numbers.Real)
False
>>> # Числа с плавающей точкой являются наследниками Real
>>> isinstance(3.14, numbers.Real)
True
>>> # Числа с плавающей точкой не являются наследниками Rational
>>> isinstance(3.14, numbers.Rational)
False
>>> # Объекты Fractions - это не наследники Rational
>>> from fractions import Fraction
>>> isinstance(Fraction(1, 2), numbers.Rational)
True
>>> # Объекты Fractions - это не наследники Integral
>>> isinstance(Fraction(1, 2), numbers.Integral)
False
>>> # Целые числа - это наследники Integral
>>> isinstance(1729, numbers.Integral)
True
>>> # Логические значения - это наследники Integral
>>> isinstance(True, numbers.Integral)
True
>>> True == 1
True
>>> False == 0
True

Всё это, на первый взгляд, выглядит вполне нормально. Правда, порядок несколько нарушает то, что значения типа bool являются числами.

Странность Python: так как тип bool относится к классу Integral (на самом деле он — прямой наследник int), со значениями True и False можно вытворять довольно необычные вещи.

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

Попробуйте выполнить «False»[True] и 1 / False в REPL!

Но если присмотреться к числовым типам поближе, окажется, что в иерархии Python-чисел имеется пара своеобразных моментов.

Числа типа Decimal не укладываются в иерархию

Как уже было сказано, в «числовой башне» Python есть 4 конкретных числовых типа, соответствующих четырём абстрактным типам: complexfloatFraction и int. Но в Python имеется и пятый числовой тип, представленный классом Decimal. Этот тип используется для точного представления десятичных чисел и для преодоления ограничений арифметических операций с плавающей точкой.

Можно предположить, что числа типа Decimal являются наследниками Real, но это, на самом деле, не так:

>>> from decimal import Decimal
>>> import numbers
>>> isinstance(Decimal("3.14159"), numbers.Real)
False

Единственный класс, наследником которого является класс Decimal — это Number:

>>> isinstance(Decimal("3.14159"), numbers.Complex)
False
>>> isinstance(Decimal("3.14159"), numbers.Rational)
False
>>> isinstance(Decimal("3.14159"), numbers.Integral)
False
>>> isinstance(Decimal("3.14159"), numbers.Number)
True

Логично то, что класс Decimal не является наследником Integral. В некоторой степени смысл есть и в том, что Decimal не является наследником Rational. Но почему Decimal не является наследником Real или Complex?

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

Объекты Decimal обладают всеми методами, определёнными в классе Real, но эти объекты не должны регистрироваться в виде наследников Real, так как Decimal-числа не взаимодействуют с двоичными числами с плавающей точкой (например, результат операции Decimal(‘3.14’) + 2.71828 не определён). Но ожидается, что числа, классы которых являются наследниками абстрактного класса Real, способны взаимодействовать друг с другом (то есть — R1+R2 должно вычисляться в том случае, если числа R1 и R2 представлены типами, являющимися наследниками Real).

Получается, что объяснение странностей сводится к особенностям реализации.

Числа с плавающей точкой — странные создания

А вот числа с плавающей точкой, с другой стороны, реализуют абстрактный базовый класс Real. Они используются для представления вещественных чисел. Но, из-за того, что компьютерная память не является неограниченным ресурсом, числа с плавающей точкой — это лишь конечные аппроксимации вещественных чисел. Это приводит к возможности написания «ненормальных» образцов кода вроде такого:

>>> 0.1 + 0.1 + 0.1 == 0.3
False

Числа с плавающей точкой хранятся в памяти в виде двоичных дробей. Это приводит к появлению некоторых проблем. Например, у дроби 1/3 нет конечного десятичного представления (после десятичной точки идёт бесконечное множество троек). А у дроби 1/10 нет конечного представления в виде двоичной дроби.

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

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

Странность Pythonfloat(«nan») — это особое значение с плавающей точкой, представляющее собой «не число». Такие значения часто обозначают как NaN. Но, так как float — это числовой тип, isinstance(float(«nan»), Number) возвращает True.

Получается, что «не числа» — это числа.

В общем, числа с плавающей точкой — странные создания.

№3: набор числовых типов Python можно расширять

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

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

import math
import numbers
class ExtendedInteger(numbers.Real):
    
    def init(self, a, b, p = 2) -> None:
        self. a = a
        self.b = b
        self.p = p
        self._val = a + (b * math.sqrt(p))
    
    def repr(self):
        return f"{self.class.name}({self.a}, {self.b}, {self.p})"
    
    def str(self):
        return f"{self.a} + {self.b}√{self.p}"
    
    def trunc(self):
        return int(self._val)
    
    def float(self):
        return float(self._val)
    
    def hash(self):
        return hash(float(self._val))
    
    def floor(self):
        return math.floor(self._val)
    
    def ceil(self):
        return math.ceil(self._val)
    
    def round(self, ndigits=None):
        return round(self._val, ndigits=ndigits)
    
    def abs(self):
        return abs(self._val)
    
    def floordiv(self, other):
        return self._val // other
    
    def rfloordiv(self, other):
        return other // self._val
    
    def truediv(self, other):
        return self._val / other
    
    def rtruediv(self, other):
        return other / self._val
    
    def mod(self, other):
        return self. _val % other
        
    def rmod(self, other):
        return other % self._val
    
    def lt(self, other):
        return self._val < other
    
    def le(self, other):
        return self._val <= other
    
    def eq(self, other):
        return float(self) == float(other)
    
    def neg(self):
        return ExtendedInteger(-self.a, -self.b, self.p)
    
    def pos(self):
        return ExtendedInteger(+self.a, +self.b, self.p)
    
    def add(self, other):
        if isinstance(other, ExtendedInteger):
            # Если оба экземпляра имеют одно и то же значение p,
            # вернуть новый экземпляр ExtendedInteger
            if self.p == other.p:
                new_a = self.a + other.a
                new_b = self.b + other.b
                return ExtendedInteger(new_a, new_b, self.p)
            # В противном случае вернуть значение типа float
            else:
                return self._val + other._val
        # Если other - значение класса Integral, прибавить значение other к значению self. a
        elif isinstance(other, numbers.Integral):
            new_a = self.a + other
            return ExtendedInteger(new_a, self.b, self.p)
        # Если other - значение класса Real, вернуть значение типа float
        elif isinstance(other, numbers.Real):
            return self._val + other._val
        # Если тип other неизвестен, позволить другим принять решение
        # о том, что делать в такой ситуации
        else:
            return NotImplemented
    
    def radd(self, other):
        # Сложение коммутативно, поэтому прибегнуть к add
        return self.add(other)
    
    def mul(self, other):
        if isinstance(other, ExtendedInteger):
            # Если оба экземпляра имеют одно и то же значение p,
            # вернуть новый экземпляр ExtendedInteger
            if self.p == other.p:
                new_a = (self.a * other.a) + (self.b * other.b * self.p)
                new_b = (self.a * other.b) + (self.b * other.a)
                return ExtendedInteger(new_a, new_b, self. p)
            # в противном случае вернуть значение типа float
            else:
                return self._val * other._val
        # Если other - значение класса Integral, умножить его компоненты a и b на other
        elif isinstance(other, numbers.Integral):
            new_a = self.a * other
            new_b = self.b * other
            return ExtendedInteger(new_a, new_b, self.p)
        # Если other - значение класса Real, вернуть значение типа float
        elif isinstance(other, numbers.Real):
            return self._val * other
        # Если тип other неизвестен, позволить другим принять решение
        # о том, что делать в такой ситуации
        else:
            return NotImplemented
    
    def rmul(self, other):
        # Умножение коммутативно, поэтому прибегнуть к mul
        return self.mul(other)
    
    def pow(self, exponent):
        return self._val ** exponent
    
    def rpow(self, base):
        return base ** self._val

Для того чтобы обеспечить правильность реализации интерфейса Real конкретным типом — нужно создать реализации множества методов, в именах которых есть два символа подчёркивания. Ещё нужно поразмыслить о том, как методы вроде .add() и .mul() взаимодействуют с другими типами, являющимися наследниками Real.

Обратите внимание: вышеприведённый пример не создавался в расчёте на его полноту или абсолютную правильность. Его цель — продемонстрировать читателю возможности работы с числами.

При наличии реализации ExtendedInteger можно заниматься следующими вычислениями:

>>> a = ExtendedInteger(1, 2)
>>> b = ExtendedInteger(2, 3)
>>> a
ExtendedInteger(1, 2, 2)
>>> # Проверяем то, что a - это наследник Number
>>> isinstance(a, numbers.Number)
True
>>> # Проверяем то, что a - это наследник Real
>>> isinstance(a, numbers.Real)
True
>>> print(a)
1 + 2√2
>>> a * b
ExtendedInteger(14, 7, 2)
>>> print(a * b)
14 + 7√2
>>> float(a)
3.8284271247461903

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

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

Итоги

Вот — те три особенности Python-чисел, которые мы здесь обсуждали:

  1. У чисел есть методы, как и у практически всех остальных объектов в Python.

  2. Числа обладают иерархией, даже несмотря на то, что их чёткие взаимоотношения несколько портит наличие типов Decimal и float.

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

Может быть, вы узнали из этого материала не только об этих особенностях чисел, но и ещё о чём-нибудь, что вам пригодится.

О, а приходите к нам работать? 😏

Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.

Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.

Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.

Присоединяйтесь к нашей команде.

чисел — Числовые абстрактные базовые классы — Документация по Python 3.11.3

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


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

класс номеров.Номер

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

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

класс номеров.Комплекс

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

настоящий

Реферат. Извлекает действительную составляющую этого числа.

изображение

Реферат. Извлекает мнимую составляющую этого числа.

абстрактный метод сопряженный ()

Реферат. Возвращает комплексное сопряжение. Например, (1+3j).conjugate() == (1-3j) .

класс номеров.Настоящий

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

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

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

класс номеров.Rational

Подтипы Действительное и добавляет числитель и знаменатель свойств. Он также обеспечивает значение по умолчанию для с плавающей запятой() .

Числитель и знаменатель значений должны быть экземплярами Integral и должны быть в наименьших условиях с знаменатель положительный.

числитель

Реферат.

знаменатель

Реферат.

класс номеров. Интеграл

Subtypes Rational и добавляет преобразование в 9, | , ~ .

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

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

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

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

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

 класс MyFoo (комплекс): ...
MyFoo.register(Настоящий)
 

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

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

 класс MyIntegral(Integral):
    def __add__(я, другой):
        если isinstance (другое, MyIntegral):
            вернуть do_my_adding_stuff(я, другой)
        elif isinstance (другое, OtherTypeIKnowAbout):
            вернуть do_my_other_adding_stuff(я, другой)
        еще:
            вернуть нереализованный
    def __radd__(я, другой):
        если isinstance (другое, MyIntegral):
            вернуть do_my_adding_stuff(другое, себя)
        elif isinstance (другое, OtherTypeIKnowAbout):
            вернуть do_my_other_adding_stuff(другое, себя)
        elif isinstance (другой, интеграл):
            вернуть int(другое) + int(себя)
        elif isinstance (другое, реальное):
            вернуть поплавок (другой) + поплавок (сам)
        elif isinstance (другой, сложный):
            вернуть комплекс (другой) + комплекс (я)
        еще:
            вернуть нереализованный
 

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

  1. Если А определяет __add__() который принимает b , все хорошо.

  2. Если A возвращается к стандартному коду, вернуть значение из __add__() , мы бы упустили возможность что B определяет более интеллектуальный __radd__() , поэтому шаблон должен возвращать NotImplemented из __добавить__() . (Или может не реализовать __add__() в все.)

  3. Затем B __radd__() получает шанс. Если он принимает и , все хорошо.

  4. Если он возвращается к шаблону, больше нет возможных методы, чтобы попробовать, так что это где реализация по умолчанию должен жить.

  5. Если B <: A , Python пытается использовать B.__radd__ до А.__добавить__ . Это нормально, потому что это было реализовано с помощью знание A , поэтому он может обрабатывать эти экземпляры до делегирование Комплекс .

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

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

 по определению _operator_fallbacks (мономорфный_оператор, резервный_оператор):
    защита вперед (а, б):
        если isinstance(b, (int, Fraction)):
            вернуть monomorphic_operator(a, b)
        elif isinstance(b, float):
            вернуть fallback_operator (с плавающей запятой (а), б)
        elif isinstance (b, комплекс):
            вернуть fallback_operator (комплекс (а), б)
        еще:
            вернуть нереализованный
    forward.__name__ = '__' + fallback_operator.__name__ + '__'
    вперед.__doc__ = мономорфный_оператор.__doc__
    деф реверс(б, а):
        если isinstance(a, Rational):
            # Включает целые числа.
            вернуть monomorphic_operator(a, b)
        elif isinstance(a, Real):
            return fallback_operator (с плавающей запятой (a), с плавающей запятой (b))
        elif isinstance(a, Комплекс):
            return fallback_operator (комплекс (а), комплекс (б))
        еще:
            вернуть нереализованный
    reverse. __name__ = '__r' + резервный_оператор.__name__ + '__'
    reverse.__doc__ = monomorphic_operator.__doc__
    возврат вперед, назад
определение _add (а, б):
    """а + б"""
    return Fraction (a.числитель * b.знаменатель +
                    б.числитель * а.знаменатель,
                    а.знаменатель * б.знаменатель)
__add__, __radd__ = _operator_fallbacks(_add, operator.add)
# ...
 

Числа Python, преобразование типов и математика (с примерами)

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

Числовые типы данных используются для хранения числовых значений.

Python поддерживает целые числа, числа с плавающей запятой и комплексные числа. В Python они определены как классы int , float и complex .

  • ряд - содержит целые числа со знаком неограниченной длины.
  • float - содержит числа с плавающей запятой и точность до 15 знаков после запятой.
  • комплекс - содержит комплексные числа.

Числовой тип данных Python

Целые числа и числа с плавающей запятой разделяются наличием или отсутствием десятичной точки. Например,

  • 5 — это целое число
  • .
  • 5.42 — число с плавающей запятой.

Комплексные числа записываются в виде x + yj , где x — действительная часть, а y — мнимая часть.

Мы можем использовать функцию type() , чтобы узнать, к какому классу принадлежит переменная или значение.

Давайте посмотрим пример,

 num1 = 5
print(num1, 'имеет тип', type(num1))
число2 = 5,42
печать (число2, 'имеет тип', тип (число2))
число3 = 8+2j
print(num3, 'имеет тип', type(num3)) 

Вывод

  5 имеет тип <класс 'int'>
5.42 имеет тип 
(8+2j) имеет тип   

В приведенном выше примере мы создали три переменные с именами num1 , num2 и num3 со значениями 5 , 5,42 , 900 8+2j соответственно.

Мы также использовали функцию type() , чтобы узнать, к какому классу принадлежит определенная переменная. С

  • 5 является целым числом, type() возвращает int как класс num1 т.е.
  • 5.42 - это значение с плавающей запятой, type() возвращает float как класс num2 , т.е.
  • 1 + 2j — комплексное число, type() возвращает комплекс как класс num3 , т. е.

Системы счисления

Числа, с которыми мы имеем дело каждый день, имеют десятичную систему счисления (основание 10) .

Но программисты должны работать с двоичной (основание 2) , шестнадцатеричной (основание 16) и восьмеричной (основание 8) системами счисления.

В Python мы можем представить эти числа, поместив соответствующий префикс перед этим числом. В следующей таблице перечислены эти префиксы.

Система счисления Префикс
Двоичный 0b или 0B
Восьмеричный 00 или 00
Шестнадцатеричный 0x или 0X

Вот несколько примеров

 print(0b1101011) # отпечатков 107
print(0xFB + 0b10) # печатает 253
print(0o15) # печатает 13 

Преобразование типов в Python

В программировании преобразование типов — это процесс преобразования одного типа числа в другой.

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

 print(1 + 2. 0) # выводит 3.0 

Здесь мы видим выше, что 1 (целое) преобразуется в 1.0 (с плавающей запятой) для сложения, и результат также является числом с плавающей запятой.

Явное преобразование типов

Мы также можем использовать встроенные функции, такие как int() , float() и complex() для явного преобразования между типами. Эти функции могут даже конвертировать из строк.

 число1 = целое (2.3)
print(num1) # печатает 2
число2 = целое (-2,8)
print(num2) # печатает -2
число3 = число с плавающей запятой (5)
print(num3) # печатает 5.0
число4 = комплекс('3+5j')
print(num4) # печатает (3 + 5j) 

Здесь при преобразовании из числа с плавающей запятой в целое число усекается (удаляются десятичные части).

Аналогичным образом при преобразовании из целого числа в число с плавающей запятой .0 добавляется к числу в постфиксе.


Случайный модуль Python

Python предлагает модуль random для генерации случайных чисел или выбора случайного элемента из итератора.

Сначала нам нужно импортировать модуль random . Например,

 случайный импорт
печать (случайный. случайный диапазон (10, 20))
список1 = ['а', 'б', 'с', 'г', 'е']
# получить случайный элемент из list1
печать (случайный выбор (список1))
# Перемешать список1
случайный. случайный (список1)
# Распечатать перетасованный список1
печать (список1)
# Напечатать случайный элемент
печать(случайный.случайный()) 

Выход

  15
а
['д', 'б', 'в', 'е', 'а']
0,6716121217631744  

Чтобы узнать больше о модуле random , посетите Python Random Module.


Python Mathematics

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

 import math
печать (math.pi)
печать (math.cos (math.pi))
печать (математика.выражение (10))
печать (математика. log10 (1000))
печать (математика.