Python基础语法(三):函数

学过 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
返回类型声明 必须(intvoid 等) 不需要
形参类型声明 必须 不需要
函数体边界 { } 缩进
无返回值时 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::pairstd::tuple、结构体,或者引用形参------每种都有一段样板代码。Python 是语言级支持,底层实现是"返回一个元组,然后自动拆包"。

cpp 复制代码
// C++ 的对应写法
std::pair<int, int> getPoint() { return {10, 20}; }
auto [a, b] = getPoint();

4.3 没有 return 时返回 None

Python 函数永远有返回值。没写 return,默认返回 None(相当于 C++ 里没有 returnvoid 函数)。区别是: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++ 里,iffor{ } 内部是一个新的块作用域,块内变量出块即失效:

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

两个硬性要求:

  1. 必须有结束条件,否则无限递归。
  2. 每次递归的实参要逼近结束条件

递归的取舍(和 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++ 开发者来说,真正需要重新建立的直觉只有三条:

  1. 不用写类型,但要接受运行时才报错。
  2. 传参是"对象引用",可变不可变决定了能不能改外部数据。
  3. 作用域只有函数级,global 改全局、if/for 不隔离变量。
相关推荐
从小就看凹凸曼^o^2 小时前
Python基础5 - 字典与集合:(1)字典
开发语言·python
侧耳倾听1112 小时前
java 日志框架简介
java·开发语言
小赵AI手记2 小时前
技术拆解(十七)具身智能:机器人动作生成为何走向Diffusion Policy?
人工智能·笔记·python·机器人
本地化文档3 小时前
poethepoet-docs-l10n
python·github·gitcode·sphinx
baopixiaoz3 小时前
AI量化策略师|Web3 量化交易研究员
大数据·人工智能·python·区块链
zlinear数据采集卡3 小时前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#
全栈技术负责人3 小时前
解密 MCP(Model Context Protocol):大模型时代的“Type-C”总线与三驾马车架构深度解析
开发语言·ai·架构
丰锋ff3 小时前
1.7在Qt窗口中添加右键菜单
开发语言·qt
Titan20243 小时前
Linux网络学习:套接字、UDP的封装与应用
linux·服务器·开发语言·网络·c++
Uncommon.3 小时前
使用Pytorch自动计算梯度
人工智能·pytorch·python