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'>

之所以会发生这种自动转换,是因为浮点数既可以表示整数也可以表示小数,而整数无法表示小数。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

和加法一样,当你混合使用整数和浮点数时,减法也会把整数提升为浮点数:

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

乘法遵循相同的类型转换规则。当你用一个整数和一个浮点数相乘时,结果是浮点数:

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,因为参与运算的其中一个操作数是浮点数。结果的类型由输入的类型决定,而不是由数学结果是否是整数决定。

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) 使用 / 进行常规除法

/ 运算符执行“真实除法 (true division)”——它总是返回一个浮点数结果,即使两个操作数都是整数:

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

这里的关键点是:/ 总是 产生浮点数结果,即使两个操作数都是整数,并且结果在数学上恰好是整数。这是刻意设计的:Python 希望除法的行为保持一致,并尽可能保留小数精度。

这种行为和某些其他编程语言不同,在那些语言中,两个整数相除会得到整数结果。在 Python 3 中,如果你想要整数结果,需要使用地板除(下一小节会介绍),或者显式地转换结果类型。

4.2.2) 使用 // 进行地板除 (floor division)

// 运算符执行“地板除 (floor division)”——它先做除法,然后向下取整到最接近的整数。当两个操作数都是整数时,结果是整数;当任一操作数是浮点数时,结果是浮点数(但仍然是向下取整后的值):

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)”或“模运算 (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) //、% 和 / 之间的关系

地板除和取模运算关系紧密。它们一起可以给出除法的完整结果:

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

这个关系始终成立:对于任意数字 ab(其中 b 不为 0),都有 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 提供了一种简洁的方式来完成这些操作,这就是复合赋值 (augmented assignment) 运算符。

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) 关于复合赋值的重要细节

对于不可变类型 (immutable type),复合赋值会创建新对象:

回忆第 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'>

结果遵循和普通运算相同的类型转换规则:当你混合使用整数和浮点数时,结果为浮点数。

4.3.5) 何时使用复合赋值

只要你是在基于变量当前值更新它时,就可以使用复合赋值运算符。它们能让你的代码:

  • 更简洁x += 5x = 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 按从左到右的顺序计算(这称为“左结合性 (left associativity)”):

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) 在表达式中混合使用整数和浮点数

你已经看到,Python 在简单运算中会自动处理整数和浮点数的混合使用。现在我们系统地探讨这种行为,并理解数值表达式中类型转换的规则。

4.5.1) 类型提升规则

当 Python 执行的算术运算同时涉及整数和浮点数时,它会自动先把整数转换(“提升 (promote)”)为浮点数,再进行运算。结果总是浮点数:

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 要这么做?因为浮点数可以表示整数和小数,而整数不能表示小数。转换为浮点数可以保留所有信息,避免精度丢失。

下面是一个 Python 如何决定结果类型的可视化示意:

Yes

No

Yes

No

Operation with
int and float

Any operand
is float?

Result is float

Division
operator /?

Result is 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)

一旦表达式中的某一步产生了浮点数,后续涉及该结果的所有运算也都会产生浮点数。

4.5.3) 特例:常规除法总会产生浮点数

回忆 4.2 小节中所说,/ 运算符总会产生浮点数,即使两个操作数都是整数:

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() 是截断 (truncate),即朝 0 方向取整。对正数来说它们相同,但对负数则不同:

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) 避免意外得到浮点结果

有时你会惊讶地发现自己得到了浮点数,而你本以为会得到整数。这通常是因为使用了除法或混合了类型:

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 的类型提升规则旨在保留精度并避免数据丢失。当你混用整数和浮点数,或使用常规除法时,要预期得到浮点结果。如果你需要整数,可以使用地板除或显式转换,但要清楚它们的取整方式。

4.6) 常用数值内置函数:abs()、min()、max() 和 pow()

Python 提供了一些用于常见数值运算的内置函数。这些函数同时支持整数和浮点数,是日常编程中的重要工具。我们来看看最常用的几个。

4.6.1) 使用 abs() 求绝对值

abs() 函数返回一个数字的绝对值——也就是它到 0 的距离,不考虑正负号:

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

使用 max()min() 组合实现“夹紧 (clamp)”模式,在需要把数值限制在某个范围内时非常常见:

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() 函数还可以接受第三个可选参数用于模幂运算 (modular exponentiation)(在密码学和数论中很有用,但超出基础算术的范围):

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)”的规则(也叫“银行家舍入 (banker's rounding)”),即舍入到最近的偶数。这种做法可以在大量重复舍入运算中减少系统性偏差。在大多数日常编程场景中,这个细节影响不大,但了解它是有好处的。

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() 返回浮点数(即使你舍入到 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 向下/向上取整

要注意,舍入与截断(直接去掉小数部分)或地板/天花板操作是不同的:

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) 使用舍入时的重要注意事项

浮点精度可能带来意外结果:

由于计算机表示浮点数的方式(我们将在 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

在大多数实际使用中,这不是问题。但如果你在做对精确小数要求很高的金融计算,可能需要使用 Python 的 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) 计数器:记录“发生了多少次”

计数器 (counter) 是一个用来记录某件事发生次数的变量。你将它初始化为 0,每次事件发生时把它加 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)

在真实程序中,你通常会在循环 (loop) 中使用计数器(我们会在第 10、11 章学习循环)。目前只要理解模式本身:从 0 开始,每次加 1。

计数器可以用来跟踪不同类型的事件:

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) 累加器:累积总和

累加器 (accumulator) 是一个用于维护“运行总和”的变量。和计数器一样,它也是从 0 初始化,但每次增加的值可以不同:

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

重要:计算平均值前一定要检查是否会除以 0:

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) 整数的按位运算符:&、|、^、<<、>>(简要概览)

按位运算符 (bitwise operator) 作用于整数的各个二进制位 (bit)。虽然在日常编程中你不会像使用算术运算符那样频繁地使用它们,但在一些场景中(如标志位、权限、底层数据操作、性能优化)它们非常重要。

本节只是简单概览。要完整理解按位运算,需要先理解二进制表示,这是一个比本章更深入的话题。

4.9.1) 理解二进制表示(快速介绍)

计算机将整数存储为一串比特(0 和 1)。例如:

  • 十进制 5 的二进制是 101(1×4 + 0×2 + 1×1)
  • 十进制 12 的二进制是 1100(1×8 + 1×4 + 0×2 + 0×1)

Python 的 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 notation

4.9.2) 按位与 (&)

& 运算符执行按位与 (bitwise AND):只有当两个操作数对应位置上的比特都为 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) 按位或 (|)

| 运算符执行按位或 (bitwise OR):只要两个操作数中任一在对应位置上的比特为 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) 按位异或 (^)

^ 运算符执行按位异或 (bitwise XOR, exclusive OR):只有当两个操作数在对应位置上的比特不同,结果中的该比特才为 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)

异或有一些有趣的性质,例如切换 (toggle) 指定位或交换数值:

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 次方;向右移动 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) 为什么浮点数并不总是精确的

计算机以二进制(base 2)而不是十进制(base 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 的 bug——而是计算机表示浮点数方式的根本限制。十进制的 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)

像 0.5、0.25、0.125 这样的 2 的幂或若干个 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

不要直接用“相等”比较浮点数:

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

在精度很重要时要特别小心:

对于科学计算、金融计算或其他需要精确十进制运算的领域,你可能需要:

  • 使用 Python 的 decimal 模块进行精确十进制运算
  • 使用 fractions 模块进行精确有理数运算
  • 在合适的计算节点进行小心的舍入

这些模块超出了本章范围,但知道它们的存在很有价值。

4.10.5) 什么时候浮点精度足够用

对于大多数日常编程任务,浮点精度已经绰绰有余:

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. 不要用精确相等来比较浮点数——应该检查它们是否“足够接近”
  2. 在显示时进行舍入,避免展示毫无意义的高精度
  3. 注意重复计算中误差的累积
  4. 在金融计算中,考虑舍入到合适精度或使用 decimal 模块

理解这些限制可以帮助你写出更健壮的程序,避免意外的 bug。好消息是:对于绝大多数编程任务,只要遵守这些简单规则,浮点运算“基本就是好用的”。

4.11)(可选)使用 math 模块的基础数学函数(sqrt、floor、ceil、pi)

Python 的内置运算符和函数覆盖了基础算术,但对于更高级的数学运算,Python 提供了 math 模块。模块 (module) 是一组相关函数和数值的集合,你可以在程序中使用。我们会在第 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() 返回的总是浮点数,即使输入的是完全平方数。此外,你不能对负数取平方根(否则会报错)。

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