47 Python条件语句和循环语句
本章定位(复习与补充训练层):本章对应正课第8章《If条件语句 制作交易条件单》(章节 8)与第9章《循环语句 股价涨跌幅提醒》(章节 9),用于复习、补缺与额外练习。建议先不看讲解,直接尝试下方平台任务,再对照解析补弱项。本章不属于必修主线,并与第39章《控制结构》(章节 40)互为补充训练(本章为分项强化版)。建议先不看讲解,直接尝试下方平台任务;先做本章『动手与思考』第 1 题与平台任务自测,通过即可跳过本章。
47.1 引言控制结构与算法设计
控制结构决定程序”先做什么、后做什么、何时重复”。本章以平台四任务为骨架,复习 if-elif-else、for、while 与 break/continue/pass 的配合使用。
47.2 本章学习目标
通过本章的复习与补充训练,你将能够:
- 不看正文,手写
if-elif-else多分支结构,说明缩进、冒号与真值测试在分支选择中的作用 - 用
for遍历列表、字典(items()、enumerate())与range(),并用while写出一定可以终止的价格监控循环 - 区分
break、continue、pass的执行效果,判断循环else子句是否会被执行 - 把”遍历数据列→逐元素判断→收集结果”的筛选套路迁移到新的金融数据(如指数日涨跌幅)上,独立完成平台四个任务
47.3 if条件语句
动手写分支之前,先把理论坐标立起来:任何算法都可以用三种基本控制结构组合表示。
理论背景:控制流的三种基本结构
从计算科学理论的角度,任何算法都可以用三种基本控制结构表示:
- 顺序结构(Sequence): 语句按顺序执行
- 选择结构(Selection): 根据条件选择执行路径
- 循环结构(Iteration): 重复执行代码块
这三种结构构成了结构化程序设计(Structured Programming)的基础,由Edsger W. Dijkstra在1968年提出。Python通过if-elif-else、for、while等关键字实现这三种结构。
# 场景:基于移动平均线的交易策略
# 定义关键价格指标
price = 18.50 # 当前股价
ma20 = 18.00 # 20日移动平均线
ma60 = 17.50 # 60日移动平均线
# if语句:条件判断与分支执行
# 语法: if condition1: elif condition2: ... else:
# if、elif、else后的冒号(:)表示代码块开始
# 条件1:价格同时高于MA20和MA60
# and逻辑运算符:两个条件都为True时,整体为True
if price > ma20 and price > ma60:
signal = '买入'
# 缩进的代码块属于if语句
# Python使用缩进(通常4个空格)表示代码块层次
# 条件2:价格高于MA20但低于MA60
# elif是"else if"的缩写,用于处理多个互斥条件
elif price > ma20:
signal = '持有'
# 条件3:价格低于MA60
elif price < ma60:
signal = '卖出'
# else:以上条件都不满足时执行
# 在这里是:价格介于MA60和MA20之间
else:
signal = '观望'
# 输出交易信号
# f-string是Python 3.6+的格式化字符串语法
print(f'交易信号: {signal}')
# 输出: 交易信号: 买入上面 price > ma20 and price > ma60 这类条件的判断依据,是布尔逻辑。
数学背景:布尔逻辑与条件判断
Python的条件判断基于布尔代数(Boolean Algebra),由George Boole在1854年创立:
| 运算 | 数学符号 | Python运算 | 交换律 | 结合律 | 分配律 |
|---|---|---|---|---|---|
| 与(AND) | \(a \land b\) | a and b |
✓ | ✓ | ✓ |
| 或(OR) | \(a \lor b\) | a or b |
✓ | ✓ | ✓ |
| 非(NOT) | \(\neg a\) | not a |
- | - | - |
短路径求值(Short-Circuit Evaluation):
# Python的and/or运算采用短路径求值
# 一旦能够确定结果,立即停止计算
True and some_function() # 会调用some_function()
False and some_function() # 不会调用some_function(),直接返回False代码深度解析:
条件表达式的真值测试:
# Python中的真假值判定规则 # 以下值被判定为False: # - False、None、数值0(0, 0.0)、空字符串('')、空容器([], {}, set()) # 任何其他值都被判定为True # 这称为"真值测试"(Truth Value Testing) # 示例:检查持仓是否为空 position = 0 if position: print(f'持仓{position}股') else: print('空仓') # 会执行这个分支 # 显式比较(推荐) if position != 0: print(f'持仓{position}股') else: print('空仓')链式比较(Chained Comparison):
# Python支持链式比较,数学上更自然 price = 15.0 # 方法1:链式比较(Python风格) if 10 < price < 20: print('价格在10到20之间') # 等价于方法2:逻辑与运算 if price > 10 and price < 20: print('价格在10到20之间') # 链式比较更简洁,且price只计算一次三元运算符(Ternary Operator):
# Python的三元表达式: 值1 if 条件 else 值2 # 适用于简单的条件赋值 # 示例:计算交易手续费 amount = 10000 commission_rate = 0.0003 if amount > 10000 else 0.0005 # 如果金额>10000,费率0.03%,否则0.05% print(f'手续费率: {commission_rate:.4f}') # 等价的if-else语句 if amount > 10000: commission_rate = 0.0003 else: commission_rate = 0.0005嵌套if语句的优化:
# 不推荐:深层嵌套 price = 18.5 volume = 1000000 if price > 18: if volume > 500000: if price > ma20: signal = '强力买入' else: signal = '考虑买入' else: signal = '观望' else: signal = '不操作' # 推荐:提前返回(卫语句) def generate_signal(price, volume, ma20): if price <= 18: return '不操作' if volume <= 500000: return '观望' if price > ma20: return '强力买入' return '考虑买入' signal = generate_signal(price, volume, ma20)金融应用:多条件筛选:
# 股票筛选策略 stock = { 'name': '招商银行', 'price': 45.2, 'pe': 8.5, # 市盈率 'pb': 0.8, # 市净率 'roe': 0.15 # 净资产收益率 } # 多条件筛选 conditions = [ stock['price'] < 100, # 价格<100 stock['pe'] < 10, # PE<10 stock['pb'] < 1, # PB<1 stock['roe'] > 0.10 # ROE>10% ] # all()函数:所有条件为True时返回True if all(conditions): print(f'{stock["name"]} 符合价值投资标准') else: print(f'{stock["name"]} 不符合标准')
看完 if-elif-else 的多分支写法,初学者常会问:Python 为什么不像 C 语言那样提供 switch-case?
补充说明:为什么Python没有switch-case?
Python 3.10之前没有传统的switch-case语句,这是有意的设计选择:
if-elif-else已经足够清晰- 字典映射可以替代switch-case
- 避免C语言switch的fall-through陷阱
Python 3.10引入了match-case语句,提供了更强大的模式匹配能力。
47.4 for循环
# 1. 遍历列表
# 创建股票代码列表
stocks = ['600519.SH', '000858.SZ', '600036.SH']
print('股票代码:')
# for循环:遍历序列中的每个元素
# 语法: for item in iterable:
for code in stocks:
# code依次取stocks中的每个元素
print(f' {code}')
# 输出:
# 600519.SH
# 000858.SZ
# 600036.SH
# 2. 遍历字典
stock_dict = {'600519.SH': '贵州茅台', '000858.SZ': '五粮液'}
print('\n股票名称:')
# items()方法返回(键, 值)元组的迭代器
# 这允许同时遍历键和值
for code, name in stock_dict.items():
# 每次迭代,code和name分别取键和值
print(f' {code}: {name}')
# 3. enumerate()获取索引和元素
print('\n带索引的遍历:')
# enumerate()返回(索引, 元素)的迭代器
# start参数指定索引起始值,默认为0
for i, code in enumerate(stocks):
# i从0开始,依次为0, 1, 2
print(f' 第{i+1}个: {code}')
# 输出:
# 第1个: 600519.SH
# 第2个: 000858.SZ
# 第3个: 600036.SH代码深度解析:
for循环的执行流程:
# for循环的等价while循环实现 iterable = ['a', 'b', 'c'] iterator = iter(iterable) # 获取迭代器 # 方式1:for循环(Python风格) for item in iterable: print(item) # 方式2:while循环(等价实现) try: while True: item = next(iterator) # 获取下一个元素 print(item) except StopIteration: pass # 迭代结束 # for循环本质上是迭代器协议的语法糖range()函数详解:
# range()生成整数序列,常用于for循环 # 语法: range(start, stop, step) # 用法1:指定结束值 for i in range(5): print(i) # 输出: 0, 1, 2, 3, 4 # 用法2:指定起始和结束值 for i in range(2, 5): print(i) # 输出: 2, 3, 4 # 用法3:指定步长 for i in range(0, 10, 2): print(i) # 输出: 0, 2, 4, 6, 8 # 金融应用:生成交易日序列 import pandas as pd dates = pd.date_range('2024-01-01', periods=5) for i in range(len(dates)): print(f'第{i+1}个交易日: {dates[i].date()}')zip()函数并行遍历:
# zip()可以同时遍历多个序列 codes = ['600519.SH', '000858.SZ', '600036.SH'] prices = [1850, 220, 45] # 同时遍历代码和价格 for code, price in zip(codes, prices): print(f'{code}: {price}元') # 使用enumerate和zip组合 for i, (code, price) in enumerate(zip(codes, prices)): print(f'{i+1}. {code}: {price}元')字典的多种遍历方式:
portfolio = { '600519.SH': {'name': '茅台', 'shares': 100}, '000858.SZ': {'name': '五粮液', 'shares': 200} } # 方式1:遍历键 print('股票代码:') for code in portfolio: print(f' {code}') # 方式2:遍历键值对 print('\n股票详情:') for code, info in portfolio.items(): print(f' {code}: {info["name"]}, {info["shares"]}股') # 方式3:遍历值 print('\n股票名称:') for info in portfolio.values(): print(f' {info["name"]}')列表推导式(List Comprehension):
# 列表推导式是for循环的简洁表达 # 语法: [expression for item in iterable if condition] # 传统for循环 squares = [] for i in range(10): squares.append(i ** 2) # 列表推导式(Python风格) squares = [i ** 2 for i in range(10)] # 带条件的列表推导式 even_squares = [i ** 2 for i in range(10) if i % 2 == 0] print(even_squares) # [0, 4, 16, 36, 64] # 金融应用:筛选符合条件的股票 stocks = [ {'name': '茅台', 'pe': 45}, {'name': '五粮液', 'pe': 35}, {'name': '招行', 'pe': 8} ] # 找出PE<20的股票 value_stocks = [s for s in stocks if s['pe'] < 20] print(f'价值股: {[s["name"] for s in value_stocks]}')
47.5 while循环
# 场景:监控股价直到达到目标价
# 定义初始变量
price = 18.00 # 当前价格
target = 20.00 # 目标价格
days = 0 # 已监控天数
# while循环:当条件为True时重复执行
# 语法: while condition:
# 注意:必须确保循环能够终止,否则会形成无限循环
while price < target:
# 每天价格增长0.50元
# += 是增量赋值操作符,等价于 price = price + 0.50
price += 0.50
# 天数加1
days += 1
# 输出当前状态
# :.2f 表示格式化为两位小数
print(f'第{days}天, 价格: {price:.2f}元')
# 安全措施:防止无限循环
# 如果超过100天仍未达到目标价,强制退出
if days > 100:
print('达到最大监控天数')
break # break语句立即跳出循环
print(f'\n达到目标价耗时{days}天')代码深度解析:
while vs for循环的选择:
# for循环:已知迭代次数 for i in range(10): print(i) # while循环:未知迭代次数,根据条件决定 i = 0 while i < 10: print(i) i += 1 # 选择原则: # - 遍历序列 → 用for # - 等待条件满足 → 用whilewhile循环的常见模式:
# 模式1:计数器循环 count = 0 while count < 5: print(count) count += 1 # 模式2:无限循环+break while True: user_input = input('输入命令(quit退出):') if user_input == 'quit': break process_command(user_input) # 模式3:标志变量 running = True while running: # 执行某些操作 if should_stop(): running = Falsewhile-else结构:
# while循环可以带else子句 # else在循环正常结束时执行(break跳出不执行) count = 0 while count < 3: print(count) count += 1 else: print('循环正常结束') # 会执行 # 对比:使用break的情况 count = 0 while count < 3: print(count) count += 1 if count == 2: break else: print('循环正常结束') # 不会执行金融应用:蒙特卡洛模拟:
import random # 模拟股价路径,直到达到止损或止盈 price = 100 stop_loss = 95 # 止损价 take_profit = 105 # 止盈价 days = 0 while stop_loss < price < take_profit: # 每日收益率服从正态分布 # 均值0.001,标准差0.02 daily_return = random.gauss(0.001, 0.02) price *= (1 + daily_return) days += 1 # 安全措施 if days > 252: # 最多模拟一年 break if price >= take_profit: print(f'第{days}天止盈, 价格: {price:.2f}元') elif price <= stop_loss: print(f'第{days}天止损, 价格: {price:.2f}元') else: print(f'第{days}天未达目标, 价格: {price:.2f}元')循环的性能考虑:
# while循环可能比for循环慢 import time # 测试for循环 start = time.time() for i in range(1000000): _ print(f'for循环: {time.time() - start:.4f}秒') # 测试while循环 start = time.time() i = 0 while i < 1000000: _ i += 1 print(f'while循环: {time.time() - start:.4f}秒') # for循环通常更快,因为迭代器协议更优化
47.6 循环控制
# 1. break语句:立即跳出循环
# 场景:查找第一个负收益并判断是否需要止损
returns = [0.05, -0.02, 0.01, -0.015, 0.03]
print('查找负收益:')
# enumerate()同时获取索引和值
for i, ret in enumerate(returns):
# 检查是否为负收益
if ret < 0:
# :.2% 表示格式化为百分比,保留两位小数
print(f' 第{i+1}天: {ret:.2%}')
# 检查是否需要止损(亏损超过1%)
if ret < -0.01:
print(' 触发止损,停止监控')
break # 立即跳出循环,不再检查后续元素
# 2. continue语句:跳过本次迭代,继续下一次
print('\n正收益日:')
for ret in returns:
# 如果是负收益,跳过本次迭代
if ret < 0:
continue # 直接进入下一次循环,不执行后面的代码
# 只有正收益才会执行这里
print(f' {ret:.2%}')代码深度解析:
break的执行流程:
# break只跳出最近的一层循环 for i in range(3): for j in range(3): if j == 1: break # 只跳出内层循环 print(f'i={i}, j={j}') # 输出: # i=0, j=0 # i=1, j=0 # i=2, j=0 # 如果要跳出多层循环,需要使用标志变量 found = False for i in range(3): if found: break for j in range(3): if j == 1: found = True breakcontinue的执行流程:
# continue跳过本次迭代的剩余代码 for i in range(5): if i % 2 == 0: continue # 跳过偶数 print(i) # 只打印奇数 # 输出: 1, 3, 5 # continue的等价写法 for i in range(5): if i % 2 != 0: # 反转条件 print(i)循环else子句与break:
# break会阻止else子句的执行 # 情况1:正常结束(执行else) for i in range(3): if i > 10: break else: print('循环正常结束,未找到') # 会执行 # 情况2:break结束(不执行else) for i in range(3): if i > 1: break else: print('循环正常结束,未找到') # 不会执行金融应用:订单撮合:
# 模拟订单撮合过程 buy_orders = [ {'price': 18.50, 'quantity': 1000}, {'price': 18.45, 'quantity': 500}, {'price': 18.40, 'quantity': 2000} ] sell_orders = [ {'price': 18.35, 'quantity': 800}, {'price': 18.40, 'quantity': 1500} ] # 按价格排序(买单降序,卖单升序) buy_orders.sort(key=lambda x: -x['price']) sell_orders.sort(key=lambda x: x['price']) # 撮合逻辑 for buy in buy_orders: for sell in sell_orders: # 如果买单价格低于卖单价格,无法成交 if buy['price'] < sell['price']: print(f'买单{buy["price"]}元低于卖单{sell["price"]}元,停止撮合') break # 跳出内层循环 # 计算成交量 trade_quantity = min(buy['quantity'], sell['quantity']) print(f'成交: 价格{sell["price"]}元, 数量{trade_quantity}股') # 更新剩余数量 buy['quantity'] -= trade_quantity sell['quantity'] -= trade_quantity # 如果卖单已完全成交,继续下一个卖单 if sell['quantity'] == 0: continue # 如果买单已完全成交,跳出内层循环 if buy['quantity'] == 0: breakpass语句:占位符:
# pass语句什么都不做,用作占位符 # 场景1:空函数体 def placeholder_function(): pass # TODO: 待实现 # 场景2:空类 class PlaceholderClass: pass # 场景3:条件分支占位 if condition: pass # 待添加代码 else: do_something()
47.7 综合应用股票筛选
# 模拟股票数据
# 每个股票是一个字典,包含代码、价格、PE等属性
stocks = [
{'code': '600519.SH', 'price': 1850, 'pe': 45},
{'code': '000858.SZ', 'price': 220, 'pe': 35},
{'code': '600036.SH', 'price': 45, 'pe': 8}
]
# 筛选策略:价格<500且PE<40的价值股
print('筛选结果:')
# 遍历所有股票
for stock in stocks:
# 条件1:价格<500(排除高价股)
if stock['price'] >= 500:
# continue:跳过本次迭代,继续下一个股票
continue
# 条件2:PE<40(合理估值)
# 只有价格条件满足的股票才会到达这里
if stock['pe'] < 40:
# 两个条件都满足,输出股票信息
print(f" {stock['code']}: 价格={stock['price']}元, PE={stock['pe']}")
# 如果PE>=40,什么都不做,继续下一个股票代码深度解析:
控制流程图:
# 本例的执行流程: # # 开始 # ↓ # 遍历stocks列表 # ↓ # 取第一个股票{'code': '600519.SH', 'price': 1850, 'pe': 45} # ↓ # 检查price>=500? # ├─ 是 → continue → 跳过,取下一个股票 # └─ 否 → 继续检查 # ↓ # 检查pe<40? # ├─ 是 → 输出股票信息 # └─ 否 → 什么都不做 # ↓ # 取下一个股票,重复上述流程 # ↓ # 所有股票处理完毕 # ↓ # 结束筛选逻辑的优化:
# 方法1:原始版本(本例使用) for stock in stocks: if stock['price'] >= 500: continue if stock['pe'] < 40: print(f"{stock['code']}: PE={stock['pe']}元") # 方法2:单个复合条件 for stock in stocks: if stock['price'] < 500 and stock['pe'] < 40: print(f"{stock['code']}: PE={stock['pe']}元") # 方法3:列表推导式(最Pythonic) filtered = [s for s in stocks if s['price'] < 500 and s['pe'] < 40] for stock in filtered: print(f"{stock['code']}: PE={stock['pe']}元") # 方法4:filter()函数(函数式风格) filtered = filter( lambda s: s['price'] < 500 and s['pe'] < 40, stocks ) for stock in filtered: print(f"{stock['code']}: PE={stock['pe']}元")多策略组合:
# 更复杂的筛选场景 stocks = [ {'code': '600519.SH', 'price': 1850, 'pe': 45, 'pb': 10, 'dividend': 1.5}, {'code': '000858.SZ', 'price': 220, 'pe': 35, 'pb': 8, 'dividend': 1.2}, {'code': '600036.SH', 'price': 45, 'pe': 8, 'pb': 0.8, 'dividend': 2.5}, {'code': '601318.SH', 'price': 50, 'pe': 9, 'pb': 0.9, 'dividend': 2.0} ] # 策略1:价值投资(低PE低PB) print('=== 价值投资策略 ===') for stock in stocks: if stock['pe'] < 20 and stock['pb'] < 1: print(f"{stock['code']}: PE={stock['pe']}, PB={stock['pb']}") # 策略2:红利策略(高股息) print('\n=== 红利策略 ===') for stock in stocks: if stock['dividend'] > 2.0: print(f"{stock['code']}: 股息率={stock['dividend']}%") # 策略3:成长策略(合理估值) print('\n=== 成长策略 ===') for stock in stocks: if 10 < stock['pe'] < 30 and stock['price'] < 100: print(f"{stock['code']}: PE={stock['pe']}, 价格={stock['price']}")性能优化:提前过滤:
# 优化前:多次遍历 results = [] for stock in stocks: if stock['price'] < 500: if stock['pe'] < 40: if stock['pb'] < 5: results.append(stock) # 优化后:将最严格的条件放在前面 # 这样可以尽早排除不符合条件的元素 results = [] for stock in stocks: # 价格条件最严格,先检查 if stock['price'] >= 500: continue # PE条件次之 if stock['pe'] >= 40: continue # PB条件最后 if stock['pb'] >= 5: continue results.append(stock)实际应用:动态止损策略:
# 动态调整止损策略 positions = [ {'code': '600519.SH', 'entry_price': 1800, 'current_price': 1850}, {'code': '000858.SZ', 'entry_price': 230, 'current_price': 220}, {'code': '600036.SH', 'entry_price': 48, 'current_price': 45} ] # 止损策略:亏损超过5%且亏损超过3元 print('止损信号:') for pos in positions: # 计算收益率 ret = (pos['current_price'] - pos['entry_price']) / pos['entry_price'] # 计算亏损金额 loss = pos['entry_price'] - pos['current_price'] # 检查止损条件 if ret < -0.05 and loss > 3: print(f"{pos['code']}: 亏损{ret:.2%}, 亏损{loss:.0f}元, 建议止损") elif ret < 0: print(f"{pos['code']}: 亏损{ret:.2%}, 继续持有") else: print(f"{pos['code']}: 盈利{ret:.2%}, 继续持有")
最佳实践总结:
- 选择合适的循环结构:
- 遍历序列 → 用
for循环 - 等待条件 → 用
while循环 - 已知次数 → 用
for+range() - 未知次数 → 用
while条件
- 遍历序列 → 用
- 避免常见陷阱:
- 无限循环:确保
while循环有终止条件 - 修改遍历对象:不要在遍历时修改列表
- 过深嵌套:超过3层嵌套考虑重构
- 循环中的计算:将不变的计算移到循环外
- 无限循环:确保
- 性能优化技巧:
- 使用列表推导式替代简单的for循环
- 使用生成器表达式处理大数据
- 将最严格的条件放在前面
- 使用内置函数(
sum(),max(),min())替代循环
- 可读性提升:
- 使用有意义的变量名
- 提取复杂条件为函数
- 添加注释说明逻辑意图
- 保持函数简短(单一职责)
平台任务(平台原始代码)
以下代码与教学平台任务要求完全一致:
任务要求:任务一,遍历道琼斯、恒生、上证、深证四列日涨跌幅,把每列收集成列表并依次打印;任务二,遍历道琼斯列表,遇到小于 -0.006 的数据即用 break 跳出;任务三,遍历恒生列表,上涨日用 pass 跳过、只输出下跌日;任务四,把上证综指处于设定区间内的日涨跌幅收集到新列表并打印。请将代码原样输入教学平台(注释除外),判定以平台为准。
# 注:该代码块读取教学平台的 OSS 镜像直链(联网即可在本地复现)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
DJ_index = [] # 定义列表DJ_index
HS_index= [] # 定义列表HS_index
SH_index = [] # 定义列表SH_index
SZ_index = [] # 定义列表SZ_index
for i in data["道琼斯工业平均指数"]: # 遍历data["道琼斯工业平均指数"]中的每个i
DJ_index.append(i) # 将道琼斯指数涨跌幅数据添加到列表
for i in data["恒生指数"]: #获取恒生指数的日涨跌幅列表
HS_index.append(i) # 将恒生指数涨跌幅数据添加到列表
for i in data["上证综指"]: #获取上证综指的日涨跌幅列表
SH_index.append(i) # 将上证综指涨跌幅数据添加到列表
for i in data["深证成指"]: # 遍历data["深证成指"]中的每个i
SZ_index.append(i) # 将深证成指涨跌幅数据添加到列表
print(DJ_index) # 输出指数数据
print(HS_index) # 输出指数数据
print(SH_index) # 输出指数数据
print(SZ_index) # 输出指数数据
#任务二
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
DJ_index = [] # 定义列表DJ_index
for i in data["道琼斯工业平均指数"]: # 遍历data["道琼斯工业平均指数"]中的每个i
DJ_index.append(i) # 将道琼斯指数涨跌幅数据添加到列表
for i in DJ_index: # 遍历DJ_index中的每个i
if i < -0.006: #如果小于-0.006
break # 跳出循环
print("已经访问的道琼斯工业平均指数日涨跌幅数据",i) # 输出已经访问的道琼斯工业平均指数日涨跌幅数据
#任务三
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
HS_index = [] # 定义列表HS_index
for i in data["恒生指数"]: # 遍历data["恒生指数"]中的每个i
HS_index.append(i) # 将恒生指数涨跌幅数据添加到列表
for i in HS_index: # 遍历HS_index中的每个i
if i > 0: #大于零的话
pass # 占位符,暂不执行任何操作
else: # 不满足以上条件时
print("恒生指数的日下跌数据(负数)",i) # 输出恒生指数的日下跌数据(负数)
#任务四
import pandas as pd
# 从Excel文件读取数据存入data
data=pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx")
SH_index = [] # 定义列表SH_index
SZ_index = [] # 定义列表SZ_index
for i in data["上证综指"]: # 遍历data["上证综指"]中的每个i
SH_index.append(i) # 将上证综指涨跌幅数据添加到列表
for i in data["深证成指"]: # 遍历data["深证成指"]中的每个i
SZ_index.append(i) # 将深证成指涨跌幅数据添加到列表
R_SH = [] #创建一个R_SH的空列表
for i in SH_index: # 遍历SH_index中的每个i
if i < -0.1: #如果小于-0.1
pass # 占位符,暂不执行任何操作
elif i > 0.01: #如果大于0.01
pass # 占位符,暂不执行任何操作
else: # 不满足以上条件时
R_SH.append(i) # 将上证综指筛选后的涨跌幅数据添加到列表
print("上证综指的日涨跌幅处于-1%至1%区间的列表\n",R_SH) # 输出上证综指的日涨跌幅处于-1%至1%区间的列表\n预期输出(OSS 直链本机实测,数据文件为 2024-08-01 至 2024-08-16 共 12 个交易日的四大指数日涨跌幅;具体以平台运行结果为准):
任务一依次打印道琼斯、恒生、上证、深证四个列表,每个 12 个数值:
[-0.0121, -0.0151, -0.026, 0.0076, -0.006, 0.0176, 0.0013, -0.0036, 0.0104, 0.0061, 0.0139, 0.0024]
[-0.0023, -0.0208, -0.0146, -0.0031, 0.0138, 0.0008, 0.0117, 0.0013, 0.0036, -0.0035, -0.0002, 0.0188]
[-0.0022, -0.0092, -0.0154, 0.0023, 0.0009, 0.0, -0.0027, -0.0014, 0.0034, -0.006, 0.0094, 0.0007]
[-0.0092, -0.0138, -0.0185, 0.0082, -0.0017, -0.0004, -0.0062, -0.0024, 0.0043, -0.0117, 0.0071, -0.0024]
- 任务二在当前平台数据下首日(2024-08-01 道琼斯 -0.0121)即触发
break,无任何输出行——这不是代码坏了,而是首观测就满足终止条件 - 任务三打印 6 行”恒生指数的日下跌数据(负数)“,对应恒生列表中的全部 6 个负值,依次为
-0.0023、-0.0208、-0.0146、-0.0031、-0.0035、-0.0002 - 任务四先打印一行”上证综指的日涨跌幅处于-1%至1%区间的列表”,随后输出 12 个数值——当前数据全部落在代码设定的
[-0.1, 0.01]区间内(注意下限是 -0.1 即 -10%,而非输出文字所称的 -1%),故该列表与上证综指原始列表完全相同
47.8 本章小结
要点:
- 三种控制结构:顺序、选择(
if-elif-else)、循环(for/while);for适合遍历已知序列,while适合等待条件满足 - 条件判断基于真值测试:数值 0、空字符串、空容器、
None、False被判定为假,其余为真;多条件用and/or/not组合,支持链式比较(10 < price < 20) for的常用搭档:range(start, stop, step)生成整数序列,enumerate()同时取序号与元素,items()同时取字典的键与值- 循环控制三兄弟:
break立即跳出所在层循环,continue跳过本次迭代,pass仅作占位;循环正常走完才执行else子句,被break打断则不执行 - 筛选套路:遍历→条件分支决定”收集/跳过/终止”,可等价改写为复合条件判断或列表推导式,后者更简洁
易错点:
if/elif/else后的冒号与 4 空格缩进决定代码块归属,缩进不一致会改变分支逻辑甚至直接报错while循环忘记更新循环变量会陷入无限循环,实务上应设置最大迭代次数等安全阀break只跳出最近一层循环,多层循环需要借助标志变量逐层退出- 遍历列表的同时原地修改列表,容易漏元素或越界,应遍历副本或改用推导式生成新列表
- 平台任务二把
if i < -0.006: break写在print之前,触发终止当天的数据不会被输出;先打印再判断会多出一行结果;且在当前平台数据下首日(2024-08-01,道琼斯 -0.0121)就满足终止条件,整个循环一行都不输出,属正常现象而非代码出错
47.9 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
returns = [0.03, -0.008, 0.012, -0.02] count = 0 for r in returns: if r < 0: count += 1 if r < -0.01: break print(count)参考答案(先写下你的预测再点开)
解题思路:逐个跟踪
count。0.03不小于 0,跳过;-0.008小于 0,count变 1,但-0.008 < -0.01不成立(-0.008 比 -0.01 大),不触发break;0.012跳过;-0.02小于 0,count变 2,且-0.02 < -0.01成立,break终止循环。最终输出2——内层if只统计下跌日,break只在跌幅超过 1% 时触发。# 验证脚本:原样运行题面代码 returns = [0.03, -0.008, 0.012, -0.02] # 四个交易日的收益率 count = 0 # 下跌日计数器 for r in returns: # 遍历收益率序列 if r < 0: # 下跌日 count += 1 # 计数加一 if r < -0.01: # 跌幅超过1% break # 立即终止循环 print(count) # 输出计数值预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
2自测回忆:不看正文,完整列举 Python 中被判定为
False的值;再说明elif price > ma20这个分支在什么前提下才会被求值。参考答案(点开前请先独立完成)
解题思路:被判定为
False的值——布尔值False与None;数值零(整数0、浮点0.0、复数0j);空序列与空容器(空字符串''、空列表[]、空元组()、空字典{}、空集合set()、空range(0))。其余一切值均为True(注意'0'是非空字符串、为真)。elif price > ma20只有在它之前的所有分支条件(如if price > ma5:等)都为False、且轮到该分支时才会被求值——elif链自上而下短路匹配,命中一个分支后其余分支不再判断。回扣主线章:真值测试与
if-elif-else匹配顺序的讲解见第 章节 8 章。变式任务(平台任务同型改造):把平台任务三改为”输出恒生指数的日上涨数据(正数)“,再进一步改为”只输出涨幅大于 1% 的交易日”;说明
pass分支的条件应如何随之调整。参考答案(点开前请先独立完成)
解题思路:平台任务三“输出下跌日”的结构是
上涨日 pass、其余输出;改为“输出上涨日”只需把pass分支的条件反转为i <= 0(下跌或平盘日跳过),输出分支打印正数;进一步“只输出涨幅大于 1%”则把跳过条件收紧为i <= 0.01(不超过 1% 的都跳过)。规律:pass分支承接“不想要的情形”,筛选标准提高时跳过条件随之收紧。# 变式程序:输出恒生指数的日上涨数据,以及涨幅大于1%的交易日 import pandas as pd # 导入pandas用于读取Excel data = pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx') # 读取平台OSS直链的指数涨跌幅数据 hs_index = [] # 定义恒生指数涨跌幅列表 for i in data['恒生指数']: # 遍历恒生指数列 hs_index.append(i) # 逐个收集为列表 for i in hs_index: # 第一问:输出上涨日 if i <= 0: # 下跌或平盘日 pass # 跳过 else: print('恒生指数的日上涨数据(正数)', i) # 输出上涨日 for i in hs_index: # 第二问:只输出涨幅大于1%的交易日 if i <= 0.01: # 涨幅不超过1% pass # 跳过 else: print('恒生指数涨幅大于1%的交易日', i) # 输出大于1%的交易日预期输出(OSS 直链 2026-08-27 本机 peter 环境实际拉取运行结果,具体以平台运行结果为准;该直链为公开镜像,若失效请改在教学平台读取同一数据文件):
恒生指数的日上涨数据(正数) 0.0138 恒生指数的日上涨数据(正数) 0.0008 恒生指数的日上涨数据(正数) 0.0117 恒生指数的日上涨数据(正数) 0.0013 恒生指数的日上涨数据(正数) 0.0036 恒生指数的日上涨数据(正数) 0.0188 恒生指数涨幅大于1%的交易日 0.0138 恒生指数涨幅大于1%的交易日 0.0117 恒生指数涨幅大于1%的交易日 0.0188注意:以上为本题变式的独立代码;列表 47.6 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
变式任务:把平台任务一中的任意一个
for循环改写为while循环,要求输出与原来完全一致,并说明你用什么条件保证循环一定终止。参考答案(点开前请先独立完成)
解题思路:取任务一“把道琼斯列收集成列表”的 for 循环改写。for 版由序列长度自动控制次数;while 版需要自建位置索引
pos,循环条件写pos < len(序列)——索引一旦越界条件即失效,循环必然终止,且循环体末尾必须pos += 1推进索引(漏写即无限循环)。两版输出完全一致。# 变式程序:任务一“收集道琼斯列”的for循环改写为while循环 import pandas as pd # 导入pandas用于读取Excel data = pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx') # 读取平台OSS直链的指数涨跌幅数据 dj_index = [] # 定义道琼斯涨跌幅列表 pos = 0 # 位置索引,从0开始 while pos < len(data['道琼斯工业平均指数']): # 索引未越界就继续循环 dj_index.append(data['道琼斯工业平均指数'][pos]) # 按索引收集元素 pos += 1 # 推进索引,保证循环一定终止 print(dj_index) # 输出与任务一for循环完全一致的列表预期输出(OSS 直链 2026-08-27 本机 peter 环境实际拉取运行结果,具体以平台运行结果为准;该直链为公开镜像,若失效请改在教学平台读取同一数据文件):
[-0.0121, -0.0151, -0.026, 0.0076, -0.006, 0.0176, 0.0013, -0.0036, 0.0104, 0.0061, 0.0139, 0.0024]注意:以上为本题变式的独立代码;列表 47.6 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣主线章:for 与 while 的选择准则见第 章节 9 章。
思考题:平台任务二用
break在遇到第一个小于 -0.006 的数据时停止遍历。如果想知道”最后一个”满足条件的数据是什么,应如何改写循环?请分别用for和while各实现一次。参考答案(点开前请先独立完成)
解题思路:两种思路。思路一(正向遍历、记录后继续):把
break换成“记录”——每次命中条件就更新last_drop,循环自然走完,变量里留下的就是最后一个满足条件的数据;适合还要继续统计其他信息的场合。思路二(反向遍历、命中即停):索引从末尾往前走,while pos >= 0且循环体内命中dj_index[pos] < -0.006就break——从后往前的第一个就是“最后一个”,效率更高(当前数据在第 3 个就命中,即 -0.026)。# 思考题解答:分别用for与while找出最后一个小于-0.006的数据 import pandas as pd # 导入pandas用于读取Excel data = pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/1725522421307.xlsx') # 读取平台OSS直链的指数涨跌幅数据 dj_index = list(data['道琼斯工业平均指数']) # 道琼斯涨跌幅列表(同平台任务二) last_drop = None # 记录最后一个满足条件的数据 for i in dj_index: # for版:正向遍历,命中就覆盖记录 if i < -0.006: # 满足平台任务二的条件 last_drop = i # 覆盖旧记录 print('for版最后一个小于-0.006的数据:', last_drop) # 输出最后命中的数据 pos = len(dj_index) - 1 # while版:从末尾索引开始 while pos >= 0: # 索引未走完就继续 if dj_index[pos] < -0.006: # 从后往前首个命中 print('while版最后一个小于-0.006的数据:', dj_index[pos]) # 输出该数据 break # 命中即最后一个,立即终止 pos -= 1 # 索引前移预期输出(OSS 直链 2026-08-27 本机 peter 环境实际拉取运行结果,具体以平台运行结果为准;该直链为公开镜像,若失效请改在教学平台读取同一数据文件):
for版最后一个小于-0.006的数据: -0.026 while版最后一个小于-0.006的数据: -0.026回扣主线章:
break的终止时机与循环改写的讲解见第 章节 9 章。