跳至主要内容

Python 中的取模运算符:关键概念详解

了解如何在 Python 中使用取模运算符(%)解决实际问题,从判断奇偶数到密码学,再到循环数据结构。
· 9分钟 阅读

使用 AI 探索

Python 中的取模运算符(%)是一个基础的算术运算符,用于确定除法运算后的余数。概念上虽简单,却在多种编程场景中发挥关键作用。从判断一个数是奇数还是偶数,到循环遍历序列、判断闰年,熟练掌握取模运算符能提升您在 Python 中的问题求解能力。

理解取模运算符至关重要,因为它经常出现在以下场景:

  • 判断奇数与偶数
  • 按循环方式遍历元素
  • 在模运算中的数学计算
  • 实际应用场景,如日期计算、密码学与游戏逻辑。

在本文中,我将全面介绍如何在 Python 中使用取模运算符,涵盖语法、在不同数字类型下的行为、用例以及实用示例。若您是 Python 新手,建议先学习我们的Python 入门课程。

什么是 Python 中的取模运算符?

取模运算符(%)用于计算两个数相除后的余数。给定两个数 a(被除数)与 b(除数),取模运算会返回 a 被 b 整除后的余数。

数学背景

取模源于模运算,这是一种整数算术体系,当数值达到某个特定值(模)后会“回绕”。它广泛应用于密码学、哈希以及循环计算。

想象一个 12 小时制的时钟:过了 12,不是继续到 13,而是回到 1。本质上这就是模为 12 的模运算:

  • 13 % 12 = 1,意味着 13 点在 12 小时制中对应 1 点。

Python 取模语法与基础用法

在 Python 中,取模运算符的语法非常直接:

remainder = dividend % divisor

来看一些基础示例:

# Basic modulo operations
print(7 % 3)     
print(15 % 4)   
print(20 % 5)  
1 
3 
0 

当我们用 7 除以 3,商为 2,余数为 1。取模运算符返回的就是这个余数。同理,15 除以 4,商为 3、余数为 3;20 除以 5,恰好等于 4,没有余数。

与整数的配合使用

既然已经理解了取模运算符的基础,我们来看看它在整数上的具体表现。

正整数取模

对正整数进行取模是最直接的用法:

# Examples with positive integers
print(10 % 3)      
print(25 % 7)     
print(100 % 10)  
1 
4 
0 

在这些示例中,我们通过计算除数能整除被除数多少次,然后确定剩余部分来得到结果:

  • 10 ÷ 3 = 3 余 1,因此 10 % 3 = 1
  • 25 ÷ 7 = 3 余 4,因此 25 % 7 = 4
  • 100 ÷ 10 = 10 余 0,因此 100 % 10 = 0

负整数取模

当处理负数时,取模运算符的行为会更有意思。Python 遵循一个特定规则:a % b 的结果与 b(除数)同号。

# Examples with negative integers
print(-10 % 3)    
print(10 % -3)    
print(-10 % -3) 
2 
-2 
-1 

这些结果乍看可能有些反直觉,但它们遵循一致的规则:

  1. 对于 -10 % 3:有人可能会认为 -10 ÷ 3 = -3 余 -1,因此结果应为 -1。但 Python 返回 2。原因在于 Python 寻找一个介于 0 与 3(不含)之间的值,使其加上 3 的某个倍数后等于 -10。答案是 2,因为 -12 + 2 = -10。
  2. 对于 10 % -3:结果与除数(-3)同号,因此得到 -2。因为 10 = (-3) × (-4) + (-2)。
  3. 对于 -10 % -3:结果为 -1,因为 -10 = (-3) × 3 + (-1)。

理解 Python 在负数取模时的处理方式,对于以下循环计算尤为重要:

  • 实现哈希函数
  • 解决与钟表算术相关的问题
  • 处理列表中的负索引逻辑

与浮点数的配合使用

Python 的取模运算符并不限于整数;它同样适用于浮点数:

# Modulo with floating-point numbers
print(7.5 % 2)     
print(9.7 % 4.2)  
print(5.0 % 0.2)  
1.5
1.299999999999999
0.19999999999999973

对于浮点数,取模操作遵循同样的原则:返回相除后的余数。不过,由于计算机中浮点数的存储方式(IEEE 754 格式),可能会出现微小的舍入误差。

例如,Python 可能返回 1.299999999999999 而不是 1.3,返回 0.19999999999999973 而不是 0.2。这类细微不准在浮点运算中很常见,进行精确计算时需要加以考虑。

Python 还提供了 math 模块中的 math.fmod() 函数,作为浮点取模操作的替代方案,它在某些边界情形下的处理可能不同:

import math

print(math.fmod(7.5, 2))    
print(math.fmod(-7.5, 2))  

# Compare with Python's modulo operator 
print(-7.5 % 2) 
1.5
-1.5
0.5

% 与 math.fmod() 的主要区别在于它们处理负数的方式。% 确保结果与除数同号,且返回范围为 [0, 除数);而 math.fmod() 确保结果与被除数同号。在需要关注余数符号的应用中,这一区别非常重要。

那么,何时应使用 %,何时应使用 math.fmod()?

  • 当您需要一致的符号行为(例如在范围内循环)时,使用 %。
  • 当必须保留被除数的符号(如物理模拟或数学函数)时,使用 math.fmod()。

在 Python 取模中处理负数

正如先前的示例所示,Python 在负数取模上的实现可能与直觉不完全一致。我们更系统地探究这一行为:

# Understanding the pattern with negative numbers
for i in range(-10, 11):
    result = i % 3
    print(f"{i} % 3 = {result}")
-10 % 3 = 2
-9 % 3 = 0
-8 % 3 = 1
-7 % 3 = 2
-6 % 3 = 0
-5 % 3 = 1
-4 % 3 = 2
-3 % 3 = 0
-2 % 3 = 1
-1 % 3 = 2
0 % 3 = 0
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0
7 % 3 = 1
8 % 3 = 2
9 % 3 = 0
10 % 3 = 1

请注意这一模式:无论被除数是正是负,结果都会按 0 → 1 → 2 循环。这种一致性使取模运算在循环计算中尤为有用,例如:

  • 在环形数组或缓冲区中轮转索引
  • 将值映射到周期函数(如基于时间的计算)

Python 遵循的取模数学公式为:

Import math 

a % b = a - (b * math.floor(a / b))

这可确保当 b 为正时,结果总在 0 与 b(不含)之间;当 b 为负时,结果总在 b 与 0(不含)之间。

Python 取模运算符总结

Python 中的取模运算符(%)用于计算除法运算的余数。它遵循一致的数学公式:

a % b = a - (b * math.floor(a / b))

Python 取模行为的关键规则:

  1. 对于正除数(b > 0)
    • 结果总在范围 [0, b) 内(包含 0,不包含 b)。
  2. 对于负除数(b < 0)
    • 结果总在范围 (b, 0] 内(不包含 b,包含 0)。
    • 余数与 b(除数)同号。
  3. 同时适用于整数与浮点数
    • 对浮点数,% 遵循相同公式,但可能因浮点精度引入微小舍入误差。
  4. % 与 math.fmod() 的区别
    • % 确保余数与除数同号。
    • math.fmod(a, b) 确保余数与被除数同号。

取模运算符的常见用例

下面看看取模运算符的一些实际应用,并进一步举例说明。

判断一个数是奇数还是偶数

最常见的应用之一是判断一个数是否为偶数或奇数:

def is_even(number):
    return number % 2 == 0

def is_odd(number):
    return number % 2 != 0

# Testing the functions
numbers = [1, 2, -3, 4, -5, 6, 7]
for num in numbers:
    if is_even(num):
        print(f"{num} is even")
    else:
        print(f"{num} is odd")
1 is odd
2 is even
-3 is odd
4 is even
-5 is odd
6 is even
7 is odd

其原理是:偶数被 2 整除时余数为 0,而奇数的余数为 1 或 -1。

循环遍历序列

取模运算符非常适合实现循环行为,例如在列表中轮流取值:

days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]

def get_day(n):
    # Get the day of the week for the nth day, where day 0 is Monday
    return days[n % len(days)]

# Starting from Monday (day 0), what day is it after 10 days?
print(get_day(10))  # Output: "Thursday"

# What day was it 15 days ago from Monday?
print(get_day(-15))  # Output: "Sunday"
Thursday
Sunday

在此示例中,无论 n 有多大或多小(为负),函数始终会返回一个有效的星期几。

闰年计算

虽然闰年的计算涉及多重条件,但取模运算符是算法的核心:

闰年遵循如下规则:

  • 如果某年能被 4 整除,则是闰年。
  • 但若能被 100 整除,则不是闰年。
  • 除非也能被 400 整除,则是闰年。
def is_leap_year(year):

    return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

# Test the function
years = [1900, 2000, 2004, 2020, 2023, 2024]
for year in years:
    if is_leap_year(year):
        print(f"{year} is a leap year")
    else:
        print(f"{year} is not a leap year")
1900 is not a leap year
2000 is a leap year
2004 is a leap year
2020 is a leap year
2023 is not a leap year
2024 is a leap year

这确保了正确应用格里高利历的闰年规则。

Python 取模的实用示例

以下是一些您可以在自己的Python 项目中使用取模运算符的方法:

在循环中使用取模

取模运算符常用于循环中,用于按固定间隔执行操作:

# Print a message every 5 iterations
for i in range(1, 11):
    print(f"Processing item {i}", end=": ")
    if i % 5 == 0:
        print("Checkpoint reached!")
    else:
        print("Continuing...")
Processing item 1: Continuing...
Processing item 2: Continuing...
Processing item 3: Continuing...
Processing item 4: Continuing...
Processing item 5: Checkpoint reached!
Processing item 6: Continuing...
Processing item 7: Continuing...
Processing item 8: Continuing...
Processing item 9: Continuing...
Processing item 10: Checkpoint reached!

这种模式适用于批处理、进度汇报,或实现任何需要周期性执行的功能。

取模在真实场景中的应用

密码哈希

在密码学应用中,模运算是基础。以下是一个简化示例,展示取模如何用于基本的哈希函数:

def simple_hash(text, modulus=1000000007):
    """A very simple hash function using modulo."""
    hash_value = 0
    for char in text:
        # Combine the current hash with the character's Unicode code point
        hash_value = (hash_value * 31 + ord(char)) % modulus
    return hash_value

# Test with some strings
print(simple_hash("hello"))  # Output a number between 0 and 1000000006
print(simple_hash("world"))
print(simple_hash("hello world"))
print(simple_hash("こんにちは"))  
99162322
113318802
204910434
807637228

这只是一个非常基础的示例,真实的加密哈希要复杂得多。取模操作确保哈希值保持在可控范围内,同时尽可能保留良好哈希函数的性质。

时钟时间计算

取模在时间计算中十分有用,例如将大量小时数转换为标准时间格式。

def convert_to_12_hour_format(hours):
    return hours % 12 or 12  # Ensure 0 maps to 12

# Example
print(convert_to_12_hour_format(13))  # Output: 1
print(convert_to_12_hour_format(24))  # Output: 12
print(convert_to_12_hour_format(36))  # Output: 12

这里,取模运算符将小时数按 12 小时制回绕。

游戏开发:实现多人轮流回合

在多人回合制游戏中,我们可以使用取模来轮流切换玩家。

players = ["Alice", "Bob", "Charlie"]
turns = 10

for turn in range(turns):
      current_player = players[turn % len(players)]
      print(f"Turn {turn + 1}: {current_player}'s move")
Turn 1: Alice's move
Turn 2: Bob's move
Turn 3: Charlie's move
Turn 4: Alice's move
Turn 5: Bob's move
Turn 6: Charlie's move
Turn 7: Alice's move
Turn 8: Bob's move
Turn 9: Charlie's move
Turn 10: Alice's move

条形码校验位验证

许多条形码体系使用模运算来检测扫描数字的错误。

def is_valid_isbn13(isbn):
    digits = [int(d) for d in str(isbn)]
    checksum = sum(d * (1 if i % 2 == 0 else 3) for i, d in enumerate(digits))
    return checksum % 10 == 0

# Example ISBN Checksum validation
print(is_valid_isbn13(9780306406157))  # Output: True
print(is_valid_isbn13(9780306406158))  # Output: False
True
False

结语

取模运算符是 Python 工具箱中一个用途广泛的工具,远不止用于简单的余数计算。从判断奇偶,到实现带有循环模式的复杂算法,取模能为各种编程挑战带来更简洁、优雅的解决方案。

通过理解其基本功能以及在不同数值类型下的细微行为,您可以更高效、更可读地编写代码。下次当您需要实现与循环、轮换或周期事件相关的逻辑时,不妨想起取模运算符或许正是理想之选。

如果您希望进一步学习如何在项目中使用 Python,请查看我们的交互式课程Introduction to Data Science in Python。

Python 取模常见问答

Python 中取模运算符如何处理负数?

在 Python 中,使用取模运算符处理负数时,结果将与除数同号。

取模运算符能用于浮点数吗?

是的,取模运算符可用于浮点数,但需注意由于计算机中浮点表示方式,可能会有微小的舍入误差。

取模运算符在实际应用中如何使用?

取模运算符用于多种真实场景,例如哈希函数、时钟时间计算以及 ISBN 等校验中的错误检测。

为什么取模运算符在编程中很重要?

取模运算符对于解决涉及周期性、整除与回绕的问题至关重要,是实现高效编码与算法设计的多面手。

Benito Martin

作者

Benito Martin

作为 Martin Data Solutions 的创始人兼自由职业数据科学家、ML 与 AI 工程师,我在回归、分类、NLP、LLM、RAG、神经网络、集成方法和计算机视觉等领域拥有多元项目经验。

  • 成功交付多项端到端机器学习项目,覆盖数据清洗、分析、建模与在 AWS 和 GCP 上的部署,提供具备影响力且可扩展的解决方案。
  • 使用 Streamlit 和 Gradio 构建交互式、可扩展的网页应用,服务于不同行业场景。
  • 教授并指导数据科学与数据分析学员,通过个性化学习路径促进其职业成长。
  • 为企业级需求设计检索增强生成(RAG)应用课程内容。
  • 撰写高影响力的 AI 与 ML 技术博客,涵盖 MLOps、向量数据库和 LLM 等主题,并获得显著关注。

在我参与的每个项目中,我都会应用最新的软件工程与 DevOps 实践,如 CI/CD、代码规范检查、格式化、模型监控、实验追踪以及健壮的错误处理。我致力于交付完整的解决方案,将数据洞察转化为切实可行的策略,帮助企业成长,并充分发挥数据科学、机器学习与 AI 的价值。

主题