
学过 C++ 再看 Python 函数,最大的感受就是一个字------"松":不用写返回类型、参数随便传、返回值一次能给好几个。本文直接从 C++ 视角出发,把 Python 函数里"和 C++ 一样"的地方一笔带过,重点展开"和 C++ 不一样"的地方。
一、函数是什么
和 C++ 一样,函数就是一段可以被重复使用的代码片段。
区别在于动机:C++ 里函数是程序组织的基石(main 入口、模块化、复用),Python 里同样如此。真正值得关注的是语法和设计哲学上的差异,后面逐点展开。
二、定义与调用:先看一段对比
python
# Python
def calcSum(beg, end):
total = 0
for i in range(beg, end + 1):
total += i
return total
result = calcSum(1, 100)
cpp
// C++
int calcSum(int beg, int end) {
int total = 0;
for (int i = beg; i <= end; i++)
total += i;
return total;
}
int result = calcSum(1, 100);
| 对比点 | C++ | Python |
|---|---|---|
| 定义关键字 | 无(直接写返回类型) | def |
| 返回类型声明 | 必须(int、void 等) |
不需要 |
| 形参类型声明 | 必须 | 不需要 |
| 函数体边界 | { } |
缩进 |
| 无返回值时 | void + return; |
省略 return,返回 None |
两个共同点,快速确认:
- 先定义,再使用。定义只是"登记",函数体不会执行,必须调用才执行。
- 调用几次,执行几次。
三、参数:差异最集中的区域
3.1 形参不需要类型声明
Python 是动态类型语言,同一个形参可以接收不同类型的实参:
python
def test(a):
print(a)
test(10) # 整数
test('hello') # 字符串
test(True) # 布尔
这在 C++ 中只能用模板或 std::variant 之类的手段勉强实现。Python 直接放开,代价是没有编译期类型检查,类型错误要等运行时才暴露。
3.2 形参、实参的对应关系
形参是"占位符",实参是"具体值"。调用时逐个对应,个数必须匹配:
python
def test(a, b, c):
print(a, b, c)
test(10) # 报错:缺少实参
3.3 默认参数
Python 和 C++ 都有默认参数,且规则一致:带默认值的参数必须放在不带默认值的参数后面。
python
def add(x, y, debug=False):
if debug:
print(f'调试信息: x={x}, y={y}')
return x + y
print(add(10, 20)) # 不传第三个参数,debug=False
print(add(10, 20, True)) # 显式传 True
def add(a,b=1):
return a+b
print(add(1)) #2
cpp
int add(int x, int y, bool debug = false) {
if (debug) std::cout << "debug: " << x << ", " << y << "\n";
return x + y;
}
3.4 关键字参数(C++ 没有)
默认情况下实参按位置依次传给形参。Python 额外允许按形参名传参,顺序随意:
python
def test(x, y):
print(x, y)
test(x=10, y=20)
test(y=100, x=200) # 顺序反过来也行
C++ 要实现类似效果,只能定义多个重载函数,或用结构体把参数打包。Python 的关键字参数让"参数很多时调用意图清晰"这件事变得零成本。
3.5 传参的本质:对象引用传递
C++ 的默认传参是值拷贝,想改原值得用引用/指针。Python 的传参规则要换一个角度看:
传进去的是"对象引用",但对象分可变与不可变。
- 传入不可变对象(int、str、bool、tuple):函数内部重新赋值,不影响外部变量。
- 传入可变对象 (list、dict 等):函数内部修改它,外部会跟着变。
python
def modify(x, lst):
x = 100 # 只改局部变量 x,外部不变
lst.append(4) # 修改了同一个 list 对象,外部跟着变
num = 1
a = [1, 2, 3]
modify(num, a)
print(num) # 1
print(a) # [1, 2, 3, 4]
这与 C++ 的直觉差异很大,也是写 Python 最常见的坑之一。要不要在函数里改外部数据,取决于你传的是不是可变对象,而不是形参有没有写 &。
四、返回值
4.1 return 立即结束函数
和 C++ 一致:执行到 return,函数立刻回到调用处,后面代码不执行。
python
def isOdd(num):
if num % 2 == 0:
return False
return True # 只有 num 为奇数才会执行到这里
4.2 多返回值:C++ 里最羡慕的特性
Python 可以用逗号一次返回多个值:
python
def getPoint():
x = 10
y = 20
return x, y
a, b = getPoint() # 同时拿到两个值 多元赋值
print(a, b)
不想接收的返回值用 _ 忽略:
python
_, b = getPoint() # 只关心第二个值
C++ 要返回多个值,通常用 std::pair、std::tuple、结构体,或者引用形参------每种都有一段样板代码。Python 是语言级支持,底层实现是"返回一个元组,然后自动拆包"。
cpp
// C++ 的对应写法
std::pair<int, int> getPoint() { return {10, 20}; }
auto [a, b] = getPoint();
4.3 没有 return 时返回 None
Python 函数永远有返回值。没写 return,默认返回 None(相当于 C++ 里没有 return 的 void 函数)。区别是:None 是一个真实的值,可以赋值、可以比较,只是表示"什么都没有"。
五、变量作用域
5.1 局部变量与全局变量
函数内部定义的变量是局部变量 ,只在函数内生效;函数外部的变量是全局变量。同名变量在不同作用域中互不干扰:
python
x = 20
def test():
x = 10
print(f'函数内部 x = {x}') # 10
test()
print(f'函数外部 x = {x}') # 20
函数内部读变量时,局部找不到会自动去全局作用域找。
5.2 global 关键字(C++ 没有对应的东西)
想在函数内部修改 全局变量,必须先用 global 声明:
python
x = 20
def test():
global x
x = 10
test()
print(x) # 10,全局变量被修改了
如果不写 global,函数里的 x = 10 会被当成新建一个局部变量 ,和全局 x 毫无关系。这是 Python 的经典陷阱:读全局不用声明,改全局必须声明。
C++ 没有这个机制------全局变量可以直接在函数里改。Python 这样做是为了强制"明确",避免无意识地污染全局状态。
5.3 if / for / while 不产生新作用域(与 C++ 最大差异之一)
C++ 里,if、for 的 { } 内部是一个新的块作用域,块内变量出块即失效:
cpp
for (int i = 0; i < 10; i++) { }
// 此处 i 已不可见
Python 里,只有函数(和类)才产生作用域 。if / for / while 中定义的变量,外面照常能用:
python
for i in range(1, 10):
pass
print(i) # 9,循环结束后 i 依然存在
对写惯 C++ 的人来说这一点要特别警惕:Python 的循环变量会"泄漏"到函数作用域,后面的代码可能不小心读到循环残留值。
六、函数的执行与调用栈
- 调用函数时,程序跳到函数体执行;执行完或遇到
return,回到调用位置继续。 - 函数内部可以调用其他函数,称为嵌套调用,层数不限。
- 把函数返回值直接作为另一个函数的参数,称为链式调用:
python
x = 20
def test():
x = 10
print(f'函数内部 x = {x}') # 10
test()
print(f'函数外部 x = {x}') # 20

Python 用函数调用栈 记录每一层调用,每层对应一个栈帧,函数内部的局部变量就存在各自的栈帧里。这个模型和 C++ 完全一致,PyCharm 调试器可以直观看到。
七、递归
递归是嵌套调用自己。代码层面和 C++ 几乎一样,注意事项也相同:
python
def factor(n):
if n == 1:
return 1
return n * factor(n - 1)
print(factor(5)) # 120
两个硬性要求:
- 必须有结束条件,否则无限递归。
- 每次递归的实参要逼近结束条件。
递归的取舍(和 C++ 相同):
- 优点:代码量极少,符合数学归纳法的直觉。
- 缺点:难理解、容易栈溢出、性能通常不如循环。
一个 Python 特有的点:Python 对递归深度有硬性限制 (默认约 1000 层,sys.getrecursionlimit() 可查),超过直接抛 RecursionError。C++ 没有这个显式限制,但栈空间同样有限,本质风险一致。所以实际开发中递归要慎重。
八、一点扩展:函数是一等公民
这是 Python 函数和 C++ 函数最大的哲学差异,PDF 里没细讲,但对理解 Python 非常重要:
在 Python 中,函数本身也是一个对象,可以赋值给变量、放进列表、作为参数传递:
python
def add(x, y):
return x + y
def mul(x, y):
return x * y
# 把函数当值用
op = add
print(op(1, 2)) # 3
# 函数作为参数传入
def calc(op, a, b):
return op(a, b)
print(calc(mul, 3, 4)) # 12
C++ 里要这样做需要函数指针或 std::function。Python 把这个能力做成语言默认行为,这也是 Python 中"高阶函数"、"装饰器"等高级玩法的基石,后续写代码时会大量遇到。
九、小结
| 主题 | C++ | Python |
|---|---|---|
| 类型声明 | 必须(静态类型) | 不需要(动态类型) |
| 默认参数 | 有,规则相同 | 有,规则相同 |
| 关键字参数 | 无 | 有 |
| 多返回值 | tuple / 结构体 | 语言级支持 |
| 修改全局变量 | 直接改 | 需 global 声明 |
| 块级作用域 | {} 产生新作用域 |
if/for/while 不产生 |
| 传参语义 | 值/引用 | 对象引用(看可变性) |
| 递归限制 | 栈空间限制 | 额外有深度硬限制 |
| 函数地位 | 对象,可作值传递 | 一等公民,天然可作值传递 |
核心就三件事,与 PDF 课程总结一致:函数的定义、调用、参数传递。对 C++ 开发者来说,真正需要重新建立的直觉只有三条:
- 不用写类型,但要接受运行时才报错。
- 传参是"对象引用",可变不可变决定了能不能改外部数据。
- 作用域只有函数级,
global改全局、if/for 不隔离变量。