跳到主要内容Python sum 函数用法及源码签名误解解析 | 极客日志Python
Python sum 函数用法及源码签名误解解析
综述由AI生成Python sum 函数的正确用法,指出其需要可迭代对象而非不定长位置参数。解释了 start 参数的应用场景及类型一致性要求。针对源码显示的不定长参数与实际不支持之间的矛盾,揭示了 CPython 内置函数的伪签名机制,建议通过官方文档或 help() 验证真实签名。
清心27 浏览 Python sum 函数用法及源码签名误解解析
1. sum() 函数的真实签名
sum(iterable, start=0)
- 第一个参数必须是可迭代对象(列表、元组、集合等)
- 第二个参数是可选的起始累加值(默认为 0)
2. 为什么 sum(1, 2) 会报错?
print(sum(1, 2))
- 1 不是可迭代对象(不是列表/元组等)
- 因此报错:TypeError: 'int' object is not iterable
3. 正确的使用方式
print(sum([1, 2, 3, 4]))
print(sum((1, 2, 3, 4)))
print(sum({1, 2, 3, 4}))
print(sum([1, 2, 3], 10))
print(sum([], 100))
4. 对比 print() 的不定长参数
print(1, 2, 3, 4)
sum([1, 2, 3, 4])
sum(1, 2, 3, 4)
5. 如果您想要"直接求和多个数字"的语法
有以下几种替代方案:
方案 A:使用 * 解包
numbers = 1, 2, 3, 4
print(sum(numbers))
print(sum((1, 2, 3, 4)))
方案 B:自定义函数
def multi_sum(*args):
"""模拟多个数字直接求和"""
return sum(args)
print(multi_sum(1, 2, 3, 4))
print(multi_sum(1, 2))
方案 C:使用 math.fsum(浮点数专用)
import math
print(math.fsum([1.1, 2.2, 3.3]))
6. 设计哲学解释
| 函数 | 参数模式 | 示例 |
|---|
| print() | 不定长位置参数 | print(1, 2, 3) |
| sum() | 单可迭代对象 + 可选参数 | sum([1, 2, 3]) |
| max()/min() | 两者都支持 | max(1, 2, 3) 或 max([1, 2, 3]) |
- 性能考虑:直接处理可迭代对象更高效
- 一致性:与 map()、filter() 等函数风格一致
- 明确性:强制显式创建集合,避免意外行为
7. 记忆技巧
sum(1, 2, 3)
sum([1, 2, 3])
sum({1, 2, 3})
sum((1, 2, 3))
sum([1, 2, 3], 10)
所以您的观察是对的——sum() 的参数设计与 print() 不同,这是 Python 的刻意设计选择。
sum 中的 start 怎么用
sum() 函数中的 start 参数是起始累加值,默认值为 0。它的作用是让求和从某个初始值开始,而不是从 0 开始。
1. 基本语法
2. 基础示例
numbers = [1, 2, 3, 4]
print(sum(numbers))
print(sum(numbers, 0))
print(sum(numbers, 10))
print(sum(numbers, -5))
3. 实际应用场景
场景 1:累加偏移量
scores = [85, 90, 78]
total = sum(scores, 100)
print(f"总分:{total}")
场景 2:处理空列表
empty_list = []
print(sum(empty_list))
print(sum(empty_list, 100))
initial_value = 1000
daily_earnings = []
balance = sum(daily_earnings, initial_value)
print(f"余额:{balance}")
场景 3:非数字类型求和
words = ['Hello', ' ', 'World', '!']
result = sum(words, '')
print(result)
list1 = [1, 2]
list2 = [3, 4]
list3 = [5, 6]
combined = sum([list2, list3], list1)
print(combined)
场景 4:自定义起始计算
prices = [25.5, 18.0, 32.0]
shipping_fee = 5.99
total_cost = sum(prices, shipping_fee)
print(f"总价:${total_cost:.2f}")
from functools import reduce
numbers = [2, 3, 4]
product = reduce(lambda x, y: x * y, numbers, 1)
print(product)
4. 注意事项
类型一致性
numbers = [1, 2, 3]
print(sum([1, 2, 3], 10))
print(sum(["a", "b"], ""))
print(sum([[1], [2]], []))
性能考虑
large_list = list(range(1000000))
result1 = sum(large_list) + 1000
result2 = sum(large_list, 1000)
print(result1 == result2)
5. 与 + 运算符的对比
numbers = [1, 2, 3]
start = 10
result1 = sum(numbers, start)
result2 = start
for num in numbers:
result2 += num
from functools import reduce
result3 = reduce(lambda x, y: x + y, numbers, start)
print(result1, result2, result3)
6. 特殊用法示例
示例 1:计算加权和
weights = [0.3, 0.5, 0.2]
scores = [85, 90, 78]
base_score = 20
weighted_sum = sum(w * s for w, s in zip(weights, scores))
total = weighted_sum + base_score
print(f"最终得分:{total:.1f}")
示例 2:链式操作
step1 = [10, 20, 30]
step2 = [5, 15]
step3 = [2, 3, 4]
total = 100
total = sum(step1, total)
total = sum(step2, total)
total = sum(step3, total)
print(f"最终结果:{total}")
7. 总结要点
- 默认值:start 默认为 0,可省略
- 空可迭代对象:返回 start 值本身
- 类型要求:start 必须与可迭代元素类型兼容(支持 + 操作)
- 常见用途:
- 设置初始累加值
- 处理空列表返回默认值
- 非数字类型拼接(字符串、列表等)
- 包含固定偏移量的计算
sum([a, b, c], start) = start + a + b + c
sum 怎样传不定长关键字参数
sum() 函数本身不支持不定长关键字参数(**kwargs),它的签名是固定的:
但可以通过以下几种方式实现类似功能:
1. 传递字典值给 sum()
def sum_kwargs(**kwargs):
"""对关键字参数的值求和"""
return sum(kwargs.values())
print(sum_kwargs(a=1, b=2, c=3))
print(sum_kwargs(x=10, y=20, z=30))
def sum_kwargs_with_start(start=0, **kwargs):
return sum(kwargs.values(), start)
print(sum_kwargs_with_start(100, a=1, b=2))
2. 使用字典直接作为参数
data = {'math': 85, 'english': 90, 'science': 78}
total = sum(data.values())
print(f"总分:{total}")
selected_keys = ['math', 'english']
selected_sum = sum(data[key] for key in selected_keys)
print(f"选科总分:{selected_sum}")
3. 自定义包装函数
def flexible_sum(*args, **kwargs):
"""灵活的求和函数,支持多种参数形式
用法:
- flexible_sum(1, 2, 3) # 位置参数
- flexible_sum(a=1, b=2, c=3) # 关键字参数
- flexible_sum([1, 2, 3]) # 可迭代对象
- flexible_sum([1, 2, 3], start=10) # 带起始值
"""
start = kwargs.pop('start', 0)
if args and isinstance(args[0], (list, tuple, set)):
return sum(args[0], start)
elif kwargs:
return sum(kwargs.values(), start)
else:
return sum(args, start)
print(flexible_sum(1, 2, 3))
print(flexible_sum(a=1, b=2, c=3))
print(flexible_sum([1, 2, 3]))
print(flexible_sum([1, 2, 3], start=10))
print(flexible_sum(a=1, b=2, start=100))
4. 使用 locals() 动态获取变量
def sum_local_variables():
"""对局部变量求和(需要变量名规范)"""
math_score = 85
english_score = 90
science_score = 78
local_vars = locals()
score_vars = {k: v for k, v in local_vars.items() if k.endswith('_score')}
return sum(score_vars.values())
print(sum_local_variables())
5. 实际应用场景
config = {'base_salary': 5000, 'bonus': 1000, 'overtime': 500, 'allowance': 300}
def sum_config_items(**items):
return sum(items.values())
total_income = sum_config_items(**{k: config[k] for k in ['base_salary', 'bonus']})
print(f"基本收入:{total_income}")
def calculate_total(**expenses):
"""计算各项支出总和"""
print("支出明细:")
for category, amount in expenses.items():
print(f" {category}: ${amount}")
total = sum(expenses.values())
print(f"总支出:${total}")
return total
calculate_total(food=200, transport=50, entertainment=100, utilities=150)
6. 与 *args 结合使用
def smart_sum(*args, **kwargs):
"""智能求和:支持混合参数
参数优先级:
1. 如果提供 iterable 参数,使用它
2. 否则使用 args 或 kwargs
"""
start = kwargs.pop('start', 0)
if 'iterable' in kwargs:
return sum(kwargs['iterable'], start)
elif args and hasattr(args[0], '__iter__'):
return sum(args[0], start)
elif kwargs:
return sum(kwargs.values(), start)
else:
return sum(args, start)
print(smart_sum(1, 2, 3))
print(smart_sum(iterable=[1, 2, 3]))
print(smart_sum(a=1, b=2, c=3))
print(smart_sum([1, 2, 3], start=10))
7. 为什么 sum() 本身不支持 **kwargs
- 设计目的单一:sum() 专为可迭代对象设计
- 性能考虑:保持简单高效
- 明确性:强制开发者显式处理数据结构
- 一致性:与 map(), filter(), reduce() 等函数风格一致
8. 最佳实践建议
def sum_keyword_args(**kwargs):
"""明确将关键字参数转换为可迭代对象"""
return sum(kwargs.values())
def sum_dict_values(data: Dict[str, int]) -> int:
"""明确要求字典输入"""
return sum(data.values())
def calculate_sum(*, start: int = 0, **values) -> int:
"""使用关键字专用参数语法 * 强制使用关键字参数"""
return sum(values.values(), start)
result = calculate_sum(start=100, math=85, english=90)
print(result)
总结:
虽然 sum() 本身不支持 **kwargs,但可以通过包装函数轻松实现。关键是理解 sum() 需要的是可迭代对象,而 **kwargs 本质上是一个字典,通过 .values() 即可获取可迭代的值序列。
源码中有不定长关键字参数,为什么实际上不支持呢
1. 您看到的是'伪签名'
您引用的 def sum(*args, **kwargs): 实际上是 CPython 实现中的 Python 包装层签名,不是真正的函数签名。
- sum() 是用 C 语言实现的(builtin_sum 函数)
- Python 层面显示的这个签名只是一个占位符
- 实际参数处理在 C 代码中进行验证
2. 验证真实签名
help(sum)
"""输出:sum(iterable, /, start=0) Return the sum of a 'start' value (default: 0) plus an iterable of numbers ..."""
import inspect
try:
print(inspect.signature(sum))
except Exception as e:
print(f"无法获取内置函数签名:{e}")
print(sum([1, 2, 3]))
print(sum([1, 2, 3], 10))
print(sum(1, 2, 3))
3. 为什么会有这种'伪签名'?
在 CPython 源码中(Python/bltinmodule.c):
static PyObject *builtin_sum(PyObject *self, PyObject *const*args, Py_ssize_t nargs, PyObject *kwnames){
PyObject *start = NULL;
PyObject *iterable;
if (!_PyArg_UnpackStack(args, nargs, "sum", 1, 2, &iterable, &start)){
return NULL;
}
}
PyDoc_STRVAR(sum_doc, "sum(iterable, /, start=0)\n\
Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n\
...");
static PyMethodDef builtin_methods[]={{"sum", (PyCFunction)builtin_sum, METH_FASTCALL, sum_doc},
4. 其他内置函数的类似情况
def len(*args, **kwargs):
pass
help(len)
"""len(obj, /) Return the number of items in a container."""
print(len([1, 2, 3]))
print(len([1, 2, 3], 10))
5. 如何正确理解内置函数签名
方法 1:查看官方文档
方法 2:使用类型提示(Python 3.5+)
from typing import Iterable, Union
def sum(
iterable: Iterable[Union[int, float]],
start: Union[int, float] = 0
) -> Union[int, float]:
...
方法 3:查看 text_signature
print(sum.__text_signature__)
6. 实际参数数量验证
import sys
def test_function_args(func):
"""测试函数接受的参数数量"""
try:
func(1, 2, 3, 4, 5)
return "接受多个参数"
except TypeError as e:
error_msg = str(e)
if "takes at most" in error_msg:
import re
match = re.search(r'takes at most (\d+)', error_msg)
if match:
return f"最多接受 {match.group(1)} 个参数"
return f"参数错误:{error_msg}"
print(test_function_args(sum))
print(test_function_args(print))
7. 为什么会显示 args, kwargs?
- 历史原因:早期 Python 版本的内置函数文档不完善
- 实现简化:C 函数通过 METH_FASTCALL 等标志接收参数
- 工具限制:某些 IDE 和文档生成工具无法解析 C 函数的准确签名
- 向后兼容:保持伪签名避免破坏现有工具
8. 正确获取函数信息的方法
help(sum)
print(sum.__doc__[:200])
import IPython
import sys
print(f"sum 模块:{sum.__module__}")
print(f"sum 类型:{type(sum)}")
9. 总结
- 不要依赖伪签名:
def sum(*args, **kwargs) 是误导性的
- 真实签名:
sum(iterable, start=0)
- 验证方法:使用 help()、官方文档、实际测试
- 设计原则:Python 内置函数通常有明确的参数要求,不是所有都支持 *args/**kwargs
记住:实践是检验真理的唯一标准。当文档和实际行为不一致时,以实际运行结果为准。
相关免费在线工具
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online