1. Objectives 学习目标

  • To understand definite and indefinite loops using for and while 理解使用forwhile的确定循环和不确定循环
  • To understand interactive loops and sentinel loops using while 理解使用while的交互循环和哨兵循环
  • To understand end-of-file loops and their implementation 理解文件结束循环及其实现
  • To design and implement solutions with nested loop structures 设计和实现嵌套循环结构的解决方案

2. Definite Loops 确定循环

For Loops: A Quick Review for循环快速回顾

for <var> in <sequence>:
    <body>

特点: 循环次数在循环开始时确定

Example: Average Calculator 示例:平均值计算器

# 计算用户输入数字的平均值
def main():
    n = int(input("How many numbers? "))
    total = 0.0
    
    for i in range(n):
        number = float(input("Enter a number: "))
        total = total + number
        
    print("The average is:", total/n)
 
if __name__ == '__main__':
    main()

文件读取示例:计算成绩平均值

# 读取文件中的成绩并计算平均值
def calculate_average():
    total = 0.0
    count = 0
    
    with open('grades.txt', 'r') as file:
        for line in file:
            grade = float(line.strip())
            total += grade
            count += 1
            
    if count > 0:
        average = total / count
        print(f"Ming's average grade is: {average:.2f}")
    else:
        print("No grades found in the file.")

2.1 Exercises 练习

Exercise 1: Count Words with ‘p’ 练习1:统计包含’p’的单词

def count_words_with_p():
    count = 0
    with open('story.txt', 'r') as file:
        text = file.read()
        words = text.split()
        
        for word in words:
            if 'p' in word.lower():
                count += 1
                
    print(f"Number of words containing 'p': {count}")
 
# 使用while循环实现
def count_words_with_p_while():
    count = 0
    with open('story.txt', 'r') as file:
        text = file.read()
        words = text.split()
        i = 0
        
        while i < len(words):
            if 'p' in words[i].lower():
                count += 1
            i += 1
            
    print(f"Number of words containing 'p': {count}")

Exercise 2: Multiplication Table 练习2:乘法表

def multiplication_table():
    for i in range(1, 10):
        for j in range(1, i + 1):
            print(f"{j}×{i}={i*j:2d}", end="  ")
        print()  # 换行
 
# 输出:
# 1×1= 1
# 1×2= 2  2×2= 4
# 1×3= 3  2×3= 6  3×3= 9
# ...

Exercise 3: Prime Number Check 练习3:质数检查

def prime_check():
    for num in range(2, 11):
        is_prime = True
        factors = []
        
        for i in range(2, num):
            if num % i == 0:
                is_prime = False
                factors.append(i)
        
        if is_prime:
            print(f"{num} is prime")
        else:
            print(f"{num} = {factors}")
 
# 优化版本
def prime_check_optimized():
    for num in range(2, 11):
        factors = []
        for i in range(2, int(num**0.5) + 1):
            if num % i == 0:
                factors.extend([i, num//i])
        
        if not factors:
            print(f"{num} is prime")
        else:
            # 去重并排序
            unique_factors = sorted(set(factors))
            print(f"{num} = {unique_factors}")

Exercise 4: String Split and Pad 练习4:字符串分割和填充

def split_strings():
    result = []
    
    while True:
        s = input("Enter a string (empty to stop): ")
        if not s:
            break
            
        # 按长度8分割
        for i in range(0, len(s), 8):
            chunk = s[i:i+8]
            # 长度不足8时补0
            if len(chunk) < 8:
                chunk = chunk.ljust(8, '0')
            result.append(chunk)
    
    print("Result:", result)
 
# 示例输入输出
# 输入: "1234567890", "abc"
# 输出: ['12345678', '90000000', 'abc00000']

3. Indefinite Loops 不确定循环

While Loop while循环

while <condition>:
    <body>

特点: 前置测试循环(pre-test loop),条件为真时继续执行

While与for对比

# for循环
for i in range(11):
    print(i)
 
# while循环等价实现
i = 0
while i <= 10:
    print(i)
    i = i + 1

无限循环警告

# 错误示例 - 无限循环
i = 0
while i <= 10:
    print(i)
    # 缺少 i = i + 1
 
# 解决方法:Ctrl+C, Ctrl+Alt+Delete, 或重启

Number Guessing Game 猜数字游戏

import random
 
def guessing_game():
    # 生成随机数
    secret_number = random.randint(0, 100)
    attempts = 0
    
    print("I'm thinking of a number between 0 and 100. Can you guess it?")
    
    while True:
        try:
            guess = int(input("Enter your guess: "))
            attempts += 1
            
            if guess == secret_number:
                print(f"Success! You guessed it in {attempts} attempts.")
                break
            elif guess < secret_number:
                print("Too small! Try again.")
            else:
                print("Too big! Try again.")
                
        except ValueError:
            print("Please enter a valid number.")
 
if __name__ == '__main__':
    guessing_game()

4. Loop Control Statements 循环控制语句

Break Statement break语句

# 在列表中找到特定课程
courses = ["Math", "Physics", "Python Programming", "Chemistry", "Biology"]
 
for course in courses:
    if course == "Python Programming":
        print("Found the Python Programming course!")
        break
    print(f"Checking: {course}")
 
# 输出:
# Checking: Math
# Checking: Physics
# Found the Python Programming course!

Continue Statement continue语句

# 跳过特定课程
courses = ["Math", "Physics", "Python Programming", "Chemistry", "Biology"]
 
for course in courses:
    if course == "Python Programming":
        continue  # 跳过Python课程
    print(f"Studying: {course}")
 
# 输出:
# Studying: Math
# Studying: Physics
# Studying: Chemistry
# Studying: Biology

Else Clause with Loops 循环的else子句

在循环正常结束时执行

# 在循环正常结束时执行
numbers = [2, 4, 6, 8, 10]
 
for num in numbers:
    if num % 2 != 0:
        print("Found an odd number!")
        break
else:
    print("All numbers are even!")  # 会执行
 
# 搜索示例
search_item = "Python"
items = ["Java", "C++", "JavaScript"]
 
for item in items:
    if item == search_item:
        print(f"Found {search_item}!")
        break
else:
    print(f"{search_item} not found in the list.")  # 会执行

5. Boolean Operations 布尔运算

Boolean Operators 布尔运算符

# 运算符优先级: not > and > or
result1 = True or not False and True    # 等价于 True or ((not False) and True)
result2 = (True or (not False)) and True
 
print(result1)  # True
print(result2)  # True

Boolean Algebra 布尔代数

# 德摩根定律应用
scoreA, scoreB = 10, 12
 
# 原始复杂条件
if not(scoreA == 15 or scoreB == 15):
    print("Game continues")
 
# 应用德摩根定律简化
if scoreA != 15 and scoreB != 15:
    print("Game continues - simplified")
 
# 其他布尔恒等式
a = True
print(a or True)           # True
print(not(not a))          # True (双重否定)
print(not(a and False))    # True

Short-circuit Evaluation 短路求值

# 使用or设置默认值
flavor = input("What flavor do you want [vanilla]: ") or "vanilla"
print(f"You chose: {flavor}")
 
# 等价的长版本
user_input = input("What flavor do you want [vanilla]: ")
if user_input:
    flavor = user_input
else:
    flavor = "vanilla"

6. Loop Patterns 循环模式

Interactive Loops 交互循环

def interactive_average():
    total = 0.0
    count = 0
    more_data = "yes"
    
    while more_data[0].lower() == "y":
        number = float(input("Enter a number: "))
        total += number
        count += 1
        more_data = input("Do you have more numbers (yes or no)? ")
    
    if count > 0:
        print(f"The average is {total/count}")
    else:
        print("No numbers were entered.")
 
# 测试运行:
# Enter a number >> 32
# Do you have more numbers (yes or no)? y
# Enter a number >> 45
# Do you have more numbers (yes or no)? yes
# Enter a number >> 34
# The average of the numbers is 37.0

Sentinel Loops 哨兵循环

def sentinel_average():
    total = 0.0
    count = 0
    
    print("Enter numbers (negative to quit):")
    number = float(input("Enter a number: "))
    
    while number >= 0:
        total += number
        count += 1
        number = float(input("Enter a number: "))
    
    if count > 0:
        print(f"The average is {total/count}")
    else:
        print("No numbers were entered.")
 
# 改进版本 - 使用字符串哨兵
def sentinel_average_improved():
    total = 0.0
    count = 0
    
    while True:
        input_str = input("Enter a number (<Enter> to quit): ")
        if input_str == "":
            break
        
        number = float(input_str)
        total += number
        count += 1
    
    if count > 0:
        print(f"The average is {total/count}")

File Loops 文件循环

def file_average():
    total = 0.0
    count = 0
    
    with open('numbers.txt', 'r') as infile:
        for line in infile:
            number = float(line.strip())
            total += number
            count += 1
    
    if count > 0:
        print(f"The average is {total/count}")
 
# 使用readline的版本
def file_average_readline():
    total = 0.0
    count = 0
    
    with open('numbers.txt', 'r') as infile:
        line = infile.readline()
        while line != "":
            number = float(line.strip())
            total += number
            count += 1
            line = infile.readline()
    
    if count > 0:
        print(f"The average is {total/count}")

Nested Loops 嵌套循环

def process_csv_file():
    total = 0.0
    count = 0
    
    with open('data.csv', 'r') as infile:
        line = infile.readline()
        while line != "":
            # 外层循环:处理每一行
            numbers = line.strip().split(',')
            
            # 内层循环:处理行中的每个数字
            for num_str in numbers:
                if num_str:  # 确保不是空字符串
                    total += float(num_str)
                    count += 1
            
            line = infile.readline()
    
    if count > 0:
        print(f"The average is {total/count}")
 
# 处理每行多个数字的CSV文件
# 文件内容示例:
# 10,20,30
# 40,50
# 60,70,80,90

7. Post-Test Loops 后测试循环

Input Validation 输入验证

# 方法1:使用标志变量
def get_positive_number_v1():
    number = -1  # 初始化为非法值
    while number < 0:
        number = float(input("Enter a positive number: "))
        if number < 0:
            print("The number must be positive!")
    return number
 
# 方法2:使用break(推荐)
def get_positive_number_v2():
    while True:
        number = float(input("Enter a positive number: "))
        if number >= 0:
            break
        print("The number must be positive!")
    return number

Loop and a Half 循环中途模式

def sentinel_loop_elegant():
    total = 0.0
    count = 0
    
    while True:
        data = input("Enter a number (or 'done' to finish): ")
        if data.lower() == 'done':
            break  # 哨兵值,不处理
        
        number = float(data)
        total += number
        count += 1
    
    if count > 0:
        print(f"The average is {total/count}")

7.1 Example: Event Loop 示例:事件循环

Simple GUI Event Loop 简单GUI事件循环

from graphics import *
 
def simple_event_loop():
    win = GraphWin("Color Changer", 400, 300)
    win.setBackground("white")
    
    instructions = Text(Point(200, 20), "Press r(red), g(green), b(blue), q(quit)")
    instructions.draw(win)
    
    while True:
        key = win.checkKey()
        
        if key == "q":
            break
        elif key == "r":
            win.setBackground("pink")
        elif key == "g":
            win.setBackground("lightgray")
        elif key == "b":
            win.setBackground("lightblue")
        
        # 检查鼠标点击
        click = win.checkMouse()
        if click:
            handle_click(click, win)
    
    win.close()
 
def handle_click(point, window):
    # 在点击位置创建输入框
    entry = Entry(point, 10)
    entry.draw(window)
    
    # 进入文本输入模式
    while True:
        key = window.getKey()
        if key == "Return":
            break
    
    # 获取文本并显示
    text = entry.getText()
    entry.undraw()
    
    display_text = Text(point, text)
    display_text.draw(window)
 
if __name__ == '__main__':
    simple_event_loop()

7.2 Data Analysis Example: S&P 500 数据分析示例:标普500

S&P 500 Industry Analysis 标普500行业分析

import matplotlib.pyplot as plt
import csv
from tkinter.filedialog import askopenfilename
 
def sp500_analysis():
    # 文件选择循环
    while True:
        filename = askopenfilename(title="Select CSV file", filetypes=[("CSV files", "*.csv")])
        if filename and filename.endswith('.csv'):
            break
        print("Please select a valid CSV file.")
    
    # 读取和分析数据
    sectors = {}
    
    with open(filename, 'r', encoding='utf-8') as file:
        reader = csv.DictReader(file)
        
        for row in reader:
            sector = row.get('Sector', 'Unknown')
            market_cap = float(row.get('Market Cap', 0))
            
            if sector not in sectors:
                sectors[sector] = {'count': 0, 'market_cap': 0}
            
            sectors[sector]['count'] += 1
            sectors[sector]['market_cap'] += market_cap
    
    # 用户交互循环
    while True:
        choice = input("Number of stocks (N) or Market Capitalization (M) (Enter for exit): ")
        
        if choice == '':
            break
        elif choice.upper() == 'N':
            plot_sector_counts(sectors)
        elif choice.upper() == 'M':
            plot_market_share(sectors)
        else:
            print("Wrong input, please try again.")
 
def plot_sector_counts(sectors):
    labels = list(sectors.keys())
    counts = [sectors[sector]['count'] for sector in labels]
    
    plt.figure(figsize=(10, 8))
    plt.pie(counts, labels=labels, autopct='%1.1f')
    plt.title('S&P 500 Market Capitalization by Sector')
    plt.show()
 
if __name__ == '__main__':
    sp500_analysis()

8. Summary 总结

Loop Types 循环类型

  1. Definite loops确定循环: for loops with known iterations 已知迭代次数的for循环
  2. Indefinite loops不确定循环: while loops with conditions 基于条件的while循环
  3. Interactive loops交互循环: User-controlled repetition 用户控制的重复
  4. Sentinel loops哨兵循环: Special value terminates 特殊值终止
  5. File loops文件循环: Process until end of file 处理直到文件结束
  6. Nested loops嵌套循环: Loops within loops 循环中的循环

Control Statements 控制语句

  • break: Exit loop immediately 立即退出循环
  • continue: Skip to next iteration 跳到下一次迭代
  • else: Execute after normal completion 正常完成后执行

Best Practices 最佳实践

  • Use meaningful loop variable names 使用有意义的循环变量名
  • Avoid infinite loops with proper termination conditions 使用适当的终止条件避免无限循环
  • Use break sparingly for clarity 谨慎使用break以保持清晰度
  • Prefer for loops when number of iterations is known 当迭代次数已知时优先使用for循环
  • Validate input in post-test loops 在后测试循环中验证输入

Ex. 补充:列表推导式 (List Comprehensions)

基础语法

[p.x for p in points]  # 从points列表中提取每个对象的x属性

详细解释

基本用法

# 假设我们有一个Point类
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
# 创建点列表
points = [Point(1, 2), Point(3, 4), Point(5, 6)]
 
# 使用列表推导式提取所有x坐标
x_coordinates = [p.x for p in points]
print(x_coordinates)  # 输出: [1, 3, 5]
 
# 等价于传统的for循环
x_coordinates_manual = []
for p in points:
    x_coordinates_manual.append(p.x)

在数据分析中的应用

# 从学生对象列表中提取成绩
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade
 
students = [
    Student("Alice", 85),
    Student("Bob", 92),
    Student("Charlie", 78)
]
 
# 提取所有成绩
grades = [s.grade for s in students]
print(grades)  # [85, 92, 78]
 
# 计算平均成绩
average_grade = sum([s.grade for s in students]) / len(students)
print(f"Average grade: {average_grade:.2f}")

带条件的列表推导式

# 只提取及格成绩
passing_grades = [s.grade for s in students if s.grade >= 80]
print(passing_grades)  # [85, 92]
 
# 提取优秀学生的名字
top_students = [s.name for s in students if s.grade >= 90]
print(top_students)  # ['Bob']

与循环结构的对比

# 传统方法 - 需要多行代码
def get_x_coordinates_traditional(points):
    result = []
    for p in points:
        result.append(p.x)
    return result
 
# 列表推导式 - 一行搞定
def get_x_coordinates_comprehension(points):
    return [p.x for p in points]
 
# 两者功能完全相同,但列表推导式更简洁

在文件处理中的应用

# 从文件中读取数字并转换为浮点数
def read_numbers_from_file(filename):
    with open(filename, 'r') as file:
        # 使用列表推导式处理每一行
        numbers = [float(line.strip()) for line in file if line.strip()]
    return numbers
 
# 等价于
def read_numbers_traditional(filename):
    numbers = []
    with open(filename, 'r') as file:
        for line in file:
            if line.strip():  # 跳过空行
                numbers.append(float(line.strip()))
    return numbers

嵌套列表推导式

# 处理二维数据
class DataPoint:
    def __init__(self, values):
        self.values = values
 
data_points = [
    DataPoint([1, 2, 3]),
    DataPoint([4, 5, 6]),
    DataPoint([7, 8, 9])
]
 
# 提取所有值并展平
all_values = [value for dp in data_points for value in dp.values]
print(all_values)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]
 
# 提取每个数据点的第一个值
first_values = [dp.values[0] for dp in data_points]
print(first_values)  # [1, 4, 7]

在S&P 500分析中的应用

# 使用列表推导式简化之前的S&P 500分析代码
def sp500_analysis_improved():
    filename = askopenfilename(title="Select CSV file", filetypes=[("CSV files", "*.csv")])
    
    with open(filename, 'r', encoding='utf-8') as file:
        reader = csv.DictReader(file)
        
        # 使用列表推导式一次性读取所有行
        rows = [row for row in reader]
        
        # 使用列表推导式提取所有行业
        sectors = list(set([row.get('Sector', 'Unknown') for row in rows]))
        
        # 使用列表推导式计算每个行业的公司数量
        sector_counts = {sector: len([row for row in rows if row.get('Sector') == sector]) 
                        for sector in sectors}
        
        # 使用列表推导式计算每个行业的总市值
        sector_market_caps = {
            sector: sum([float(row.get('Market Cap', 0)) 
                        for row in rows if row.get('Sector') == sector])
            for sector in sectors
        }

性能优势

import time
 
# 列表推导式通常比传统循环更快
def benchmark_comprehension():
    points = [Point(i, i*2) for i in range(1000000)]
    
    # 方法1: 列表推导式
    start = time.time()
    x_coords = [p.x for p in points]
    time1 = time.time() - start
    
    # 方法2: 传统循环
    start = time.time()
    x_coords_manual = []
    for p in points:
        x_coords_manual.append(p.x)
    time2 = time.time() - start
    
    print(f"列表推导式: {time1:.4f}秒")
    print(f"传统循环: {time2:.4f}秒")
    print(f"速度提升: {time2/time1:.2f}x")

总结

  • [p.x for p in points] 是Python的列表推导式语法
  • 用于从可迭代对象中快速提取属性或转换数据
  • 比传统for循环更简洁、易读、高效
  • 可以结合条件语句进行过滤
  • 在数据分析、文件处理等场景中广泛应用
  • 是Pythonic编程的重要特性之一

这种语法体现了Python的”简洁优雅”哲学,让代码更加Pythonic!

下一章