4. Работа с числами
В главе 3 вы узнали, как создавать переменные и работать с основными числовыми типами Python: целыми числами и числами с плавающей точкой. Теперь пришло время заставить эти числа работать. В этой главе вы узнаете, как выполнять вычисления, комбинировать операции и использовать встроенные инструменты Python для работы с числовыми данными.
Числа — фундамент программирования. Считаете ли вы итоговые суммы, обрабатываете измерения, управляете запасами или анализируете данные, вам нужно выполнять арифметические операции. Python делает числовые операции простыми и интуитивно понятными, но есть важные детали, которые нужно понимать — особенно о том, как работают разные виды деления, как взаимодействуют операторы и как ведут себя числа с плавающей точкой.
К концу этой главы вы будете уверенно выполнять вычисления, понимать поведение операторов и использовать числовые функции Python для решения реальных задач.
4.1) Базовая арифметика: сложение, вычитание и умножение
Начнем с самых фундаментальных арифметических операций. Python использует знакомые символы для базовой математики, и эти операции работают так, как вы ожидаете из повседневной арифметики.
4.1.1) Сложение с оператором +
Оператор + складывает два числа. Он работает как с целыми, так и с числами с плавающей точкой:
# 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 автоматически преобразует результат в число с плавающей точкой, чтобы сохранить десятичную точность:
# mixed_addition.py
result = 10 + 3.5
print(result) # Output: 13.5
print(type(result)) # Output: <class 'float'>Это автоматическое преобразование происходит потому, что float может представлять как целые числа, так и дробные, тогда как целое число не может представлять дробную часть. Python выбирает тип, при котором информация не теряется.
4.1.2) Вычитание с оператором -
Оператор - вычитает второе число из первого:
# 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, когда вы смешиваете типы:
# mixed_subtraction.py
result = 100 - 0.01
print(result) # Output: 99.99
print(type(result)) # Output: <class 'float'>4.1.3) Умножение с оператором *
Оператор * умножает два числа:
# 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:
# 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) Практические примеры с базовой арифметикой
Посмотрим, как эти операции работают вместе в реалистичных сценариях:
# 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# 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, даже при делении двух целых чисел:
# 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 (но все равно округленный вниз):
# 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'>«Округление вниз» означает движение к минус бесконечности, а не просто отбрасывание десятичной части. Это важно для отрицательных чисел:
# 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 ...Целочисленное деление полезно, когда нужно разбить элементы на группы или посчитать, сколько полных единиц помещается в количестве:
# 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: 24.2.3) Остаток от деления (modulo) с %
Оператор % (называется «modulo» или «mod») возвращает остаток после деления. Он отвечает на вопрос: «После деления, что осталось?»
# 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Оператор % невероятно полезен для нескольких распространенных шаблонов программирования.
Проверка, четное число или нечетное:
# 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Получение оставшихся элементов:
# leftover_items.py
eggs = 50
eggs_per_dozen = 12
leftover = eggs % eggs_per_dozen
print(f"Leftover eggs: {leftover}") # Output: Leftover eggs: 2Зацикливание значений в диапазоне (как на часах):
# 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: 114.2.4) Связь между //, % и /
Целочисленное деление и modulo тесно связаны. Вместе они дают вам полный результат деления:
# 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) Выбор подходящего оператора деления
Краткое руководство по выбору оператора:
- Используйте
/, когда вам нужен точный математический результат с десятичной частью (чаще всего в расчетах) - Используйте
//, когда нужно посчитать, сколько полных групп помещается (например, дюжины, часы, страницы) - Используйте
%, когда нужен остаток или оставшееся количество (например, проверка четности, зацикливание значений, поиск «хвостов»)
# 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.8333333333333334.3) Сокращенные операторы присваивания
При программировании вам часто нужно обновлять переменную, выполняя операцию над ее текущим значением. Например, добавлять к накапливаемой сумме, вычитать из баланса или умножать значение на коэффициент. Python предоставляет краткий способ сделать это с помощью сокращенных операторов присваивания.
4.3.1) Что такое сокращенные операторы присваивания
Сокращенный оператор присваивания объединяет арифметическую операцию с присваиванием. Вместо того чтобы писать:
# traditional_update.py
count = 10
count = count + 5 # Add 5 to count
print(count) # Output: 15вы можете написать:
# augmented_update.py
count = 10
count += 5 # Same as: count = count + 5
print(count) # Output: 15Обе версии делают одно и то же, но сокращенная более лаконична и наглядно показывает намерение: «увеличить count на 5».
4.3.2) Все сокращенные операторы присваивания
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: 84.3.3) Типичные случаи использования сокращенного присваивания
Сокращенные операторы присваивания особенно удобны в нескольких распространенных шаблонах программирования.
Накопление суммы:
# 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Подсчет количества:
# 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Обновление баланса:
# 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Применение повторяющихся операций:
# 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.100000000000024.3.4) Важные детали о сокращенном присваивании
Сокращенное присваивание создает новый объект для неизменяемых типов:
Помните из главы 3, что числа — неизменяемые: вы не можете изменить значение числа, можно только создать новое число. Когда вы пишете x += 5, Python создает новый объект-число и привязывает его к x. Старый объект-число отбрасывается (мы подробнее рассмотрим это в главе 17, когда будем обсуждать объектную модель 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 — это удобное сокращение, а не принципиально другая операция.
Нельзя использовать сокращенное присваивание до создания переменной:
# 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Преобразования типов происходят так же:
# 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 - Более читаемым: намерение сразу понятно
- Менее подверженным ошибкам: меньше риск опечататься в имени переменной
Сравните эти две версии:
# 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.
# 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Вот порядок приоритета для операторов, которые мы уже изучили (от высшего к низшему):
- Скобки
()— наивысший приоритет, вычисляются первыми - Возведение в степень
**— (раздел 4.6) - Умножение, деление, целочисленное деление, остаток
*,/,//,%— один уровень, слева направо - Сложение, вычитание
+,-— один уровень, слева направо
Посмотрим еще примеры работы приоритета:
# 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) Использование скобок для управления порядком
Скобки переопределяют приоритет по умолчанию. Операции в скобках всегда выполняются первыми, независимо от операторов:
# 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 вычисляет выражения, начиная с самых внутренних скобок:
# 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 = 124.4.3) Когда операторы имеют одинаковый приоритет
Когда несколько операторов одного уровня приоритета появляются в выражении, 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.54.4.4) Практические примеры с приоритетом
Рассмотрим реальные ситуации, где понимание приоритета важно:
# 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)# 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# 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.44.4.5) Рекомендации по использованию приоритета операторов
Используйте скобки для ясности, даже когда это не строго необходимо:
Иногда добавление скобок делает ваше намерение более понятным, даже если они не меняют результат:
# 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Разбивайте сложные выражения на шаги:
Когда выражение становится слишком сложным, часто лучше разбить его на несколько строк:
# 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:
# 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 выбирает тип результата:
4.5.2) Продвижение типов в сложных выражениях
Когда выражение содержит несколько операций со смешанными типами, 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, даже при делении двух целых:
# 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'>Если вы хотите получить целый результат деления, используйте целочисленное деление // (но помните, что оно округляет вниз):
# 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) Практические последствия смешивания типов
Понимание продвижения типов помогает предсказывать и контролировать типы результатов:
# 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): 904.5.5) Когда важны целочисленные результаты
Иногда вам специально нужны целые результаты — для подсчета, индексации или других дискретных операций. Вот как гарантировать получение целых:
# 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() усекает к нулю. Для положительных чисел результат одинаковый, но для отрицательных — различается:
# 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, когда ожидали целое. Обычно это связано с делением или смешиванием типов:
# 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() возвращает модуль числа — расстояние от нуля, без учета знака:
# 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Функция модуля полезна, когда вас интересует величина, но не направление:
# 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: 154.6.2) Поиск минимума и максимума с min() и max()
Функция min() возвращает наименьший из своих аргументов, а max() — наибольший:
# 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Эти функции незаменимы для поиска крайних значений в данных:
# 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() часто используется, когда нужно удержать значение в определенных пределах:
# 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() предоставляет дополнительные возможности:
# 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() может принимать необязательный третий аргумент для модульного возведения в степень (полезно в криптографии и теории чисел, но выходит за рамки базовой арифметики):
# 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():
# 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: 274.6.4) Комбинирование встроенных функций
Эти функции хорошо работают вместе для решения распространенных задач:
# 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() принимает число и округляет его до ближайшего целого:
# 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() принимает необязательный второй аргумент, задающий количество сохраняемых знаков после запятой:
# 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) Практическое использование округления
Округление необходимо при выводе денежных сумм, измерений и других значений, где избыточная точность не нужна или сбивает с толку:
# 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 meters4.7.4) Округление vs усечение vs floor/ceil
Важно понимать, что округление отличается от усечения (удаления дробной части) и операций floor/ceil:
# 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)Для отрицательных чисел различия еще заметнее:
# 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): -34.7.5) Важные нюансы при округлении
Точность float может давать неожиданные результаты:
Из-за того, как компьютеры представляют числа с плавающей точкой (об этом в разделе 4.10), округление не всегда дает именно тот результат, который вы ожидаете:
# 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 для вычислений:
Часто вы хотите округлять только для отображения, сохраняя полную точность для вычислений:
# 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) Счетчики: отслеживание количества
Счетчик — это переменная, которая отслеживает, сколько раз что-то произошло. Вы инициализируете ее нулем и увеличиваете каждый раз, когда событие происходит:
# 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). Пока что важно понять шаблон: начать с нуля и добавлять один каждый раз.
Счетчики могут отслеживать разные типы событий:
# 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: 14.8.2) Аккумуляторы: накапливание сумм
Аккумулятор — это переменная, которая накапливает текущую сумму. Как и счетчик, вы инициализируете его нулем, но вместо добавления единицы каждый раз добавляете разные значения:
# 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Аккумуляторы — основа обработки данных. Они позволяют агрегировать значения по мере их обработки:
# 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: 34.8.3) Вычисление средних значений
Среднее сочетает в себе шаблоны счетчика и аккумулятора: вам нужна и сумма (аккумулятор), и количество (счетчик), затем вы делите одно на другое:
# 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Важно: всегда проверяйте деление на ноль:
При вычислении средних нужно убедиться, что счетчик не равен нулю:
# 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(), чтобы реализовать это:
# 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: 684.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 показывает двоичное представление целого числа:
# 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 notation4.9.2) Побитовое И (&)
Оператор & выполняет побитовое И: каждый бит результата равен 1, только если соответствующие биты обоих операндов равны 1:
# 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)Побитовое И часто используют для проверки, установлен ли определенный бит (проверка флагов):
# 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: False4.9.3) Побитовое ИЛИ (|)
Оператор | выполняет побитовое ИЛИ: каждый бит результата равен 1, если соответствующий бит хотя бы одного операнда равен 1:
# 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)Побитовое ИЛИ используют для установки (комбинирования) флагов:
# 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: 0b1114.9.4) Побитовое XOR (^)
Оператор ^ выполняет побитовое XOR (исключающее ИЛИ): каждый бит результата равен 1, если соответствующие биты операндов различаются:
# 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 имеет интересные свойства, например, позволяет инвертировать биты или использоваться для простого шифрования:
# 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: 424.9.5) Сдвиги влево (<<) и вправо (>>)
Оператор << сдвигает биты влево, а >> — вправо:
# 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 (целочисленное деление):
# 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... продолжается бесконечно).
Вот удивительный пример:
# 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) Понимание проблемы представления
Рассмотрим еще примеры такого поведения:
# 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) Практические последствия
В большинстве повседневных задач эти крошечные ошибки не имеют значения. Но есть ситуации, когда о них нужно помнить.
Сравнение чисел с плавающей точкой:
# 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)Накопление ошибок при повторных вычислениях:
# 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Финансовые вычисления:
# 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) Стратегии работы с числами с плавающей точкой
Округляйте результаты для отображения:
# 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 на точное равенство:
# 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 не важна
Для большинства повседневных задач точность чисел с плавающей точкой более чем достаточна:
# 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) Главный вывод
Числа с плавающей точкой — приближения действительных чисел. Для большинства задач программирования они достаточно точны. Но помните:
- Не сравнивайте float на точное равенство — вместо этого проверяйте, достаточно ли они близки
- Округляйте результаты при выводе, чтобы не показывать бессмысленную «длинную» точность
- Помните о накоплении ошибок при повторных вычислениях
- Для финансовых расчетов рассмотрите возможность округления до нужной точности или использования модуля
decimal
Понимание этих ограничений помогает писать более надежные программы и избегать неожиданных ошибок. Хорошая новость: для подавляющего большинства задач, если следовать этим простым рекомендациям, числа с плавающей точкой «просто работают».
4.11) (Необязательно) Базовые математические функции модуля math (sqrt, floor, ceil, pi)
Встроенных операторов и функций Python достаточно для базовой арифметики, но для более продвинутых математических операций в Python есть модуль math. Модуль — это коллекция связанных функций и значений, которые вы можете использовать в своих программах. Мы подробно поговорим о модулях в главе 22, а сейчас кратко рассмотрим основы, необходимые для использования math.
4.11.1) Импорт модуля math
Чтобы использовать модуль math, нужно импортировать его в начале программы:
# 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 предоставляет точные значения важных математических констант:
# 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.544.11.3) Квадратный корень с sqrt()
Функция sqrt() вычисляет квадратный корень числа:
# 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() округляет вверх (к плюс бесконечности):
# 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)Эти функции полезны, когда нужно гарантировать, что значение попадает в определенный диапазон, или когда нужны целые результаты с конкретным способом округления:
# 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: 84.11.5) Сравнение floor(), ceil(), round() и int()
Полезно понимать, как отличаются друг от друга эти способы округления:
# 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Вот наглядное представление поведения этих функций:
4.11.6) Другие полезные функции модуля math
Модуль math содержит множество других функций. Вот еще несколько часто полезных:
# 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):
# 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 close4.11.7) Когда использовать модуль math
Используйте модуль math, когда вам нужны:
- математические константы (π, e)
- квадратные корни и другие корни
- тригонометрические функции (sin, cos, tan)
- логарифмические и экспоненциальные функции
- точный контроль округления (floor, ceil)
- сравнение чисел с плавающей точкой (isclose)
Для базовой арифметики (+, -, *, /, //, %, **) используйте встроенные операторы Python. Для более продвинутых математических операций импортируйте и используйте модуль math.
Модуль math входит в стандартную библиотеку Python, то есть всегда доступен — нужно только импортировать его. Мы рассмотрим больше модулей и изучим систему импорта подробно в главе 22.
В этой главе вы узнали, как работать с числами в Python: выполнять арифметические операции, понимать различные виды деления, использовать приоритет операторов, смешивать целые числа и числа с плавающей точкой, применять встроенные числовые функции, использовать распространенные числовые шаблоны, работать с побитовыми операторами, понимать точность чисел с плавающей точкой и использовать модуль math для продвинутых операций.
Эти числовые операции — фундамент бесчисленных задач программирования: от простых вычислений до сложного анализа данных. По мере изучения Python вы будете постоянно использовать эти операции, часто в сочетании со структурами управления потоком и коллекциями данных, о которых вы узнаете в следующих главах.
Практикуйте эти операции, пока они не станут естественными. Попробуйте написать небольшие программы, которые вычисляют реальные величины: преобразуют температуры, считают площади и объемы, обрабатывают финансовые данные или анализируют измерения. Чем больше вы практикуетесь, тем увереннее будете себя чувствовать с числовыми возможностями Python.