欢迎光临
我们一直在努力

Python基础语法(三):函数

学过 C++ 再看 Python 函数,最大的感受就是一个字——"松":不用写返回类型、参数随便传、返回值一次能给好几个。本文直接从 C++ 视角出发,把 Python 函数里"和 C++ 一样"的地方一笔带过,重点展开"和 C++ 不一样"的地方。


一、函数是什么

和 C++ 一样,函数就是一段可以被重复使用的代码片段。

区别在于动机:C++ 里函数是程序组织的基石(main 入口、模块化、复用),Python 里同样如此。真正值得关注的是语法和设计哲学上的差异,后面逐点展开。

二、定义与调用:先看一段对比

# Python
def calcSum(beg, end):
total = 0
for i in range(beg, end + 1):
total += i
return total

result = calcSum(1, 100)

// 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 是动态类型语言,同一个形参可以接收不同类型的实参:

def test(a):
print(a)

test(10) # 整数
test('hello') # 字符串
test(True) # 布尔

这在 C++ 中只能用模板或 std::variant 之类的手段勉强实现。Python 直接放开,代价是没有编译期类型检查,类型错误要等运行时才暴露。

3.2 形参、实参的对应关系

形参是"占位符",实参是"具体值"。调用时逐个对应,个数必须匹配:

def test(a, b, c):
print(a, b, c)

test(10) # 报错:缺少实参

3.3 默认参数

Python 和 C++ 都有默认参数,且规则一致:带默认值的参数必须放在不带默认值的参数后面。

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

int add(int x, int y, bool debug = false) {
if (debug) std::cout << "debug: " << x << ", " << y << "\\n";
return x + y;
}

3.4 关键字参数(C++ 没有)

默认情况下实参按位置依次传给形参。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 等):函数内部修改它,外部会跟着变。

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,函数立刻回到调用处,后面代码不执行。

def isOdd(num):
if num % 2 == 0:
return False
return True # 只有 num 为奇数才会执行到这里

4.2 多返回值:C++ 里最羡慕的特性

Python 可以用逗号一次返回多个值:

def getPoint():
x = 10
y = 20
return x, y

a, b = getPoint() # 同时拿到两个值 多元赋值
print(a, b)

不想接收的返回值用 _ 忽略:

_, b = getPoint() # 只关心第二个值

C++ 要返回多个值,通常用 std::pair、std::tuple、结构体,或者引用形参——每种都有一段样板代码。Python 是语言级支持,底层实现是"返回一个元组,然后自动拆包"。

// 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 局部变量与全局变量

函数内部定义的变量是局部变量,只在函数内生效;函数外部的变量是全局变量。同名变量在不同作用域中互不干扰:

x = 20

def test():
x = 10
print(f'函数内部 x = {x}') # 10

test()
print(f'函数外部 x = {x}') # 20

函数内部读变量时,局部找不到会自动去全局作用域找。

5.2 global 关键字(C++ 没有对应的东西)

想在函数内部修改全局变量,必须先用 global 声明:

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 的 { } 内部是一个新的块作用域,块内变量出块即失效:

for (int i = 0; i < 10; i++) { }
// 此处 i 已不可见

Python 里,只有函数(和类)才产生作用域。if / for / while 中定义的变量,外面照常能用:

for i in range(1, 10):
pass

print(i) # 9,循环结束后 i 依然存在

对写惯 C++ 的人来说这一点要特别警惕:Python 的循环变量会"泄漏"到函数作用域,后面的代码可能不小心读到循环残留值。

六、函数的执行与调用栈

  • 调用函数时,程序跳到函数体执行;执行完或遇到 return,回到调用位置继续。
  • 函数内部可以调用其他函数,称为嵌套调用,层数不限。
  • 把函数返回值直接作为另一个函数的参数,称为链式调用:

x = 20

def test():
x = 10
print(f'函数内部 x = {x}') # 10

test()
print(f'函数外部 x = {x}') # 20

Python 用函数调用栈记录每一层调用,每层对应一个栈帧,函数内部的局部变量就存在各自的栈帧里。这个模型和 C++ 完全一致,PyCharm 调试器可以直观看到。

七、递归

递归是嵌套调用自己。代码层面和 C++ 几乎一样,注意事项也相同:

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 中,函数本身也是一个对象,可以赋值给变量、放进列表、作为参数传递:

    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 不隔离变量。
  • 赞(0)
    未经允许不得转载:171主机测评 » Python基础语法(三):函数
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址