云计算百科
云计算领域专业知识百科平台

第五章|函数(基础篇):写一次,用一辈子


📌 引言:不要重复发明轮子

还记得写九九乘法表的时候吗?你得把循环嵌套写一遍。要是哪天想改个格式,得把所有代码翻出来改。这就像每天做同样的饭,每次都要从头切菜——太累了!

函数就是编程界的“预制菜”。你把一段代码打包起来,起个名字,以后想用就“调用”一下。改了函数体,所有用到的地方都自动更新。这就是传说中的 DRY 原则(Don‘t Repeat Yourself)。

本章我们学习函数的方方面面,从定义到调用,从参数到作用域,从递归到 lambda。掌握函数,你的代码将脱胎换骨。


一、定义与调用:Hello Function!

1️⃣ 最简单的函数

def greet():
print("你好,世界!")

greet() # 调用函数

  • def 关键字声明函数
  • 函数名后跟圆括号和冒号
  • 函数体缩进
  • 调用时写函数名加括号

2️⃣ 带参数和返回值

def add(a, b):
result = a + b
return result

sum = add(3, 5)
print(sum) # 8

  • 参数放在括号里,多个参数用逗号分隔
  • return 返回结果,函数执行结束
  • 如果没有 return,函数默认返回 None

3️⃣ 多个返回值(其实是元组)

def get_min_max(numbers):
return min(numbers), max(numbers)

result = get_min_max([1, 5, 3, 9, 2])
print(result) # (1, 9)
min_val, max_val = get_min_max([1, 5, 3, 9, 2]) # 解包


🧠 冷知识:函数是一等公民

在 Python 里,函数也是对象,可以赋值给变量、存在列表里、作为参数传递:

def say_hello():
print("Hello")

func = say_hello
func() # Hello


二、参数类型:Python 的花式传参

Python 的参数系统非常灵活,支持多种传递方式。


1️⃣ 位置参数(Positional Arguments)

最简单的形式,调用时按顺序赋值:

def describe_pet(name, species):
print(f"{name} 是一只 {species}")

describe_pet("旺财", "狗")


2️⃣ 默认参数(Default Arguments)

给参数指定默认值,调用时可以省略:

def describe_pet(name, species="狗"):
print(f"{name} 是一只 {species}")

describe_pet("旺财") # 旺财 是一只 狗
describe_pet("咪咪", "猫") # 咪咪 是一只 猫

⚠️ 重要陷阱:默认参数在函数定义时计算一次,如果默认值是可变对象(如列表、字典),多次调用会共享同一个对象!

def add_item(item, lst=[]): # 不要这样写!
lst.append(item)
return lst

print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] → 咦?不是应该 [2] 吗?

正确做法:默认参数设为 None,内部再创建新对象。

def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst


3️⃣ 可变位置参数(*args)

当你不知道调用时会传入多少个位置参数时,用 *args。它会将多余的位置参数打包成元组。

def sum_all(*numbers):
total = 0
for n in numbers:
total += n
return total

print(sum_all(1, 2, 3)) # 6
print(sum_all(10, 20)) # 30

名字不一定要叫 args,但星号 * 是必须的,惯例用 *args。


4️⃣ 可变关键字参数(**kwargs)

处理任意数量的关键字参数,打包成字典。

def print_info(**info):
for key, value in info.items():
print(f"{key}: {value}")

print_info(name="Alice", age=25, city="北京")
# 输出:
# name: Alice
# age: 25
# city: 北京

惯例用 **kwargs。


5️⃣ 组合参数顺序

如果同时使用多种参数,顺序必须固定:

位置参数 → *args → 默认参数 → **kwargs

def func(a, b, *args, c=10, **kwargs):
print(a, b, args, c, kwargs)

func(1, 2, 3, 4, 5, c=20, name="Alice")
# 1 2 (3, 4, 5) 20 {'name': 'Alice'}


6️⃣ 仅位置参数(Python 3.8+)

使用 / 分隔,/ 之前的参数只能按位置传递,不能作为关键字参数。

def power(x, y, /):
return x ** y

print(power(2, 3)) # 8
# print(power(x=2, y=3)) # 报错


7️⃣ 仅关键字参数(Keyword-Only Arguments)

使用 * 分隔,* 之后的参数必须用关键字传递。

def greet(name, *, greeting="你好"):
return f"{greeting}{name}"

print(greet("Alice")) # 你好,Alice
print(greet("Bob", greeting="Hello")) # Hello,Bob
# print(greet("Bob", "Hello")) # 报错


8️⃣ 解包参数调用(Unpacking Arguments)

调用函数时,可以用 * 解包序列,** 解包字典,将值传递给参数。

def add(a, b, c):
return a + b + c

nums = [1, 2, 3]
print(add(*nums)) # 6

params = {"a": 10, "b": 20, "c": 30}
print(add(**params)) # 60


三、作用域:变量的“地盘”

1️⃣ LEGB 规则

Python 查找变量时按照 LEGB 顺序:

  • Local:函数内部局部作用域
  • Enclosing:外层嵌套函数(闭包时用到)
  • Global:全局作用域(模块级别)
  • Built-in:内置作用域(如 print、len)

x = 10 # 全局

def outer():
y = 20 # 外层函数的局部
def inner():
z = 30 # 内层局部
print(x, y, z) # 都可以访问
inner()

outer()


2️⃣ global 声明

在函数内部修改全局变量,需要用 global 声明。

count = 0

def increment():
global count
count += 1

increment()
print(count) # 1

如果没有 global,函数内部的 count 会被当作局部变量,导致 UnboundLocalError。


3️⃣ nonlocal 声明

用于嵌套函数中修改外层函数的局部变量。

def outer():
x = 10
def inner():
nonlocal x
x += 5
print(x)
inner()
print(x) # 15

outer()


🧠 冷知识:Python 没有块级作用域

在 if、for、while 内部定义的变量,在外部依然可以访问(除非函数内部)。

if True:
msg = "Hello"
print(msg) # Hello

所以不要在循环内部滥用变量,以免意外覆盖。


四、递归函数:自己调用自己

递归就是函数调用自身。必须有两个要素:

  • 基线条件(停止递归的条件)
  • 递归条件(调用自身)

示例:阶乘

def factorial(n):
if n <= 1: # 基线条件
return 1
return n * factorial(n 1) # 递归条件

print(factorial(5)) # 120

示例:斐波那契数列

def fib(n):
if n <= 1:
return n
return fib(n1) + fib(n2)

print(fib(10)) # 55

⚠️ 注意:Python 默认递归深度限制约 1000,可以用 sys.setrecursionlimit(2000) 调整,但不建议写太深的递归,容易栈溢出。


五、文档字符串(docstring):给自己留个便签

函数定义的第一行字符串(用三引号)就是文档字符串,用来描述函数功能、参数、返回值。

def add(a, b):
"""返回两个数的和"""
return a + b

print(add.__doc__) # 返回两个数的和

可以用 help(add) 查看文档。PEP 257 规定了文档字符串的格式。


六、匿名函数 lambda:轻量级小工具

当需要一个简单函数,又不想用 def 定义时,可以用 lambda。

square = lambda x: x ** 2
print(square(5)) # 25

语法:lambda 参数: 表达式,只能写一行,自动返回表达式结果。

通常和高阶函数一起用。


七、高阶函数简介(函数式编程启蒙)

高阶函数就是把函数作为参数或返回值的函数。


1️⃣ map():批量处理

map(func, iterable) 将函数作用于可迭代对象的每个元素,返回迭代器。

numbers = [1, 2, 3, 4]
squared = map(lambda x: x**2, numbers)
print(list(squared)) # [1, 4, 9, 16]


2️⃣ filter():筛选

filter(func, iterable) 返回使函数返回 True 的元素。

numbers = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens)) # [2, 4, 6]


3️⃣ sorted() 的 key 参数

sorted(iterable, key=func) 按照 key 函数的返回值排序。

students = [
{"name": "Alice", "grade": 88},
{"name": "Bob", "grade": 95},
{"name": "Charlie", "grade": 72},
]
sorted_by_grade = sorted(students, key=lambda s: s["grade"])
print(sorted_by_grade) # 按成绩升序


4️⃣ reduce()(了解即可)

functools.reduce(func, iterable) 将函数累积应用到元素上,返回单个值。

from functools import reduce
numbers = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 24

Python 3 中 reduce 移到了 functools,日常用得不多,但面试偶尔会问。


八、函数进阶预告

本章的函数基础篇没有涉及:

  • 装饰器(动态增强函数)
  • 生成器函数(yield)
  • 闭包

这些将在第10章 高级特性 中展开。


✅ 本章总结

概念说明
定义函数 def + 函数名 + 参数 + 冒号 + 缩进体
返回值 return,无 return 返回 None
位置参数 按顺序赋值
默认参数 param=value,注意可变默认值的坑
可变位置参数 *args 收集多余位置参数为元组
可变关键字参数 **kwargs 收集多余关键字参数为字典
仅位置参数 / 之前参数不能作关键字
仅关键字参数 * 之后参数必须用关键字
解包参数 调用时 *seq **dict
作用域 LEGB 规则
global 修改全局变量
nonlocal 修改外层嵌套变量
递归 自己调用自己,有基线条件
文档字符串 三引号写在函数开头
lambda 匿名函数,一行表达式
高阶函数 map、filter、sorted key、reduce

函数是模块化编程的基石。现在你可以把复杂任务拆解成一个个小函数,像搭积木一样构建程序。


🚀 下集预告

下一章我们将学习 模块与包——如何把函数组织成文件,如何导入别人写好的轮子,以及 Python 标准库的冰山一角。从此你的代码可以站在巨人的肩膀上!

记得多练习:写几个函数练习参数传递,试试递归解决汉诺塔问题,用 lambda 和 map/filter 处理数据。函数写多了,你会爱上这种“写一次,用一辈子”的感觉!✨


🔗 上一篇:流程控制:让程序学会思考与循环 🔗 下一篇:模块与包:站在巨人的肩膀上(待更新)


赞(0)
未经允许不得转载:网硕互联帮助中心 » 第五章|函数(基础篇):写一次,用一辈子
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!