45 Python内置函数
本章定位(复习与补充训练层):本章对应正课第4章《常用内置函数》(章节 4),用于复习、补缺与额外练习。建议先不看讲解,直接尝试下方平台任务,再对照解析补弱项。本章不属于必修主线。先做本章『动手与思考』第 1 题与平台任务自测,通过即可跳过本章。
45.1 引言Python内置函数的设计哲学
内置函数无需导入任何模块即可使用,是Python编程的基础工具箱:数学统计、序列操作、迭代器与类型转换一应俱全。本章用一个含五个小任务的平台代码块,检验你对常用内置函数的复习效果。
45.2 本章学习目标
本章为复习与补充训练,目标是检验你是否能脱离正课独立完成下列任务:
- 能独立完成平台任务一:用
len()统计券商列表元素个数,用list(enumerate(stock, start=1))输出带序号的排名列表 - 能独立完成平台任务二:用
sum()求十家券商净利润总和,配合len()与round(x, 4)计算并输出平均值 - 能独立完成平台任务三:用
max()、min()找出一季度股价的最大/最小涨幅,用sorted()对股价升序排列 - 能独立完成平台任务四:用
zip()把名称、代码、净利润、涨幅、股价五个平行列表打包为元组列表;任务五:用int()对整手买入的股数取整 - 能说出
map()、filter()的作用,以及round()的”银行家舍入”行为
若上述任务无法独立完成,请回到正课第4章对应小节补弱,再回到本章练习。
45.3 常用内置函数
平台任务(平台原始代码)
以下代码与教学平台任务要求完全一致:
任务要求(单块含任务一至任务五):给定2023年净利润排名前10位证券公司的相关数据列表。 - 任务一:用 len() 统计券商列表元素个数,用 list(enumerate(stock, start=1)) 输出带序号的排名列表; - 任务二:用 sum() 求十家券商净利润总和,配合 len() 与 round(x, 4) 计算并输出平均值; - 任务三:用 max()、min() 找出一季度股价的最大/最小涨幅,用 sorted() 对股价升序排列; - 任务四:用 zip() 把名称、代码、净利润、涨幅、股价五个平行列表打包为元组列表输出; - 任务五:用 int() 对”整手买入”的股数取整并输出。 请按平台原始题面原样输入(注释除外),判定以平台为准。
# 注:平台题面为填空形式,下方展示的是填空完成后的参考形态;请按平台原始题面原样输入,以平台判定为准
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
stock = ["中信证券","华泰证券","国泰君安","招商证券","中国银河","广发证券","中信建投","国信证券","中金公司","申万宏源"]
print(len(stock)) #计算列表中元素的个数
print(list(enumerate(stock,start=1))) #创建带有索引并且以列表方式分输出
#任务二
profit = [205.39,130.36,98.85,87.69,78.84,78.63,70.47,64.27,61.64,54.75]
stock = ["中信证券","华泰证券","国泰君安","招商证券","中国银河","广发证券","中信建投","国信证券","中金公司","申万宏源"] # 定义列表stock
profit_total = sum(profit) #计算10家证券公司的净利润总和
profit_average = profit_total/len(stock) # 获取数据长度
print("2023年净利润排名前10位的证券公司净利润总和(亿元)",profit_total) # 输出2023年净利润排名前10位的证券公司净利润总和(
print("2023年净利润排名前10位的证券公司净利润平均数(亿元)",round(profit_average,4)) # 输出2023年净利润排名前10位的证券公司净利润平均数
#任务三
return_Q1 = [-0.0588,-0.0067,-0.0698,0.0239,-0.0059,-0.0672,-0.0739,-0.0193,-0.1543,0.0046]
return_max = max(return_Q1) #找出最大涨幅
return_min = min(return_Q1) #找出最小涨幅
print("2024年1季度股价的最大涨幅",return_max) # 输出2024年1季度股价的最大涨幅
print("2024年1季度股价的最小涨幅",return_min) # 输出2024年1季度股价的最小涨幅
price = [18.73,13.61,13.47,13.71,11.76,13.05,21.68,8.11,32.03,4.40] # 定义列表price
price_sorted = sorted(price) #将股价由小到大排序
print(price_sorted) # 输出价格数据
#任务四
stock = ["中信证券","华泰证券","国泰君安","招商证券","中国银河","广发证券","中信建投","国信证券","中金公司","申万宏源"]
# 定义列表code
code = ["600030","601688","601211","600999","601881","000776","601066","002736","601995","000166"]
profit = [205.39,130.36,98.85,87.69,78.84,78.63,70.47,64.27,61.64,54.75] # 定义列表profit
# 定义列表return_Q1
return_Q1 = [-0.0588,-0.0067,-0.0698,0.0239,-0.0059,-0.0672,-0.0739,-0.0193,-0.1543,0.0046]
price = [18.73,13.61,13.47,13.71,11.76,13.05,21.68,8.11,32.03,4.40] # 定义列表price
print(list(zip(stock,code,profit,return_Q1,price))) #将多个列表中对应的元素打包为一个元组并以列表方式输出
#任务五
fund = 1e7 #H公司的投资资金额
price_huatai = 13.76 #华泰证券股票
N = 100 # 设置期数/数量为100
share = N*int(fund/(price_huatai*N)) # 将数据转换为整数
print("H公司购买华泰证券的股票数量",share) # 输出H公司购买华泰证券的股票数量预期输出(本机实际运行结果,具体以平台运行结果为准):
10
[(1, '中信证券'), (2, '华泰证券'), (3, '国泰君安'), (4, '招商证券'), (5, '中国银河'), (6, '广发证券'), (7, '中信建投'), (8, '国信证券'), (9, '中金公司'), (10, '申万宏源')]
2023年净利润排名前10位的证券公司净利润总和(亿元) 930.89
2023年净利润排名前10位的证券公司净利润平均数(亿元) 93.089
2024年1季度股价的最大涨幅 0.0239
2024年1季度股价的最小涨幅 -0.1543
[4.4, 8.11, 11.76, 13.05, 13.47, 13.61, 13.71, 18.73, 21.68, 32.03]
[('中信证券', '600030', 205.39, -0.0588, 18.73), ('华泰证券', '601688', 130.36, -0.0067, 13.61), ('国泰君安', '601211', 98.85, -0.0698, 13.47), ('招商证券', '600999', 87.69, 0.0239, 13.71), ('中国银河', '601881', 78.84, -0.0059, 11.76), ('广发证券', '000776', 78.63, -0.0672, 13.05), ('中信建投', '601066', 70.47, -0.0739, 21.68), ('国信证券', '002736', 64.27, -0.0193, 8.11), ('中金公司', '601995', 61.64, -0.1543, 32.03), ('申万宏源', '000166', 54.75, 0.0046, 4.4)]
H公司购买华泰证券的股票数量 726700
# 创建数值列表,用于演示各种内置函数
numbers = [1, 2, 3, 4, 5]
# 1. len(): 获取序列长度
# 返回序列中元素的个数
# 适用于字符串、列表、元组、字典等
print(f'长度: {len(numbers)}')
# 输出: 5
# 2. max(): 返回最大值
# 对数值序列,返回最大的数
# 对字符串序列,按Unicode编码比较
print(f'最大值: {max(numbers)}')
# 输出: 5
# 3. min(): 返回最小值
# 与max()类似,返回最小的元素
print(f'最小值: {min(numbers)}')
# 输出: 1
# 4. sum(): 计算序列和
# 从左到右对元素求和
# 注意:sum()只支持数值类型
print(f'求和: {sum(numbers)}')
# 输出: 15 (1+2+3+4+5)
# 5. sorted(): 返回排序后的新列表
# 原序列保持不变
# 默认升序,reverse=True可降序
print(f'排序: {sorted(numbers, reverse=True)}')
# 输出: [5, 4, 3, 2, 1]
# 6. abs(): 返回绝对值
# 对整数或浮点数取绝对值
# 对复数,返回模长
print(f'绝对值: {abs(-10)}')
# 输出: 10
# 7. round(): 四舍五入
# 语法: round(number, ndigits)
# ndigits指定小数位数,默认为0(取整)
print(f'四舍五入: {round(3.14159, 2)}')
# 输出: 3.14
# 8. pow(): 幂运算
# 等价于 x ** y,但支持模运算
# pow(x, y, mod) 等价于 (x ** y) % mod
print(f'幂运算: {pow(2, 3)}')
# 输出: 8代码深度解析:
len()的时间复杂度:
# Python对象内部维护长度信息 # 因此len()的时间复杂度是O(1) # 列表 lst = [1, 2, 3] # 内部结构: PyObject_VAR_HEAD包含ob_size字段 print(len(lst)) # 直接读取ob_size,无需遍历 # 字符串 s = "hello" # 同样直接读取长度信息 print(len(s)) # O(1) # 但对于生成器等迭代器,len()不适用 # 需要转换为列表或手动计数max()/min()的多键排序:
# 复杂结构的多键比较 portfolio = [ {'name': '茅台', 'price': 1850, 'pe': 45}, {'name': '五粮液', 'price': 220, 'pe': 32}, {'name': '招商银行', 'price': 45, 'pe': 8} ] # 找出PE最高的股票 # key参数指定比较依据 highest_pe = max(portfolio, key=lambda x: x['pe']) print(f'PE最高: {highest_pe["name"]}, PE={highest_pe["pe"]}') # 找出价格最低的股票 lowest_price = min(portfolio, key=lambda x: x['price']) print(f'价格最低: {lowest_price["name"]}, 价格={lowest_price["price"]}')sorted()的稳定性:
# Python的排序是稳定的(stable) # 即相等元素的相对顺序保持不变 # 先按价格排序 stocks = [ ('茅台', 1850, '沪'), ('五粮液', 220, '深'), ('平安', 45, '深'), ('招行', 45, '沪') ] # 按价格升序,价格相同时保持原顺序 sorted_by_price = sorted(stocks, key=lambda x: x[1]) print(sorted_by_price) # ('平安', 45, '深') 在 ('招行', 45, '沪') 之前金融应用:计算统计量:
# 收益率序列 returns = [0.05, -0.02, 0.03, 0.07, -0.01] # 平均收益率 avg = sum(returns) / len(returns) print(f'平均收益率: {avg:.2%}') # 最大收益和最大亏损 max_return = max(returns) min_return = min(returns) print(f'最大收益: {max_return:.2%}') print(f'最大亏损: {min_return:.2%}') # 极差(Range) range_ = max(returns) - min(returns) print(f'收益极差: {range_:.2%}')round()的银行家舍入:
# Python 3使用"银行家舍入"(Round Half to Even) # 当正好为0.5时,舍入到最近的偶数 print(round(2.5)) # 2 (最近的偶数) print(round(3.5)) # 4 (最近的偶数) # 这不同于传统的"四舍五入" # 原因是减少统计偏差 # 传统四舍五入需要使用decimal模块 from decimal import Decimal, ROUND_HALF_UP print(Decimal('2.5').quantize(Decimal('1'), rounding=ROUND_HALF_UP)) # 3
最后从设计层面回顾内置函数:它们体现了语言的实用性(Pragmatism)哲学,并按功能形成清晰的类别划分。
理论背景:函数式编程与内置函数
Python的内置函数(Built-in Functions)体现了语言的实用性(Pragmatism)设计哲学。这些函数无需导入任何模块即可使用,它们构成了Python编程的基础工具箱。从计算机科学的角度,内置函数的设计遵循以下原则:
- 高层抽象: 隐藏底层实现细节,提供简洁接口
- 泛型设计: 同一函数可应用于多种数据类型
- 性能优化: 用C语言实现,比纯Python代码快10-100倍
- 一致性: 遵循统一的命名和调用约定
数学分类:内置函数的功能划分
按功能可将内置函数分为以下类别:
| 类别 | 代表函数 | 数量 | 金融应用场景 |
|---|---|---|---|
| 数学运算 | abs(), round(), sum() |
8 | 收益计算,四舍五入 |
| 序列操作 | len(), max(), min(), sorted() |
12 | 价格排序,极值查找 |
| 迭代器 | map(), filter(), zip() |
7 | 数据转换,批量处理 |
| 类型转换 | int(), float(), str() |
15 | 数据清洗,类型转换 |
| 对象操作 | isinstance(), id(), hash() |
10 | 类型检查,内存管理 |
| 输入输出 | print(), input(), open() |
5 | 数据读取,结果输出 |
补充说明:为什么内置函数用C实现?
以sum()函数为例:
# 纯Python实现(慢)
def sum_python(iterable):
total = 0
for item in iterable:
total += item
return total
# C实现(快)
# 省去了Python字节码解释的开销
# 直接在C层面循环,速度提升10-50倍性能对比测试(求和100万个整数):
sum_python(): 约50mssum(): 约5ms- 性能提升: 10倍
45.4 高级内置函数
# 1. map(): 应用函数到可迭代对象的每个元素
# 语法: map(function, iterable, ...)
# 返回一个迭代器,需要用list()转换为列表
prices = ['10.5', '20.3', '15.8']
# 将字符串列表转换为浮点数列表
# float()是内置函数,将字符串转换为浮点数
prices_float = list(map(float, prices))
print(f'转换为浮点数: {prices_float}')
# 输出: [10.5, 20.3, 15.8]
# 2. filter(): 过滤元素
# 语法: filter(function, iterable)
# function返回True时保留元素,False时过滤掉
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 使用lambda表达式定义过滤条件
# lambda x: x % 2 == 0 表示"偶数"
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f'偶数: {evens}')
# 输出: [2, 4, 6, 8, 10]
# 3. zip(): 配对多个可迭代对象
# 将多个序列对应位置的元素组合成元组
names = ['张三', '李四', '王五']
ages = [25, 30, 35]
# zip()返回迭代器,每个元素是一个元组
pairs = list(zip(names, ages))
print(f'配对: {pairs}')
# 输出: [('张三', 25), ('李四', 30), ('王五', 35)]
# zip()的妙用:字典构造
# 直接将两个列表转换为字典
name_age_dict = dict(zip(names, ages))
print(f'字典: {name_age_dict}')
# 输出: {'张三': 25, '李四': 30, '王五': 35}
# 4. enumerate(): 同时获取索引和元素
# 语法: enumerate(iterable, start=0)
# 返回(索引, 元素)的迭代器
stocks = ['茅台', '五粮液', '招行']
for index, stock in enumerate(stocks, start=1):
print(f'{index}. {stock}')
# 输出:
# 1. 茅台
# 2. 五粮液
# 3. 招行代码深度解析:
map()的等价实现:
# map()的纯Python实现 def map_python(func, iterable): result = [] for item in iterable: result.append(func(item)) return result # 使用示例 numbers = [1, 2, 3, 4, 5] squared = list(map(lambda x: x**2, numbers)) print(squared) # [1, 4, 9, 16, 25] # 等价于列表推导式 squared = [x**2 for x in numbers] print(squared) # [1, 4, 9, 16, 25] # 列表推导式通常更Pythonic且更易读filter()的等价实现:
# filter()的纯Python实现 def filter_python(func, iterable): result = [] for item in iterable: if func(item): # 如果函数返回True result.append(item) return result # 使用示例 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 过滤出大于5的数 greater_than_5 = list(filter(lambda x: x > 5, numbers)) print(greater_than_5) # [6, 7, 8, 9, 10] # 等价于列表推导式 greater_than_5 = [x for x in numbers if x > 5] print(greater_than_5) # [6, 7, 8, 9, 10]zip()的配对规则:
# zip()以最短的序列为准 list1 = [1, 2, 3, 4, 5] list2 = ['a', 'b', 'c'] # 只配对前3个元素 zipped = list(zip(list1, list2)) print(zipped) # [(1, 'a'), (2, 'b'), (3, 'c')] # zip(*)的反向操作:解包 matrix = [(1, 'a'), (2, 'b'), (3, 'c')] numbers, letters = zip(*matrix) print(numbers) # (1, 2, 3) print(letters) # ('a', 'b', 'c')函数式编程应用:
# 组合使用map()、filter()、reduce() from functools import reduce # reduce在Python 3中移到functools # 场景:计算股票组合的加权收益率 stocks = [ {'name': '茅台', 'return': 0.05, 'weight': 0.4}, {'name': '五粮液', 'return': 0.03, 'weight': 0.3}, {'name': '招行', 'return': 0.02, 'weight': 0.3} ] # 1. 提取收益率 returns = list(map(lambda x: x['return'], stocks)) # 2. 提取权重 weights = list(map(lambda x: x['weight'], stocks)) # 3. 计算加权收益率 # reduce()对序列进行累积计算 weighted_return = sum(r * w for r, w in zip(returns, weights)) print(f'加权收益率: {weighted_return:.2%}')金融应用:数据清洗流水线:
# 原始数据:字符串格式的价格和数量 raw_data = [ ('10.5', '100'), ('20.3', '200'), ('15.8', '150') ] # 使用map()进行数据转换 prices = list(map(lambda x: float(x[0]), raw_data)) quantities = list(map(lambda x: int(x[1]), raw_data)) # 计算每笔交易金额 amounts = list(map(lambda p, q: p * q, prices, quantities)) print(f'交易金额: {amounts}') # [1050.0, 4060.0, 2370.0] # 过滤出金额大于2000的交易 large_trades = list(filter(lambda x: x > 2000, amounts)) print(f'大额交易: {large_trades}') # [4060.0, 2370.0]
45.5 金融应用实例投资组合分析
# 定义投资组合的日收益率序列
# 正值表示上涨,负值表示下跌
returns = [0.05, -0.02, 0.03, 0.07, -0.01]
# 1. 基础统计:平均收益率
# sum()计算总和,len()获取个数
avg_return = sum(returns) / len(returns)
print(f'平均收益率: {avg_return:.2%}')
# 输出: 平均收益率: 2.40%
# 2. 统计正收益天数
# 使用列表推导式过滤正收益
# len()统计数量
positive_returns = [r for r in returns if r > 0]
positive_count = len(positive_returns)
print(f'正收益次数: {positive_count}/{len(returns)}')
# 输出: 正收益次数: 3/5
# 3. 计算最大回撤
# 最大回撤是从峰值到谷底的最大跌幅
# 这是一个重要的风险指标
cum_return = 1.0 # 初始累计收益
max_drawdown = 0 # 最大回撤
peak = 1.0 # 历史峰值
# 遍历每日收益率
for r in returns:
# 更新累计收益
# cum_return *= (1 + r) 等价于 cum_return = cum_return * (1 + r)
cum_return *= (1 + r)
# 更新历史峰值
# 如果当前收益超过历史峰值,更新峰值
if cum_return > peak:
peak = cum_return
# 计算当前回撤
# 回撤 = (峰值 - 当前值) / 峰值
drawdown = (peak - cum_return) / peak
# 更新最大回撤
# max()函数返回两个值中的较大者
max_drawdown = max(max_drawdown, drawdown)
print(f'最大回撤: {max_drawdown:.2%}')
# 输出: 最大回撤: 2.00%
# 4. 计算夏普比率(简化版)
# 夏普比率衡量单位风险的超额收益
# Sharpe = (平均收益率 - 无风险利率) / 标准差
# 首先计算标准差
import math # 导入math模块用于sqrt()
# 计算方差
mean = sum(returns) / len(returns)
variance = sum((r - mean) ** 2 for r in returns) / len(returns)
# 标准差是方差的平方根
std_dev = math.sqrt(variance)
# 假设无风险利率为2%(年化)
risk_free_rate = 0.02
# 计算夏普比率(年化)
# 假设252个交易日
sharpe_ratio = (mean * 252 - risk_free_rate) / (std_dev * math.sqrt(252))
print(f'夏普比率: {sharpe_ratio:.2f}')
# 输出某个数值
# 5. 计算收益率的分位数
# 使用sorted()和索引计算
sorted_returns = sorted(returns)
n = len(sorted_returns)
# 中位数(50%分位数)
if n % 2 == 0:
median = (sorted_returns[n//2 - 1] + sorted_returns[n//2]) / 2
else:
median = sorted_returns[n//2]
print(f'中位数收益率: {median:.2%}')
# 25%分位数(下四分位数)
q25_index = int(n * 0.25)
q25 = sorted_returns[q25_index]
print(f'25%分位数: {q25:.2%}')
# 75%分位数(上四分位数)
q75_index = int(n * 0.75)
q75 = sorted_returns[q75_index]
print(f'75%分位数: {q75:.2%}')代码深度解析:
最大回撤的计算原理:
# 最大回撤是评估策略风险的关键指标 # 它回答了"从最高点到最低点,最多亏损多少?" # 可视化理解 # 假设累计收益曲线如下: # 1.0 -> 1.05 -> 1.029 -> 1.0599 -> 1.1341 -> 1.1228 # ↑峰值 ↓回撤2% ↑新峰值 ↑新峰值 ↓回撤 # 计算步骤: # 1. 初始峰值 = 1.0 # 2. 第1天: 1.0 * 1.05 = 1.05, 新峰值 = 1.05 # 3. 第2天: 1.05 * 0.98 = 1.029, 回撤 = (1.05-1.029)/1.05 ≈ 2% # 4. 第3天: 1.029 * 1.03 = 1.0599, 新峰值 = 1.0599 # 5. 第4天: 1.0599 * 1.07 = 1.1341, 新峰值 = 1.1341 # 6. 第5天: 1.1341 * 0.99 = 1.1228, 回撤 = (1.1341-1.1228)/1.1341 ≈ 1% # 最终最大回撤 = max(2%, 1%) = 2%标准差的数学含义:
# 标准差衡量数据的离散程度 # 在金融中,标准差代表波动率,是风险的度量 # 计算公式: # σ = sqrt(Σ(xi - μ)² / n) # 其中: # - xi: 第i个收益率 # - μ: 平均收益率 # - n: 样本数量 # 手动计算示例 returns = [0.05, -0.02, 0.03, 0.07, -0.01] mean = sum(returns) / len(returns) # 0.024 # 计算每个收益率与均值的偏差平方 squared_deviations = [(r - mean) ** 2 for r in returns] # 方差是偏差平方的平均 variance = sum(squared_deviations) / len(returns) # 标准差是方差的平方根 std_dev = variance ** 0.5 print(f'标准差(波动率): {std_dev:.4f}')分位数的应用:
# 分位数描述数据的分布特征 # 在投资组合中的应用 # 假设有100只股票的收益率 import random random.seed(42) # 固定随机种子,确保示例结果可复现 stock_returns = [random.uniform(-0.1, 0.2) for _ in range(100)] # 找出表现最好的10%股票 sorted_returns = sorted(stock_returns, reverse=True) top_10_percent = sorted_returns[:10] print(f'前10%股票的平均收益率: {sum(top_10_percent)/len(top_10_percent):.2%}') # 找出表现最差的10%股票 bottom_10_percent = sorted_returns[-10:] print(f'后10%股票的平均收益率: {sum(bottom_10_percent)/len(bottom_10_percent):.2%}')性能优化:使用NumPy替代内置函数:
# 对于大规模数值计算,NumPy比内置函数快得多 import numpy as np import time # 生成大规模数据 random.seed(42) # 固定随机种子,确保示例数据可复现 large_returns = [random.random() for _ in range(1000000)] # 方法1:使用内置函数 start = time.time() mean_python = sum(large_returns) / len(large_returns) std_python = (sum((x - mean_python) ** 2 for x in large_returns) / len(large_returns)) ** 0.5 print(f'Python方法: {time.time() - start:.4f}秒') # 方法2:使用NumPy start = time.time() returns_array = np.array(large_returns) mean_numpy = returns_array.mean() std_numpy = returns_array.std() print(f'NumPy方法: {time.time() - start:.4f}秒') # NumPy通常快10-100倍all()和any()函数的应用:
# all():所有元素为True时返回True # any():任一元素为True时返回True # 应用1:检查所有收益率是否为正 returns = [0.01, 0.02, 0.03, -0.01, 0.02] all_positive = all(r > 0 for r in returns) print(f'所有收益为正: {all_positive}') # False # 应用2:检查是否有任何收益率为负 any_negative = any(r < 0 for r in returns) print(f'存在负收益: {any_negative}') # True # 应用3:风险控制 # 检查是否所有持仓的权重之和为1 weights = [0.3, 0.4, 0.3] weights_sum = sum(weights) print(f'权重归一化: {abs(weights_sum - 1.0) < 1e-10}') # True
最佳实践总结:
优先使用内置函数而非自己实现:
- 内置函数用C实现,性能更好
- 代码更简洁,可读性更高
- 经过充分测试,可靠性更强
函数式编程 vs 列表推导式:
# map/filter:函数式风格 result = list(map(lambda x: x**2, numbers)) # 列表推导式:更Pythonic result = [x**2 for x in numbers] # 推荐:列表表导式更易读性能优化建议:
- 大规模数值计算 → 使用NumPy
- 复杂逻辑判断 → 使用列表推导式或生成器
- 需要延迟计算 → 使用迭代器而非列表
常见陷阱:
# 陷阱1:修改正在遍历的列表 lst = [1, 2, 3, 4, 5] for item in lst: if item % 2 == 0: lst.remove(item) # 危险!可能跳过元素 # 正确做法:创建新列表 lst = [1, 2, 3, 4, 5] lst = [x for x in lst if x % 2 != 0] # 陷阱2:在可变默认参数中使用mutable对象 def foo(lst=[]): # 危险! lst.append(1) return lst # 正确做法 def foo(lst=None): if lst is None: lst = [] lst.append(1) return lst
45.6 延伸阅读(选读)
历史背景:内置函数的演进
Python的内置函数集合随着语言发展不断丰富:
- Python 1.x (1991-2000): 约50个内置函数,强调基础操作
- Python 2.x (2000-2010): 增加到约70个,引入
enumerate(),sorted()等 - Python 3.x (2008-至今): 约80个,增加
all(),any(),ascii()等
45.7 本章小结
要点:
- 数学与统计:
len()(O(1))、sum()、max()/min()(可配key=)、abs()、round()、pow() - 序列工具:
sorted()返回新列表、enumerate()附带索引、zip()按位置配对多个序列 - 类型转换:
int()截断取整、float()、str();平台任务五用int(fund/(price*N))*N实现”按整手买入”的股数取整 map()、filter()返回迭代器,常配list()查看;二者与列表推导式可互相改写round()采用银行家舍入:恰为 .5 时取最近的偶数,如round(2.5)得 2
易错点:
zip()以最短序列为准,两列表长度不齐时会悄悄丢弃多出的数据round(2.5)的结果是 2 不是 3(银行家舍入),与”四舍五入”的直觉不同map()、filter()的结果是一次性迭代器,遍历过后再次使用为空,需要保留就先list()enumerate(stock)不写start=1时序号从 0 开始
45.8 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
概念自测(不看正课):
len()为什么是 O(1)?sorted()与list.sort()有何区别?两个长度不等的列表交给zip()会发生什么?参考答案(点开前请先独立完成)
解题思路:
len()是 O(1) 因为 Python 列表对象内部维护着元素计数字段,取长度只是读这个字段、无需逐个数元素。sorted()是内置函数,接收任意可迭代对象、返回排序后的新列表、原对象不动;list.sort()是列表方法,原地排序、返回None——需要保留原序用前者,只求排好序的结果用后者。两个长度不等的列表交给zip()会按较短的一方截断(“木桶原理”),多出的元素被静默丢弃,配对前应先确认长度一致。回扣主线章:这些内置函数的系统讲解见第 章节 4 章。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
profit = [205.39, 130.36, 98.85, 87.69] print(len(profit)) print(round(sum(profit) / len(profit), 2)) print(max(profit), min(profit)) print(list(enumerate(['中信证券', '华泰证券'], start=1))) print(list(zip([1, 2, 3], ['x', 'y']))) print(round(2.5))参考答案(先写下你的预测再点开)
解题思路:
len(profit)输出4;sum(profit)为 522.29,除以 4 得 130.5725,round(..., 2)输出130.57;max、min输出205.39 87.69;enumerate(..., start=1)从 1 起编号,list()展开后输出[(1, '中信证券'), (2, '华泰证券')];zip遇到不等长列表按短的截断,输出[(1, 'x'), (2, 'y')](元素 3 被丢弃);round(2.5)输出2而非 3——Python 采用“银行家舍入”(四舍六入五取偶),恰为 .5 时取偶数,故round(3.5)是 4、round(2.5)是 2。# 验证脚本:原样运行题面代码,并补验round(3.5) profit = [205.39, 130.36, 98.85, 87.69] # 四家券商净利润(亿元) print(len(profit)) # 元素个数 print(round(sum(profit) / len(profit), 2)) # 平均值保留两位小数 print(max(profit), min(profit)) # 最大值与最小值 print(list(enumerate(['中信证券', '华泰证券'], start=1))) # 从1起编号配对 print(list(zip([1, 2, 3], ['x', 'y']))) # 不等长按短的截断 print(round(2.5)) # 银行家舍入:取偶数 print(round(3.5)) # 对照:同样取偶数,得4预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
4 130.57 205.39 87.69 [(1, '中信证券'), (2, '华泰证券')] [(1, 'x'), (2, 'y')] 2 4回扣主线章:
round()的银行家舍入与其余内置函数的讲解见第 章节 4 章。动手验证:用
int(1e7 / (13.76 * 100)) * 100复算平台任务五的整手买入股数;再把int()换成round(),先验证本例中两者结果是否相同,再说明两者在什么情况下(商的小数部分 ≥0.5 时)才会给出不同结果,并用一个自选价格验证round()版本可能出现买超资金的情况。参考答案(点开前请先独立完成)
解题思路:
1e7 / (13.76 × 100) = 7267.44…,int()截断小数得 7267、再乘 100 得 726700 股;round()四舍五入同样得 7267,故本例两者同为 726700 股——差异只在商的小数部分 ≥0.5 时出现(int向下截断、round向上进位)。用自选价格 13.75 元验证:商为 7272.727…,小数部分 ≥0.5,int()版 727200 股、花费 9999000 元未超资金;round()版 727300 股、花费 10000375 元,超出 1000 万元资金 375 元——说明按手取整数应使用int()(向下截断才不会买超)。# 验证脚本:int与round取整手股数对比,以及round买超资金的自选价格演示 fund = 1e7 # 投资资金额(元) N = 100 # 每手100股 price_huatai = 13.76 # 华泰证券股价(元) print('int版股数:', N * int(fund / (price_huatai * N))) # int向下截断商 print('round版股数:', N * round(fund / (price_huatai * N))) # round对商四舍五入 price_demo = 13.75 # 自选价格:商的小数部分≥0.5 lots_demo = fund / (price_demo * N) # 计算商(可买手数) share_int_demo = N * int(lots_demo) # int版整手股数 share_round_demo = N * round(lots_demo) # round版整手股数(多进位一手) print('int版:', share_int_demo, '股,花费', share_int_demo * price_demo, '元') # int版花费 print('round版:', share_round_demo, '股,花费', share_round_demo * price_demo, '元') # round版花费 print('round版是否买超资金:', share_round_demo * price_demo > fund) # 判定是否超过资金预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
int版股数: 726700 round版股数: 726700 int版: 727200 股,花费 9999000.0 元 round版: 727300 股,花费 10000375.0 元 round版是否买超资金: True变式任务(平台任务二、三同型改造):另找一组上市公司的年度净利润数据(不少于6家),用
sum()、round()计算总和与平均值,用max()、min()找出最好与最差的公司,并用sorted()输出升序排名。参考答案(点开前请先独立完成)
解题思路:取六家长三角上市公司 2023 年年报归母净利润(亿元,数据取自本地上市公司财务报表数据:交通银行 927.28、浦发银行 367.02、中国太保 272.57、宁波银行 255.35、海康威视 141.08、上海机场 9.34)。
sum()加总、round(sum/len, 2)求平均;max()、min()配index()定位公司名;升序排名用sorted(zip(name_list, profit_2023))按元组首元素排序后即为按净利润升序的名次对。# 变式程序:六家长三角上市公司2023年年报归母净利润的汇总统计 name_list = ['交通银行', '浦发银行', '中国太保', '宁波银行', '海康威视', '上海机场'] # 公司名单 profit_2023 = [927.28, 367.02, 272.57, 255.35, 141.08, 9.34] # 与名单一一对应的净利润(亿元) print('净利润总和(亿元):', round(sum(profit_2023), 2)) # 总和保留两位小数 print('净利润平均数(亿元):', round(sum(profit_2023) / len(profit_2023), 2)) # 平均值保留两位小数 print('最赚钱的公司:', name_list[profit_2023.index(max(profit_2023))], max(profit_2023)) # 最好业绩 print('最不赚钱的公司:', name_list[profit_2023.index(min(profit_2023))], min(profit_2023)) # 最差业绩 rank_pairs = sorted(zip(name_list, profit_2023), key=lambda pair: pair[1]) # 按净利润升序的(公司, 净利润)对 print('升序排名:', rank_pairs) # 输出升序排名预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
净利润总和(亿元): 1972.64 净利润平均数(亿元): 328.77 最赚钱的公司: 交通银行 927.28 最不赚钱的公司: 上海机场 9.34 升序排名: [('上海机场', 9.34), ('海康威视', 141.08), ('宁波银行', 255.35), ('中国太保', 272.57), ('浦发银行', 367.02), ('交通银行', 927.28)]注意:以上为本题变式的独立代码;列表 45.1 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣主线章:
sum()、max()、min()、sorted()的金融应用写法见第 章节 4 章。