Python & AI Tutorials Logo
Программирование Python

4. Работа с числами

В главе 3 вы узнали, как создавать переменные и работать с основными числовыми типами Python: целыми числами и числами с плавающей точкой. Теперь пришло время заставить эти числа работать. В этой главе вы узнаете, как выполнять вычисления, комбинировать операции и использовать встроенные инструменты Python для работы с числовыми данными.

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

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

4.1) Базовая арифметика: сложение, вычитание и умножение

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

4.1.1) Сложение с оператором +

Оператор + складывает два числа. Он работает как с целыми, так и с числами с плавающей точкой:

python
# basic_addition.py
# Adding integers
total = 15 + 27
print(total)  # Output: 42
 
# Adding floats
price = 19.99 + 5.50
print(price)  # Output: 25.49
 
# You can add multiple numbers in one expression
sum_total = 10 + 20 + 30 + 40
print(sum_total)  # Output: 100

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

python
# mixed_addition.py
result = 10 + 3.5
print(result)  # Output: 13.5
print(type(result))  # Output: <class 'float'>

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

4.1.2) Вычитание с оператором -

Оператор - вычитает второе число из первого:

python
# basic_subtraction.py
# Subtracting integers
difference = 100 - 42
print(difference)  # Output: 58
 
# Subtracting floats
remaining = 50.75 - 12.25
print(remaining)  # Output: 38.5
 
# Subtraction can produce negative results
balance = 20 - 35
print(balance)  # Output: -15

Как и при сложении, вычитание также продвигает целые числа до float, когда вы смешиваете типы:

python
# mixed_subtraction.py
result = 100 - 0.01
print(result)  # Output: 99.99
print(type(result))  # Output: <class 'float'>

4.1.3) Умножение с оператором *

Оператор * умножает два числа:

python
# basic_multiplication.py
# Multiplying integers
product = 6 * 7
print(product)  # Output: 42
 
# Multiplying floats
area = 3.5 * 2.0
print(area)  # Output: 7.0
 
# Multiplying by zero always gives zero
result = 1000 * 0
print(result)  # Output: 0

Умножение подчиняется тем же правилам преобразования типов. Когда вы умножаете целое число на число с плавающей точкой, результат — float:

python
# mixed_multiplication.py
result = 5 * 2.5
print(result)  # Output: 12.5
print(type(result))  # Output: <class 'float'>
 
# Even if the result is a whole number
result = 4 * 2.0
print(result)  # Output: 8.0 (note the .0)
print(type(result))  # Output: <class 'float'>

Обратите внимание в последнем примере, что, хотя 4 × 2.0 равно 8, Python представляет это как 8.0, потому что один из операндов был float. Тип результата зависит от типов входных значений, а не от того, является ли математический результат целым числом.

4.1.4) Практические примеры с базовой арифметикой

Посмотрим, как эти операции работают вместе в реалистичных сценариях:

python
# shopping_cart.py
# Calculate a shopping cart total
item1_price = 12.99
item2_price = 8.50
item3_price = 15.00
 
subtotal = item1_price + item2_price + item3_price
print(f"Subtotal: ${subtotal}")  # Output: Subtotal: $36.49
 
tax_rate = 0.08
tax = subtotal * tax_rate
print(f"Tax: ${tax}")  # Output: Tax: $2.9192
 
total = subtotal + tax
print(f"Total: ${total}")  # Output: Total: $39.4092
python
# temperature_change.py
# Calculate temperature change
morning_temp = 45.5
afternoon_temp = 68.2
 
change = afternoon_temp - morning_temp
print(f"Temperature increased by {change} degrees")
# Output: Temperature increased by 22.7 degrees

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

4.2) Деление, целочисленное деление и остаток: /, // и %

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

4.2.1) Обычное деление с /

Оператор / выполняет «истинное деление» — он всегда возвращает результат типа float, даже при делении двух целых чисел:

python
# true_division.py
# Dividing integers
result = 10 / 2
print(result)  # Output: 5.0 (note: float, not 5)
print(type(result))  # Output: <class 'float'>
 
# Division that doesn't result in a whole number
result = 10 / 3
print(result)  # Output: 3.3333333333333335
 
# Dividing floats
result = 15.5 / 2.5
print(result)  # Output: 6.2

Ключевой момент здесь в том, что / всегда производит float, даже когда оба операнда — целые и результат математически является целым числом. Это сделано намеренно: Python хочет, чтобы деление вело себя последовательно и сохраняло дробную точность.

Это поведение отличается от некоторых других языков программирования, где деление двух целых чисел дает целый результат. В Python 3, если вам нужен целый результат, нужно использовать целочисленное деление (о нем далее) или явно преобразовать результат.

4.2.2) Целочисленное деление с //

Оператор // выполняет «целочисленное деление» (floor division) — делит, а затем округляет вниз до ближайшего целого. Когда оба операнда — целые, результат — целое. Когда хотя бы один операнд — float, результат — float (но все равно округленный вниз):

python
# floor_division.py
# Floor division with integers
result = 10 // 3
print(result)  # Output: 3 (not 3.333...)
print(type(result))  # Output: <class 'int'>
 
# Even division still gives an integer
result = 10 // 2
print(result)  # Output: 5 (integer, not 5.0)
print(type(result))  # Output: <class 'int'>
 
# Floor division with floats gives a float
result = 10.0 // 3
print(result)  # Output: 3.0
print(type(result))  # Output: <class 'float'>

«Округление вниз» означает движение к минус бесконечности, а не просто отбрасывание десятичной части. Это важно для отрицательных чисел:

python
# floor_division_negative.py
# Positive numbers: rounds down (toward negative infinity)
result = 7 // 2
print(result)  # Output: 3
 
# Negative numbers: still rounds toward negative infinity
result = -7 // 2
print(result)  # Output: -4 (not -3!)
 
# Why -4? Because -3.5 rounded down (toward negative infinity) is -4
# Think of the number line: ... -4, -3.5, -3, -2, -1, 0, 1, 2, 3, 3.5, 4 ...

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

python
# floor_division_practical.py
# How many complete dozens in 50 eggs?
eggs = 50
eggs_per_dozen = 12
complete_dozens = eggs // eggs_per_dozen
print(f"Complete dozens: {complete_dozens}")  # Output: Complete dozens: 4
 
# How many full hours in 150 minutes?
minutes = 150
full_hours = minutes // 60
print(f"Full hours: {full_hours}")  # Output: Full hours: 2

4.2.3) Остаток от деления (modulo) с %

Оператор % (называется «modulo» или «mod») возвращает остаток после деления. Он отвечает на вопрос: «После деления, что осталось?»

python
# modulo_basic.py
# 10 divided by 3 is 3 with remainder 1
result = 10 % 3
print(result)  # Output: 1
 
# 10 divided by 2 is 5 with remainder 0 (even division)
result = 10 % 2
print(result)  # Output: 0
 
# Works with floats too
result = 10.5 % 3
print(result)  # Output: 1.5

Оператор % невероятно полезен для нескольких распространенных шаблонов программирования.

Проверка, четное число или нечетное:

python
# even_odd_check.py
number = 17
 
if number % 2 == 0:
    print(f"{number} is even")
else:
    print(f"{number} is odd")
# Output: 17 is odd

Получение оставшихся элементов:

python
# leftover_items.py
eggs = 50
eggs_per_dozen = 12
 
leftover = eggs % eggs_per_dozen
print(f"Leftover eggs: {leftover}")  # Output: Leftover eggs: 2

Зацикливание значений в диапазоне (как на часах):

python
# clock_arithmetic.py
# What hour is it 25 hours from now? (on a 12-hour clock)
current_hour = 10
hours_later = 25
future_hour = (current_hour + hours_later) % 12
print(f"Hour: {future_hour}")  # Output: Hour: 11

4.2.4) Связь между //, % и /

Целочисленное деление и modulo тесно связаны. Вместе они дают вам полный результат деления:

python
# division_relationship.py
dividend = 17
divisor = 5
 
quotient = dividend // divisor  # How many times 5 goes into 17
remainder = dividend % divisor  # What's left over
 
print(f"{dividend} ÷ {divisor} = {quotient} remainder {remainder}")
# Output: 17 ÷ 5 = 3 remainder 2
 
# You can verify: quotient * divisor + remainder should equal dividend
verification = quotient * divisor + remainder
print(f"Verification: {quotient} × {divisor} + {remainder} = {verification}")
# Output: Verification: 3 × 5 + 2 = 17

Эта связь всегда верна: для любых чисел a и b (где b не ноль) выполняется a == (a // b) * b + (a % b).

4.2.5) Выбор подходящего оператора деления

Краткое руководство по выбору оператора:

  • Используйте /, когда вам нужен точный математический результат с десятичной частью (чаще всего в расчетах)
  • Используйте //, когда нужно посчитать, сколько полных групп помещается (например, дюжины, часы, страницы)
  • Используйте %, когда нужен остаток или оставшееся количество (например, проверка четности, зацикливание значений, поиск «хвостов»)
python
# choosing_operators.py
# Scenario: Distributing 47 candies among 6 children
 
candies = 47
children = 6
 
# How many candies per child? (use //)
per_child = candies // children
print(f"Each child gets {per_child} candies")  # Output: Each child gets 7 candies
 
# How many candies are left over? (use %)
leftover = candies % children
print(f"Leftover candies: {leftover}")  # Output: Leftover candies: 5
 
# What's the exact average? (use /)
average = candies / children
print(f"Average per child: {average}")  # Output: Average per child: 7.833333333333333

4.3) Сокращенные операторы присваивания

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

4.3.1) Что такое сокращенные операторы присваивания

Сокращенный оператор присваивания объединяет арифметическую операцию с присваиванием. Вместо того чтобы писать:

python
# traditional_update.py
count = 10
count = count + 5  # Add 5 to count
print(count)  # Output: 15

вы можете написать:

python
# augmented_update.py
count = 10
count += 5  # Same as: count = count + 5
print(count)  # Output: 15

Обе версии делают одно и то же, но сокращенная более лаконична и наглядно показывает намерение: «увеличить count на 5».

4.3.2) Все сокращенные операторы присваивания

Python предоставляет сокращенные операторы присваивания для всех арифметических операций:

python
# all_augmented_operators.py
# Addition
x = 10
x += 5  # x = x + 5
print(f"After += 5: {x}")  # Output: After += 5: 15
 
# Subtraction
x = 10
x -= 3  # x = x - 3
print(f"After -= 3: {x}")  # Output: After -= 3: 7
 
# Multiplication
x = 10
x *= 4  # x = x * 4
print(f"After *= 4: {x}")  # Output: After *= 4: 40
 
# Division
x = 10
x /= 2  # x = x / 2
print(f"After /= 2: {x}")  # Output: After /= 2: 5.0
 
# Floor division
x = 10
x //= 3  # x = x // 3
print(f"After //= 3: {x}")  # Output: After //= 3: 3
 
# Modulo
x = 10
x %= 3  # x = x % 3
print(f"After %= 3: {x}")  # Output: After %= 3: 1
 
# Exponentiation (we'll see more about ** in section 4.6)
x = 2
x **= 3  # x = x ** 3 (2 to the power of 3)
print(f"After **= 3: {x}")  # Output: After **= 3: 8

4.3.3) Типичные случаи использования сокращенного присваивания

Сокращенные операторы присваивания особенно удобны в нескольких распространенных шаблонах программирования.

Накопление суммы:

python
# accumulating_total.py
total = 0
total += 10  # Add first item
total += 25  # Add second item
total += 15  # Add third item
print(f"Total: {total}")  # Output: Total: 50

Подсчет количества:

python
# counting.py
count = 0
# Imagine these happen as we process data
count += 1  # Found one
count += 1  # Found another
count += 1  # Found another
print(f"Count: {count}")  # Output: Count: 3

Обновление баланса:

python
# balance_updates.py
balance = 100.00
balance -= 25.50  # Purchase
balance += 50.00  # Deposit
balance -= 10.00  # Purchase
print(f"Balance: ${balance}")  # Output: Balance: $114.5

Применение повторяющихся операций:

python
# repeated_operations.py
value = 100
value *= 1.1  # Increase by 10%
value *= 1.1  # Increase by 10% again
value *= 1.1  # Increase by 10% again
print(f"Value after three 10% increases: {value}")
# Output: Value after three 10% increases: 133.10000000000002

4.3.4) Важные детали о сокращенном присваивании

Сокращенное присваивание создает новый объект для неизменяемых типов:

Помните из главы 3, что числа — неизменяемые: вы не можете изменить значение числа, можно только создать новое число. Когда вы пишете x += 5, Python создает новый объект-число и привязывает его к x. Старый объект-число отбрасывается (мы подробнее рассмотрим это в главе 17, когда будем обсуждать объектную модель Python):

python
# augmented_with_immutables.py
x = 10
print(id(x))  # Output: (some memory address)
 
x += 5
print(id(x))  # Output: (different memory address)

Пока важно понимать, что x += 5 эквивалентно x = x + 5 — это удобное сокращение, а не принципиально другая операция.

Нельзя использовать сокращенное присваивание до создания переменной:

python
# augmented_requires_existing.py
# This will cause an error:
# count += 1  # NameError: name 'count' is not defined
 
# You must initialize the variable first:
count = 0
count += 1  # Now this works
print(count)  # Output: 1

Преобразования типов происходят так же:

python
# augmented_type_conversion.py
x = 10  # integer
x += 2.5  # Add a float
print(x)  # Output: 12.5
print(type(x))  # Output: <class 'float'>

Результат подчиняется тем же правилам преобразования типов, что и обычные операторы: если вы смешиваете int и float, результат — float.

4.3.5) Когда использовать сокращенное присваивание

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

  • Более лаконичным: x += 5 короче, чем x = x + 5
  • Более читаемым: намерение сразу понятно
  • Менее подверженным ошибкам: меньше риск опечататься в имени переменной

Сравните эти две версии:

python
# comparison.py
# Without augmented assignment
accumulated_distance_in_kilometers = 0
accumulated_distance_in_kilometers = accumulated_distance_in_kilometers + 10
accumulated_distance_in_kilometers = accumulated_distance_in_kilometers + 25
 
# With augmented assignment
accumulated_distance_in_kilometers = 0
accumulated_distance_in_kilometers += 10
accumulated_distance_in_kilometers += 25

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

4.4) Приоритет операторов и скобки

Когда вы комбинируете несколько операций в одном выражении, Python нужны правила, чтобы определить порядок вычислений. Следует ли вычислять 2 + 3 * 4 как (2 + 3) * 4 = 20 или как 2 + (3 * 4) = 14? Ответ зависит от приоритета операторов — правил, определяющих, какие операции выполняются первыми.

4.4.1) Понимание приоритета операторов

Python следует тем же правилам приоритета, которые вы изучали в математике: умножение и деление выполняются раньше сложения и вычитания. Часто это запоминают по акрониму PEMDAS (Parentheses, Exponents, Multiplication/Division, Addition/Subtraction), хотя о степенях мы поговорим в разделе 4.6.

python
# precedence_basic.py
# Multiplication happens before addition
result = 2 + 3 * 4
print(result)  # Output: 14 (not 20)
# Python calculates: 2 + (3 * 4) = 2 + 12 = 14
 
# Division happens before subtraction
result = 10 - 8 / 2
print(result)  # Output: 6.0 (not 1.0)
# Python calculates: 10 - (8 / 2) = 10 - 4.0 = 6.0

Вот порядок приоритета для операторов, которые мы уже изучили (от высшего к низшему):

  1. Скобки () — наивысший приоритет, вычисляются первыми
  2. Возведение в степень ** — (раздел 4.6)
  3. Умножение, деление, целочисленное деление, остаток *, /, //, % — один уровень, слева направо
  4. Сложение, вычитание +, - — один уровень, слева направо

Посмотрим еще примеры работы приоритета:

python
# precedence_examples.py
# Multiplication before addition
result = 5 + 2 * 3
print(result)  # Output: 11 (5 + 6)
 
# Division before subtraction
result = 20 - 10 / 2
print(result)  # Output: 15.0 (20 - 5.0)
 
# Multiple operations at the same level: left to right
result = 10 - 3 + 2
print(result)  # Output: 9 ((10 - 3) + 2)
 
result = 20 / 4 * 2
print(result)  # Output: 10.0 ((20 / 4) * 2)

4.4.2) Использование скобок для управления порядком

Скобки переопределяют приоритет по умолчанию. Операции в скобках всегда выполняются первыми, независимо от операторов:

python
# parentheses_override.py
# Without parentheses: multiplication first
result = 2 + 3 * 4
print(result)  # Output: 14
 
# With parentheses: addition first
result = (2 + 3) * 4
print(result)  # Output: 20
 
# Another example
result = 10 - 8 / 2
print(result)  # Output: 6.0
 
result = (10 - 8) / 2
print(result)  # Output: 1.0

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

python
# nested_parentheses.py
result = ((2 + 3) * 4) - 1
print(result)  # Output: 19
# Step 1: (2 + 3) = 5
# Step 2: (5 * 4) = 20
# Step 3: 20 - 1 = 19
 
result = 2 * (3 + (4 - 1))
print(result)  # Output: 12
# Step 1: (4 - 1) = 3
# Step 2: (3 + 3) = 6
# Step 3: 2 * 6 = 12

4.4.3) Когда операторы имеют одинаковый приоритет

Когда несколько операторов одного уровня приоритета появляются в выражении, Python вычисляет их слева направо (это называется «левая ассоциативность»):

python
# left_to_right.py
# Addition and subtraction: left to right
result = 10 - 3 + 2 - 1
print(result)  # Output: 8
# Evaluation: ((10 - 3) + 2) - 1 = (7 + 2) - 1 = 9 - 1 = 8
 
# Multiplication and division: left to right
result = 20 / 4 * 2
print(result)  # Output: 10.0
# Evaluation: (20 / 4) * 2 = 5.0 * 2 = 10.0
 
# This matters! Different order gives different result:
result = 20 / (4 * 2)
print(result)  # Output: 2.5

4.4.4) Практические примеры с приоритетом

Рассмотрим реальные ситуации, где понимание приоритета важно:

python
# temperature_conversion.py
# Convert Fahrenheit to Celsius: C = (F - 32) * 5 / 9
fahrenheit = 98.6
 
# Correct: parentheses ensure subtraction happens first
celsius = (fahrenheit - 32) * 5 / 9
print(f"{fahrenheit}°F = {celsius}°C")
# Output: 98.6°F = 37.0°C
 
# Wrong: without parentheses, multiplication happens first
# celsius = fahrenheit - 32 * 5 / 9  # This would be wrong!
# This would calculate: fahrenheit - ((32 * 5) / 9)
python
# calculate_average.py
# Calculate average of three numbers
num1 = 85
num2 = 92
num3 = 78
 
# Correct: parentheses ensure addition happens before division
average = (num1 + num2 + num3) / 3
print(f"Average: {average}")  # Output: Average: 85.0
 
# Wrong: without parentheses, only num3 is divided by 3
# average = num1 + num2 + num3 / 3  # This would be wrong!
# This would calculate: 85 + 92 + (78 / 3) = 85 + 92 + 26.0 = 203.0
python
# discount_calculation.py
# Calculate price after discount and tax
original_price = 100.00
discount_rate = 0.20
tax_rate = 0.08
 
# Calculate discount amount
discount = original_price * discount_rate
 
# Calculate price after discount
discounted_price = original_price - discount
 
# Calculate final price with tax
final_price = discounted_price * (1 + tax_rate)
print(f"Final price: ${final_price}")  # Output: Final price: $86.4
 
# Or in one expression (using parentheses to be clear):
final_price = (original_price * (1 - discount_rate)) * (1 + tax_rate)
print(f"Final price: ${final_price}")  # Output: Final price: $86.4

4.4.5) Рекомендации по использованию приоритета операторов

Используйте скобки для ясности, даже когда это не строго необходимо:

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

python
# clarity_with_parentheses.py
# These are equivalent, but the second is clearer:
result = 2 + 3 * 4
result = 2 + (3 * 4)  # Clearer: shows you know multiplication happens first
 
# Complex expressions benefit from parentheses:
result = (subtotal * tax_rate) + (subtotal * tip_rate)  # Clear intent

Разбивайте сложные выражения на шаги:

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

python
# breaking_into_steps.py
# Instead of this complex one-liner:
result = ((price * quantity) * (1 - discount)) * (1 + tax)
 
# Consider breaking it into steps:
subtotal = price * quantity
discounted = subtotal * (1 - discount)
final = discounted * (1 + tax)
result = final

Версия в несколько шагов легче читается, отлаживается и проверяется. Не жертвуйте понятностью ради краткости.

Если сомневаетесь — используйте скобки:

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

Понимание приоритета операторов помогает писать корректные выражения и читать чужой код. Главный принцип: при комбинировании операций думайте о порядке вычисления и используйте скобки, чтобы явно выразить намерение.

4.5) Смешивание целых чисел и float в выражениях

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

4.5.1) Правило продвижения типа (type promotion)

Когда Python выполняет арифметическую операцию, в которой участвуют и целое число, и float, он автоматически преобразует (или «продвигает») целое число к float перед выполнением операции. Результат всегда будет float:

python
# type_promotion.py
# Integer + Float = Float
result = 10 + 3.5
print(result)  # Output: 13.5
print(type(result))  # Output: <class 'float'>
 
# Float + Integer = Float (order doesn't matter)
result = 3.5 + 10
print(result)  # Output: 13.5
print(type(result))  # Output: <class 'float'>
 
# This applies to all arithmetic operators
result = 5 * 2.0
print(result)  # Output: 10.0 (float, not int)
print(type(result))  # Output: <class 'float'>

Почему Python так делает? Потому что float может представлять как целые, так и дробные числа, а int — дробные не может. Преобразование к float сохраняет всю информацию и избегает потери точности.

Вот наглядное представление того, как Python выбирает тип результата:

Yes

No

Yes

No

Операция с
int и float

Какой-нибудь
операнд — float?

Результат — float

Оператор
деления /?

Результат — int

4.5.2) Продвижение типов в сложных выражениях

Когда выражение содержит несколько операций со смешанными типами, Python применяет правило продвижения на каждом шаге:

python
# complex_mixed_types.py
# Multiple operations with mixed types
result = 10 + 3.5 * 2
print(result)  # Output: 17.0
print(type(result))  # Output: <class 'float'>
 
# What happens step by step:
# 1. 3.5 * 2 → 3.5 * 2.0 (2 promoted to float) → 7.0 (float)
# 2. 10 + 7.0 → 10.0 + 7.0 (10 promoted to float) → 17.0 (float)
 
# Another example
result = 5 / 2 + 3
print(result)  # Output: 5.5
print(type(result))  # Output: <class 'float'>
 
# Step by step:
# 1. 5 / 2 → 2.5 (division always produces float)
# 2. 2.5 + 3 → 2.5 + 3.0 (3 promoted to float) → 5.5 (float)

Как только любая операция в выражении произведет float, все последующие операции с этим результатом также будут работать с float.

4.5.3) Особый случай: обычное деление всегда возвращает float

Помните из раздела 4.2, что оператор / всегда возвращает float, даже при делении двух целых:

python
# division_always_float.py
# Even when the result is a whole number
result = 10 / 2
print(result)  # Output: 5.0 (not 5)
print(type(result))  # Output: <class 'float'>
 
# This means any expression with / will have a float result
result = 10 / 2 + 3  # 5.0 + 3 → 5.0 + 3.0 → 8.0
print(result)  # Output: 8.0
print(type(result))  # Output: <class 'float'>

Если вы хотите получить целый результат деления, используйте целочисленное деление // (но помните, что оно округляет вниз):

python
# floor_division_integer.py
# Floor division with integers produces an integer
result = 10 // 2
print(result)  # Output: 5 (integer)
print(type(result))  # Output: <class 'int'>
 
# But floor division with any float produces a float
result = 10.0 // 2
print(result)  # Output: 5.0 (float)
print(type(result))  # Output: <class 'float'>

4.5.4) Практические последствия смешивания типов

Понимание продвижения типов помогает предсказывать и контролировать типы результатов:

python
# practical_type_mixing.py
# Calculating price per item
total_cost = 47.50
num_items = 5
 
price_per_item = total_cost / num_items  # Float / int → float
print(f"Price per item: ${price_per_item}")
# Output: Price per item: $4.75
 
# Calculating average (will be float even if inputs are integers)
total_points = 450
num_tests = 5
 
average = total_points / num_tests  # Int / int → float
print(f"Average: {average}")  # Output: Average: 90.0
 
# If you need an integer result, convert explicitly
average_rounded = int(total_points / num_tests)
print(f"Average (as integer): {average_rounded}")
# Output: Average (as integer): 90

4.5.5) Когда важны целочисленные результаты

Иногда вам специально нужны целые результаты — для подсчета, индексации или других дискретных операций. Вот как гарантировать получение целых:

python
# ensuring_integer_results.py
# Using floor division when you need integer results
items = 47
items_per_box = 12
 
# How many complete boxes?
complete_boxes = items // items_per_box  # Integer result
print(f"Complete boxes: {complete_boxes}")
# Output: Complete boxes: 3
 
# If you use regular division, you get a float
boxes_float = items / items_per_box
print(f"Boxes (float): {boxes_float}")
# Output: Boxes (float): 3.9166666666666665
 
# Converting float to integer (truncates toward zero)
boxes_truncated = int(boxes_float)
print(f"Boxes (truncated): {boxes_truncated}")
# Output: Boxes (truncated): 3

Обратите внимание на разницу: // округляет вниз (к минус бесконечности), а int() усекает к нулю. Для положительных чисел результат одинаковый, но для отрицательных — различается:

python
# truncation_vs_floor.py
# For positive numbers: same result
print(7 // 2)    # Output: 3
print(int(7/2))  # Output: 3
 
# For negative numbers: different results
print(-7 // 2)    # Output: -4 (rounds down toward negative infinity)
print(int(-7/2))  # Output: -3 (truncates toward zero)

4.5.6) Избежание неожиданных результатов с float

Иногда вы можете быть удивлены, получив float, когда ожидали целое. Обычно это связано с делением или смешиванием типов:

python
# unexpected_floats.py
# Calculating average - result is always float because of division
count = 10
total = 100
average = total / count
print(average)  # Output: 10.0 (float, even though it's a whole number)
 
# If you need an integer and you know it divides evenly:
average_int = total // count
print(average_int)  # Output: 10 (integer)
 
# Or convert explicitly:
average_int = int(total / count)
print(average_int)  # Output: 10 (integer)

Главный вывод: правила продвижения типов в Python спроектированы для сохранения точности и предотвращения потери данных. Когда вы смешиваете int и float или используете обычное деление, ожидайте результат float. Если нужны целые числа, используйте целочисленное деление или явное преобразование, но учитывайте, как они округляют.

4.6) Полезные числовые встроенные функции: abs(), min(), max() и pow()

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

4.6.1) Модуль числа с abs()

Функция abs() возвращает модуль числа — расстояние от нуля, без учета знака:

python
# absolute_value.py
# Absolute value of negative numbers
result = abs(-42)
print(result)  # Output: 42
 
# Absolute value of positive numbers (unchanged)
result = abs(42)
print(result)  # Output: 42
 
# Works with floats too
result = abs(-3.14)
print(result)  # Output: 3.14
 
# Absolute value of zero is zero
result = abs(0)
print(result)  # Output: 0

Функция модуля полезна, когда вас интересует величина, но не направление:

python
# practical_abs.py
# Calculate temperature difference (magnitude only)
morning_temp = 45.5
evening_temp = 38.2
 
difference = abs(evening_temp - morning_temp)
print(f"Temperature changed by {difference} degrees")
# Output: Temperature changed by 7.3 degrees
 
# Calculate distance between two points (always positive)
position1 = 10
position2 = 25
 
distance = abs(position2 - position1)
print(f"Distance: {distance}")  # Output: Distance: 15
 
# Works the same regardless of order
distance = abs(position1 - position2)
print(f"Distance: {distance}")  # Output: Distance: 15

4.6.2) Поиск минимума и максимума с min() и max()

Функция min() возвращает наименьший из своих аргументов, а max() — наибольший:

python
# min_max_basic.py
# Find minimum of two numbers
smallest = min(10, 25)
print(smallest)  # Output: 10
 
# Find maximum of two numbers
largest = max(10, 25)
print(largest)  # Output: 25
 
# Works with more than two arguments
smallest = min(5, 12, 3, 18, 7)
print(smallest)  # Output: 3
 
largest = max(5, 12, 3, 18, 7)
print(largest)  # Output: 18
 
# Works with floats and mixed types
smallest = min(3.5, 2, 4.1, 1.9)
print(smallest)  # Output: 1.9

Эти функции незаменимы для поиска крайних значений в данных:

python
# practical_min_max.py
# Find the best and worst test scores
test1 = 85
test2 = 92
test3 = 78
test4 = 95
 
highest_score = max(test1, test2, test3, test4)
lowest_score = min(test1, test2, test3, test4)
 
print(f"Highest score: {highest_score}")  # Output: Highest score: 95
print(f"Lowest score: {lowest_score}")    # Output: Lowest score: 78
 
# Clamp a value within a range
value = 150
minimum = 0
maximum = 100
 
# Ensure value is not below minimum
value = max(value, minimum)
# Ensure value is not above maximum
value = min(value, maximum)
 
print(f"Clamped value: {value}")  # Output: Clamped value: 100

Шаблон «clamping» (ограничение значения в диапазоне) с использованием max() и min() часто используется, когда нужно удержать значение в определенных пределах:

python
# clamping_pattern.py
def clamp(value, min_value, max_value):
    """Constrain value to be within [min_value, max_value]"""
    return max(min_value, min(value, max_value))
 
# Test the clamp function
print(clamp(150, 0, 100))   # Output: 100 (too high, clamped to max)
print(clamp(-10, 0, 100))   # Output: 0 (too low, clamped to min)
print(clamp(50, 0, 100))    # Output: 50 (within range, unchanged)

4.6.3) Возведение в степень с pow()

Функция pow() возводит число в степень. В Python также есть оператор ** для возведения в степень, который используется чаще, но pow() предоставляет дополнительные возможности:

python
# exponentiation.py
# Using the ** operator (most common)
result = 2 ** 3  # 2 to the power of 3
print(result)  # Output: 8
 
result = 5 ** 2  # 5 squared
print(result)  # Output: 25
 
# Using pow() function (equivalent)
result = pow(2, 3)
print(result)  # Output: 8
 
result = pow(5, 2)
print(result)  # Output: 25
 
# Negative exponents give fractions
result = 2 ** -3  # 1 / (2^3) = 1/8
print(result)  # Output: 0.125
 
# Fractional exponents give roots
result = 9 ** 0.5  # Square root of 9
print(result)  # Output: 3.0
 
result = 8 ** (1/3)  # Cube root of 8
print(result)  # Output: 2.0

Функция pow() может принимать необязательный третий аргумент для модульного возведения в степень (полезно в криптографии и теории чисел, но выходит за рамки базовой арифметики):

python
# modular_exponentiation.py
# pow(base, exponent, modulus) computes (base ** exponent) % modulus efficiently
result = pow(2, 10, 100)  # (2^10) % 100
print(result)  # Output: 24
 
# This is more efficient than computing separately for large numbers:
# result = (2 ** 10) % 100  # Same result, but less efficient for large numbers

В большинстве повседневных задач оператор ** удобнее, чем pow():

python
# practical_exponentiation.py
# Calculate compound interest: A = P(1 + r)^t
principal = 1000  # Initial amount
rate = 0.05       # 5% interest rate
years = 10
 
amount = principal * (1 + rate) ** years
print(f"Amount after {years} years: ${amount:.2f}")
# Output: Amount after 10 years: $1628.89
 
# Calculate area of a square
side_length = 5
area = side_length ** 2
print(f"Area: {area}")  # Output: Area: 25
 
# Calculate volume of a cube
side_length = 3
volume = side_length ** 3
print(f"Volume: {volume}")  # Output: Volume: 27

4.6.4) Комбинирование встроенных функций

Эти функции хорошо работают вместе для решения распространенных задач:

python
# combining_functions.py
# Find the range (difference between max and min)
values = [15, 42, 8, 23, 37]
value_range = max(values) - min(values)
print(f"Range: {value_range}")  # Output: Range: 34
 
# Note: We're using a list here (we'll learn about lists in detail in Chapter 13)
# For now, just understand that max() and min() can work with a list of values
 
# Calculate distance between two points in 2D space
x1, y1 = 3, 4
x2, y2 = 6, 8
 
# Distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
# We'll use ** for squaring (square root comes in section 4.11)
distance_squared = (x2 - x1) ** 2 + (y2 - y1) ** 2
distance = distance_squared ** 0.5  # Square root via fractional exponent
print(f"Distance: {distance}")  # Output: Distance: 5.0

Эти встроенные функции — фундаментальные инструменты в программировании на Python. Они эффективны, хорошо протестированы и одинаково работают с разными числовыми типами. Используйте их, когда нужны такие операции — нет необходимости писать свои реализации.

4.7) Округление чисел с round()

При работе с числами с плавающей точкой вам часто нужно округлять результаты до заданного количества знаков после запятой для отображения, вычислений или хранения. В Python для этого есть функция round().

4.7.1) Базовое округление с round()

Функция round() принимает число и округляет его до ближайшего целого:

python
# basic_rounding.py
# Round to nearest integer
result = round(3.7)
print(result)  # Output: 4
 
result = round(3.2)
print(result)  # Output: 3
 
# Exactly halfway rounds to nearest even number (banker's rounding)
result = round(2.5)
print(result)  # Output: 2 (rounds to even)
 
result = round(3.5)
print(result)  # Output: 4 (rounds to even)
 
# Negative numbers work too
result = round(-3.7)
print(result)  # Output: -4
 
result = round(-3.2)
print(result)  # Output: -3

Обратите внимание на поведение при округлении чисел ровно посередине между двумя целыми (например, 2.5 или 3.5). Python использует схему «round half to even» (банковское округление), при которой результат округляется к ближайшему четному числу. Это уменьшает смещение при многократных операциях округления. В большинстве повседневных задач это редко имеет значение, но полезно знать.

4.7.2) Округление до заданного числа знаков после запятой

Функция round() принимает необязательный второй аргумент, задающий количество сохраняемых знаков после запятой:

python
# decimal_places.py
# Round to 2 decimal places
result = round(3.14159, 2)
print(result)  # Output: 3.14
 
# Round to 1 decimal place
result = round(3.14159, 1)
print(result)  # Output: 3.1
 
# Round to 3 decimal places
result = round(2.71828, 3)
print(result)  # Output: 2.718
 
# You can round to 0 decimal places (same as omitting the argument)
result = round(3.7, 0)
print(result)  # Output: 4.0 (note: returns float, not int)

Когда вы указываете количество знаков после запятой, round() возвращает float (даже при округлении до 0 знаков). Когда второй аргумент опущен, возвращается целое число.

4.7.3) Практическое использование округления

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

python
# practical_rounding.py
# Display prices with 2 decimal places
price = 19.99
tax_rate = 0.08
total = price * (1 + tax_rate)
 
print(f"Total (unrounded): ${total}")  # Output: Total (unrounded): $21.5892
print(f"Total (rounded): ${round(total, 2)}")  # Output: Total (rounded): $21.59
 
# Calculate and display average
total_score = 456
num_tests = 7
average = total_score / num_tests
 
print(f"Average (unrounded): {average}")  # Output: Average (unrounded): 65.14285714285714
print(f"Average (rounded): {round(average, 2)}")  # Output: Average (rounded): 65.14
 
# Round measurements to reasonable precision
distance_meters = 123.456789
distance_rounded = round(distance_meters, 1)
print(f"Distance: {distance_rounded} meters")  # Output: Distance: 123.5 meters

4.7.4) Округление vs усечение vs floor/ceil

Важно понимать, что округление отличается от усечения (удаления дробной части) и операций floor/ceil:

python
# rounding_vs_others.py
value = 3.7
 
# Rounding: nearest integer
rounded = round(value)
print(f"Rounded: {rounded}")  # Output: Rounded: 4
 
# Truncating: remove decimal part (convert to int)
truncated = int(value)
print(f"Truncated: {truncated}")  # Output: Truncated: 3
 
# We'll see floor and ceil in section 4.11, but briefly:
# Floor: largest integer <= value (always rounds down)
# Ceiling: smallest integer >= value (always rounds up)

Для отрицательных чисел различия еще заметнее:

python
# negative_rounding.py
value = -3.7
 
# Rounding: nearest integer
rounded = round(value)
print(f"Rounded: {rounded}")  # Output: Rounded: -4
 
# Truncating: toward zero
truncated = int(value)
print(f"Truncated: {truncated}")  # Output: Truncated: -3
 
# Floor (rounds down toward negative infinity): -4
# Ceiling (rounds up toward positive infinity): -3

4.7.5) Важные нюансы при округлении

Точность float может давать неожиданные результаты:

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

python
# rounding_surprises.py
# Sometimes rounding doesn't give the exact decimal you expect
value = 2.675
rounded = round(value, 2)
print(rounded)  # Output: 2.67 (not 2.68 as you might expect)
 
# This happens because 2.675 can't be represented exactly in binary floating-point
# The actual stored value is slightly less than 2.675

В большинстве практических задач это не проблема. Но если вы работаете с финансовыми расчетами, где важна точная десятичная арифметика, вам может понадобиться модуль decimal (мы не будем рассматривать его в этой книге, но полезно знать о нем).

Округление для отображения vs для вычислений:

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

python
# rounding_for_display.py
price1 = 19.99
price2 = 15.49
tax_rate = 0.08
 
# Calculate with full precision
subtotal = price1 + price2
tax = subtotal * tax_rate
total = subtotal + tax
 
# Round only for display
print(f"Subtotal: ${round(subtotal, 2)}")  # Output: Subtotal: $35.48
print(f"Tax: ${round(tax, 2)}")            # Output: Tax: $2.84
print(f"Total: ${round(total, 2)}")        # Output: Total: $38.32
 
# The variables still contain full precision
print(f"Total (full precision): ${total}")
# Output: Total (full precision): $38.3184

Функция round() — одна из самых часто используемых встроенных функций Python. Применяйте ее, когда нужно представить числовые результаты в читаемом виде или ограничить точность для конкретной задачи.

4.8) Распространенные числовые шаблоны (счетчики, суммы, средние)

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

4.8.1) Счетчики: отслеживание количества

Счетчик — это переменная, которая отслеживает, сколько раз что-то произошло. Вы инициализируете ее нулем и увеличиваете каждый раз, когда событие происходит:

python
# basic_counter.py
# Count how many numbers we've processed
count = 0  # Initialize counter
 
# Process first number
count += 1
print(f"Processed {count} number(s)")  # Output: Processed 1 number(s)
 
# Process second number
count += 1
print(f"Processed {count} number(s)")  # Output: Processed 2 number(s)
 
# Process third number
count += 1
print(f"Processed {count} number(s)")  # Output: Processed 3 number(s)

В реальных программах вы обычно используете счетчики внутри циклов (которые мы изучим в главах 10 и 11). Пока что важно понять шаблон: начать с нуля и добавлять один каждый раз.

Счетчики могут отслеживать разные типы событий:

python
# multiple_counters.py
# Track different categories
even_count = 0
odd_count = 0
 
# Check number 1
number = 4
if number % 2 == 0:
    even_count += 1
else:
    odd_count += 1
 
# Check number 2
number = 7
if number % 2 == 0:
    even_count += 1
else:
    odd_count += 1
 
# Check number 3
number = 10
if number % 2 == 0:
    even_count += 1
else:
    odd_count += 1
 
print(f"Even numbers: {even_count}")  # Output: Even numbers: 2
print(f"Odd numbers: {odd_count}")    # Output: Odd numbers: 1

4.8.2) Аккумуляторы: накапливание сумм

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

python
# basic_accumulator.py
# Calculate total sales
total_sales = 0  # Initialize accumulator
 
# First sale
sale = 19.99
total_sales += sale
print(f"Total so far: ${total_sales}")  # Output: Total so far: $19.99
 
# Second sale
sale = 34.50
total_sales += sale
print(f"Total so far: ${total_sales}")  # Output: Total so far: $54.49
 
# Third sale
sale = 12.00
total_sales += sale
print(f"Total so far: ${total_sales}")  # Output: Total so far: $66.49

Аккумуляторы — основа обработки данных. Они позволяют агрегировать значения по мере их обработки:

python
# multiple_accumulators.py
# Track both total and count to calculate average later
total_score = 0
count = 0
 
# Process score 1
score = 85
total_score += score
count += 1
 
# Process score 2
score = 92
total_score += score
count += 1
 
# Process score 3
score = 78
total_score += score
count += 1
 
print(f"Total score: {total_score}")  # Output: Total score: 255
print(f"Number of scores: {count}")   # Output: Number of scores: 3

4.8.3) Вычисление средних значений

Среднее сочетает в себе шаблоны счетчика и аккумулятора: вам нужна и сумма (аккумулятор), и количество (счетчик), затем вы делите одно на другое:

python
# calculating_average.py
# Calculate average of test scores
total_score = 0
count = 0
 
# Add scores
total_score += 85
count += 1
 
total_score += 92
count += 1
 
total_score += 78
count += 1
 
total_score += 88
count += 1
 
# Calculate average
average = total_score / count
print(f"Average score: {average}")  # Output: Average score: 85.75
 
# Often you'll want to round the average
average_rounded = round(average, 2)
print(f"Average (rounded): {average_rounded}")  # Output: Average (rounded): 85.75

Важно: всегда проверяйте деление на ноль:

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

python
# safe_average.py
total_score = 0
count = 0
 
# If no scores were added, count is still 0
# Dividing by zero causes an error!
 
if count > 0:
    average = total_score / count
    print(f"Average: {average}")
else:
    print("No scores to average")
    # Output: No scores to average

Мы подробнее поговорим о таких условиях в главе 8.

4.8.4) Нахождение текущего максимума и минимума

Иногда нужно отслеживать наибольшее или наименьшее значение, встречавшееся до сих пор. Вы можете использовать функции max() и min(), чтобы реализовать это:

python
# running_max_min_simplified.py
# Track highest and lowest using max() and min()
 
highest_temp = 72
lowest_temp = 72
 
# Update with new temperature
current_temp = 85
highest_temp = max(highest_temp, current_temp)
lowest_temp = min(lowest_temp, current_temp)
 
# Update with another temperature
current_temp = 68
highest_temp = max(highest_temp, current_temp)
lowest_temp = min(lowest_temp, current_temp)
 
print(f"High: {highest_temp}, Low: {lowest_temp}")
# Output: High: 85, Low: 68

4.9) Побитовые операторы для целых чисел: &, |, ^, <<, >> (краткий обзор)

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

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

4.9.1) Понимание двоичного представления (краткое введение)

Компьютеры хранят целые числа как последовательности битов (0 и 1). Например:

  • Десятичное 5 — это двоичное 101 (1×4 + 0×2 + 1×1)
  • Десятичное 12 — это двоичное 1100 (1×8 + 1×4 + 0×2 + 0×1)

Функция bin() в Python показывает двоичное представление целого числа:

python
# binary_representation.py
# See binary representation of integers
print(bin(5))   # Output: 0b101
print(bin(12))  # Output: 0b1100
print(bin(255)) # Output: 0b11111111
 
# The 0b prefix indicates binary notation

4.9.2) Побитовое И (&)

Оператор & выполняет побитовое И: каждый бит результата равен 1, только если соответствующие биты обоих операндов равны 1:

python
# bitwise_and.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 & 10
print(result)  # Output: 8
print(bin(result))  # Output: 0b1000
 
# How it works:
#   1100  (12)
# & 1010  (10)
# ------
#   1000  (8)

Побитовое И часто используют для проверки, установлен ли определенный бит (проверка флагов):

python
# checking_flags.py
# File permissions example (simplified)
READ = 4    # 100 in binary
WRITE = 2   # 010 in binary
EXECUTE = 1 # 001 in binary
 
permissions = 6  # 110 in binary (READ + WRITE)
 
# Check if READ permission is set
has_read = (permissions & READ) != 0
print(f"Has read: {has_read}")  # Output: Has read: True
 
# Check if EXECUTE permission is set
has_execute = (permissions & EXECUTE) != 0
print(f"Has execute: {has_execute}")  # Output: Has execute: False

4.9.3) Побитовое ИЛИ (|)

Оператор | выполняет побитовое ИЛИ: каждый бит результата равен 1, если соответствующий бит хотя бы одного операнда равен 1:

python
# bitwise_or.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 | 10
print(result)  # Output: 14
print(bin(result))  # Output: 0b1110
 
# How it works:
#   1100  (12)
# | 1010  (10)
# ------
#   1110  (14)

Побитовое ИЛИ используют для установки (комбинирования) флагов:

python
# combining_flags.py
READ = 4    # 100 in binary
WRITE = 2   # 010 in binary
EXECUTE = 1 # 001 in binary
 
# Grant READ and WRITE permissions
permissions = READ | WRITE
print(f"Permissions: {permissions}")  # Output: Permissions: 6
print(bin(permissions))  # Output: 0b110
 
# Add EXECUTE permission
permissions = permissions | EXECUTE
print(f"Permissions: {permissions}")  # Output: Permissions: 7
print(bin(permissions))  # Output: 0b111

4.9.4) Побитовое XOR (^)

Оператор ^ выполняет побитовое XOR (исключающее ИЛИ): каждый бит результата равен 1, если соответствующие биты операндов различаются:

python
# bitwise_xor.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 ^ 10
print(result)  # Output: 6
print(bin(result))  # Output: 0b110
 
# How it works:
#   1100  (12)
# ^ 1010  (10)
# ------
#   0110  (6)

XOR имеет интересные свойства, например, позволяет инвертировать биты или использоваться для простого шифрования:

python
# xor_properties.py
# XOR with itself gives 0
result = 5 ^ 5
print(result)  # Output: 0
 
# XOR with 0 gives the original number
result = 5 ^ 0
print(result)  # Output: 5
 
# XOR is its own inverse (useful for simple encryption)
original = 42
key = 123
encrypted = original ^ key
decrypted = encrypted ^ key
print(f"Original: {original}, Encrypted: {encrypted}, Decrypted: {decrypted}")
# Output: Original: 42, Encrypted: 81, Decrypted: 42

4.9.5) Сдвиги влево (<<) и вправо (>>)

Оператор << сдвигает биты влево, а >> — вправо:

python
# bit_shifting.py
# Left shift: multiply by powers of 2
result = 5 << 1  # Shift left by 1 bit
print(result)  # Output: 10
# 5 is 101 in binary, shifted left becomes 1010 (10)
 
result = 5 << 2  # Shift left by 2 bits
print(result)  # Output: 20
# 5 is 101 in binary, shifted left becomes 10100 (20)
 
# Right shift: divide by powers of 2 (floor division)
result = 20 >> 1  # Shift right by 1 bit
print(result)  # Output: 10
# 20 is 10100 in binary, shifted right becomes 1010 (10)
 
result = 20 >> 2  # Shift right by 2 bits
print(result)  # Output: 5
# 20 is 10100 in binary, shifted right becomes 101 (5)

Сдвиг влево на n бит умножает на 2^n, а сдвиг вправо делит на 2^n (целочисленное деление):

python
# shift_as_multiplication.py
# Left shift multiplies by powers of 2
print(3 << 1)  # Output: 6 (3 * 2^1 = 3 * 2)
print(3 << 2)  # Output: 12 (3 * 2^2 = 3 * 4)
print(3 << 3)  # Output: 24 (3 * 2^3 = 3 * 8)
 
# Right shift divides by powers of 2
print(24 >> 1)  # Output: 12 (24 // 2^1 = 24 // 2)
print(24 >> 2)  # Output: 6 (24 // 2^2 = 24 // 4)
print(24 >> 3)  # Output: 3 (24 // 2^3 = 24 // 8)

4.9.6) Когда использовать побитовые операторы

Побитовые операторы полезны в определенных сценариях:

  • Работа с бинарными флагами и правами доступа (например, файловые права в Unix/Linux)
  • Низкоуровневая обработка данных (упаковка нескольких значений в одно целое число)
  • Сетевое программирование (манипуляции с IP-адресами, флагами протоколов)
  • Графика и игровые приложения (манипуляции цветами, обнаружение столкновений)
  • Оптимизация производительности (сдвиг быстрее умножения/деления на степени 2)

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

Примечание: этот обзор покрывает основы. Побитовые операции с отрицательными числами (с использованием представления «дополнительный код») сложнее и выходят за рамки этого введения. Пока сосредоточьтесь на понимании того, что эти операторы существуют и в общих чертах, что они делают.

4.10) Точность чисел с плавающей точкой и ошибки округления (простое объяснение)

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

4.10.1) Почему числа с плавающей точкой не всегда точны

Компьютеры хранят числа с плавающей точкой в двоичном виде (основание 2), а не в десятичном (основание 10). Некоторые десятичные дроби, которые нам кажутся простыми, нельзя представить точно в двоичной системе, так же как 1/3 нельзя представить точно в десятичной (0.333333... продолжается бесконечно).

Вот удивительный пример:

python
# floating_point_surprise.py
# This seems like it should be exactly 0.3
result = 0.1 + 0.2
print(result)  # Output: 0.30000000000000004 (not exactly 0.3!)
 
# Check if it equals 0.3
print(result == 0.3)  # Output: False

Это не ошибка в Python — это фундаментальное ограничение того, как компьютеры представляют числа с плавающей точкой. Десятичное число 0.1 нельзя точно представить в двоичной форме с плавающей точкой, как и 1/3 нельзя точно представить в десятичной системе.

4.10.2) Понимание проблемы представления

Рассмотрим еще примеры такого поведения:

python
# more_precision_examples.py
# Simple decimal values that aren't exact in binary
print(0.1)  # Output: 0.1 (Python rounds for display)
print(repr(0.1))  # Output: 0.1 (still rounded)
 
# But the actual stored value has tiny errors
print(0.1 + 0.1 + 0.1)  # Output: 0.30000000000000004
 
# Multiplication can accumulate these errors
result = 0.1 * 3
print(result)  # Output: 0.30000000000000004
 
# Some numbers are exact (powers of 2)
print(0.5)  # Output: 0.5 (exact)
print(0.25)  # Output: 0.25 (exact)
print(0.125)  # Output: 0.125 (exact)

Числа, являющиеся степенями 2 (например, 0.5, 0.25, 0.125) или суммами степеней 2, можно представить точно. Но большинство десятичных дробей — нет.

4.10.3) Практические последствия

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

Сравнение чисел с плавающей точкой:

python
# comparing_floats.py
# Direct equality comparison can fail
a = 0.1 + 0.2
b = 0.3
 
print(a == b)  # Output: False (due to tiny difference)
 
# Better: check if they're close enough
difference = abs(a - b)
tolerance = 0.0001  # How close is "close enough"?
 
print(difference < tolerance)  # Output: True (they're close enough)
 
# Python 3.5+ provides math.isclose() for this (we'll see in section 4.11)

Накопление ошибок при повторных вычислениях:

python
# accumulated_errors.py
# Adding 0.1 ten times
total = 0.0
for i in range(10):
    total += 0.1
 
print(total)  # Output: 0.9999999999999999 (not exactly 1.0)
print(total == 1.0)  # Output: False
 
# The error accumulates with each addition

Финансовые вычисления:

python
# financial_calculations.py
# Money calculations can have surprising results
price = 0.10
quantity = 3
total = price * quantity
 
print(total)  # Output: 0.30000000000000004
 
# For financial calculations, consider rounding to cents
total_cents = round(total * 100)  # Convert to cents
total_dollars = total_cents / 100
print(total_dollars)  # Output: 0.3
 
# Or use Python's decimal module for exact decimal arithmetic
# (beyond the scope of this chapter, but worth knowing about)

4.10.4) Стратегии работы с числами с плавающей точкой

Округляйте результаты для отображения:

python
# rounding_for_display.py
result = 0.1 + 0.2
print(f"Result: {round(result, 2)}")  # Output: Result: 0.3
 
# Or use formatted output (we'll learn more in Chapter 6)
print(f"Result: {result:.2f}")  # Output: Result: 0.30

Не сравнивайте float на точное равенство:

python
# safe_float_comparison.py
a = 0.1 + 0.2
b = 0.3
 
# Instead of: if a == b:
# Use: if they're close enough
if abs(a - b) < 0.0001:
    print("Close enough to equal")
# Output: Close enough to equal

Будьте внимательны, когда важна точность:

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

  • модуль decimal для точной десятичной арифметики
  • модуль fractions для точной рациональной арифметики
  • аккуратное округление в подходящих местах вычислений

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

4.10.5) Когда точность float не важна

Для большинства повседневных задач точность чисел с плавающей точкой более чем достаточна:

python
# when_precision_is_fine.py
# Calculating area
length = 5.5
width = 3.2
area = length * width
print(f"Area: {area:.2f} square meters")  # Output: Area: 17.60 square meters
 
# Converting temperature
fahrenheit = 98.6
celsius = (fahrenheit - 32) * 5 / 9
print(f"Temperature: {celsius:.1f}°C")  # Output: Temperature: 37.0°C
 
# Calculating average
total = 456.78
count = 7
average = total / count
print(f"Average: {average:.2f}")  # Output: Average: 65.25

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

4.10.6) Главный вывод

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

  1. Не сравнивайте float на точное равенство — вместо этого проверяйте, достаточно ли они близки
  2. Округляйте результаты при выводе, чтобы не показывать бессмысленную «длинную» точность
  3. Помните о накоплении ошибок при повторных вычислениях
  4. Для финансовых расчетов рассмотрите возможность округления до нужной точности или использования модуля decimal

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

4.11) (Необязательно) Базовые математические функции модуля math (sqrt, floor, ceil, pi)

Встроенных операторов и функций Python достаточно для базовой арифметики, но для более продвинутых математических операций в Python есть модуль math. Модуль — это коллекция связанных функций и значений, которые вы можете использовать в своих программах. Мы подробно поговорим о модулях в главе 22, а сейчас кратко рассмотрим основы, необходимые для использования math.

4.11.1) Импорт модуля math

Чтобы использовать модуль math, нужно импортировать его в начале программы:

python
# importing_math.py
import math
 
# Now you can use functions from the math module
# by writing math.function_name()

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

4.11.2) Математические константы: pi и e

Модуль math предоставляет точные значения важных математических констант:

python
# math_constants.py
import math
 
# Pi (π): ratio of circle's circumference to diameter
print(math.pi)  # Output: 3.141592653589793
 
# Euler's number (e): base of natural logarithms
print(math.e)  # Output: 2.718281828459045
 
# Using pi to calculate circle properties
radius = 5
circumference = 2 * math.pi * radius
area = math.pi * radius ** 2
 
print(f"Circumference: {circumference:.2f}")  # Output: Circumference: 31.42
print(f"Area: {area:.2f}")  # Output: Area: 78.54

4.11.3) Квадратный корень с sqrt()

Функция sqrt() вычисляет квадратный корень числа:

python
# square_root.py
import math
 
# Square root of perfect squares
result = math.sqrt(16)
print(result)  # Output: 4.0
 
result = math.sqrt(25)
print(result)  # Output: 5.0
 
# Square root of non-perfect squares
result = math.sqrt(2)
print(result)  # Output: 1.4142135623730951
 
# Using sqrt in calculations
# Distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
x1, y1 = 3, 4
x2, y2 = 6, 8
 
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
print(f"Distance: {distance}")  # Output: Distance: 5.0

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

4.11.4) Округление вниз и вверх: floor() и ceil()

Функция floor() округляет вниз до ближайшего целого (к минус бесконечности), а ceil() округляет вверх (к плюс бесконечности):

python
# floor_and_ceil.py
import math
 
# Floor: rounds down
result = math.floor(3.7)
print(result)  # Output: 3
 
result = math.floor(3.2)
print(result)  # Output: 3
 
# Ceiling: rounds up
result = math.ceil(3.7)
print(result)  # Output: 4
 
result = math.ceil(3.2)
print(result)  # Output: 4
 
# With negative numbers
result = math.floor(-3.7)
print(result)  # Output: -4 (rounds toward negative infinity)
 
result = math.ceil(-3.7)
print(result)  # Output: -3 (rounds toward positive infinity)

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

python
# practical_floor_ceil.py
import math
 
# How many boxes needed to pack all items?
items = 47
items_per_box = 12
 
# Use ceil to round up (need a full box even if not completely filled)
boxes_needed = math.ceil(items / items_per_box)
print(f"Boxes needed: {boxes_needed}")  # Output: Boxes needed: 4
 
# How many complete pages for a document?
total_lines = 250
lines_per_page = 30
 
# Use floor to count only complete pages
complete_pages = math.floor(total_lines / lines_per_page)
print(f"Complete pages: {complete_pages}")  # Output: Complete pages: 8

4.11.5) Сравнение floor(), ceil(), round() и int()

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

python
# comparing_rounding_methods.py
import math
 
value = 3.7
 
print(f"Original: {value}")
print(f"floor(): {math.floor(value)}")  # Output: floor(): 3
print(f"ceil(): {math.ceil(value)}")    # Output: ceil(): 4
print(f"round(): {round(value)}")       # Output: round(): 4
print(f"int(): {int(value)}")           # Output: int(): 3
 
# With negative numbers, the differences are more apparent
value = -3.7
 
print(f"\nOriginal: {value}")
print(f"floor(): {math.floor(value)}")  # Output: floor(): -4
print(f"ceil(): {math.ceil(value)}")    # Output: ceil(): -3
print(f"round(): {round(value)}")       # Output: round(): -4
print(f"int(): {int(value)}")           # Output: int(): -3

Вот наглядное представление поведения этих функций:

Value: 3.7

floor: 3
rounds down

ceil: 4
rounds up

round: 4
rounds to nearest

int: 3
truncates toward zero

Value: -3.7

floor: -4
rounds down toward -∞

ceil: -3
rounds up toward +∞

round: -4
rounds to nearest

int: -3
truncates toward zero

4.11.6) Другие полезные функции модуля math

Модуль math содержит множество других функций. Вот еще несколько часто полезных:

python
# other_math_functions.py
import math
 
# Absolute value (also available as built-in abs())
result = math.fabs(-5.5)
print(result)  # Output: 5.5
 
# Power function (also available as ** operator)
result = math.pow(2, 3)
print(result)  # Output: 8.0
 
# Trigonometric functions (angles in radians)
result = math.sin(math.pi / 2)  # sin(90 degrees)
print(result)  # Output: 1.0
 
result = math.cos(0)  # cos(0 degrees)
print(result)  # Output: 1.0
 
# Logarithms
result = math.log(math.e)  # Natural log (base e)
print(result)  # Output: 1.0
 
result = math.log10(100)  # Log base 10
print(result)  # Output: 2.0
 
# Check if a float is close to another (Python 3.5+)
a = 0.1 + 0.2
b = 0.3
result = math.isclose(a, b)
print(result)  # Output: True

Функция isclose() особенно полезна для сравнения чисел с плавающей точкой (как мы обсуждали в разделе 4.10):

python
# isclose_example.py
import math
 
# Instead of direct equality comparison
a = 0.1 + 0.2
b = 0.3
 
# Don't do this:
# if a == b:  # This would be False
 
# Do this instead:
if math.isclose(a, b):
    print("Values are close enough")
# Output: Values are close enough
 
# You can specify the tolerance
if math.isclose(a, b, rel_tol=1e-9):  # Very strict tolerance
    print("Values are very close")
# Output: Values are very close

4.11.7) Когда использовать модуль math

Используйте модуль math, когда вам нужны:

  • математические константы (π, e)
  • квадратные корни и другие корни
  • тригонометрические функции (sin, cos, tan)
  • логарифмические и экспоненциальные функции
  • точный контроль округления (floor, ceil)
  • сравнение чисел с плавающей точкой (isclose)

Для базовой арифметики (+, -, *, /, //, %, **) используйте встроенные операторы Python. Для более продвинутых математических операций импортируйте и используйте модуль math.

Модуль math входит в стандартную библиотеку Python, то есть всегда доступен — нужно только импортировать его. Мы рассмотрим больше модулей и изучим систему импорта подробно в главе 22.


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

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

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

© 2025. Primesoft Co., Ltd.
support@primesoft.ai