Python 函数
函数是组织好的、可重复使用的代码段,用来实现单一或相关联的功能。
一、定义函数
使用 def 关键字定义函数:
def 函数名(参数列表):
"""文档字符串(可选)"""
函数体
return 表达式
def hello():
print("Hello World!")
hello() # 调用函数 → Hello World!
def max_num(a, b):
if a > b:
return a
else:
return b
print(max_num(4, 5)) # 5
return 语句用于退出函数并返回一个值给调用方。不带 return 或 return 后没有表达式时,返回 None。
二、参数类型
2.1 必需参数
必须按正确的顺序和数量传入:
def printme(str):
print(str)
printme("Hello") # ✅
# printme() # ❌ TypeError: missing 1 required positional argument
2.2 关键字参数
调用时通过参数名指定值,顺序可以和定义时不一致:
def printinfo(name, age):
print("名字:", name)
print("年龄:", age)
printinfo(age=50, name="runoob") # ✅ 顺序不同也能调用
2.3 默认参数
调用时如果不传某个参数,就使用默认值。默认参数必须放在必需参数后面:
def printinfo(name, age=35):
print("名字:", name)
print("年龄:", age)
printinfo("runoob") # 名字: runoob, 年龄: 35
printinfo("runoob", 50) # 名字: runoob, 年龄: 50
# ❌ 错误写法 — 默认参数不能在前面
def printinfo(age=35, name): # SyntaxError: non-default argument follows default argument
pass
2.4 不定长参数 *args
加了 * 的参数会以元组的形式接收所有未命名的变量参数:
def printinfo(arg1, *vartuple):
print("输出:")
print(arg1)
for var in vartuple:
print(var)
printinfo(10) # 输出: 10
printinfo(70, 60, 50) # 输出: 70 \n 60 \n 50
2.5 不定长参数 **kwargs
加了 ** 的参数会以字典的形式接收所有关键字参数:
def printinfo(arg1, **vardict):
print("输出:")
print(arg1)
print(vardict)
printinfo(1, a=2, b=3)
# 输出:
# 1
# {'a': 2, 'b': 3}
2.6 强制位置参数 / 和强制关键字参数 *
Python 3.8 新增的语法,用 / 和 * 来分隔参数:
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
f(10, 20, 30, d=40, e=50, f=60) # ✅
# f(10, b=20, c=30, d=40, e=50, f=60) # ❌ b 必须用位置参数
# f(10, 20, 30, 40, 50, f=60) # ❌ e 必须用关键字参数
/ 之前的参数:只能用位置参数,不能用关键字
* 之后的参数:只能用关键字,不能用位置
- 中间的参数:两种都可以
三、参数传递:可变 vs 不可变
Python 中参数传递严格来说既不是值传递也不是引用传递,而是传对象引用。
3.1 传不可变对象(字符串、数字、元组)
函数内部修改参数时,实际上是创建了一个新对象,不影响外部变量:
def change(a):
print(id(a)) # 4379369136 — 和外部的 a 指向同一个对象
a = 10
print(id(a)) # 4379369424 — 新对象,和外部的 a 没关系了
a = 1
print(id(a)) # 4379369136
change(a)
print(a) # 1 — 外部 a 没变
3.2 传可变对象(列表、字典)
函数内部修改参数的内容(如 append),会影响外部变量:
def changeme(mylist):
mylist.append([1, 2, 3, 4])
print("函数内取值:", mylist)
mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值:", mylist)
# 函数内取值: [10, 20, 30, [1, 2, 3, 4]]
# 函数外取值: [10, 20, 30, [1, 2, 3, 4]] — 外部也变了!
但如果在函数内部对参数重新赋值,外部不受影响:
def changeme(mylist):
mylist = [1, 2, 3, 4] # 重新赋值,创建了新对象
print("函数内取值:", mylist)
mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值:", mylist)
# 函数内取值: [1, 2, 3, 4]
# 函数外取值: [10, 20, 30] — 外部没变
一句话区分: 修改对象的内容(append/修改元素)会影响外部,重新赋值(=)不会。
四、返回值
Python 函数可以返回多个值,以元组形式返回:
def return_sum(x, y):
return x + y
res = return_sum(4, 5)
print(res) # 9
# 返回多个值 — 本质上返回一个元组
def fun(a, b):
return a, b, a + b
print(fun(1, 2)) # (1, 2, 3)
# 解包接收
x, y, z = fun(1, 2)
print(x, y, z) # 1 2 3
# 不写 return 或 return 后没有值,返回 None
def empty_return(x, y):
c = x + y
return
res = empty_return(4, 5)
print(res) # None
五、变量作用域
变量的作用范围遵循 LEGB 规则,从内到外依次查找:
L(Local)→ E(Enclosing)→ G(Global)→ B(Built-in)
x = "全局变量"
def outer():
x = "外层函数变量"
def inner():
x = "内层函数变量"
print(x) # L — 找到 inner 的 x → "内层函数变量"
inner()
outer()
x = "全局变量"
def outer():
x = "外层函数变量"
def inner():
# inner 没有定义 x,向上找
print(x) # E — 找到 outer 的 x → "外层函数变量"
inner()
outer()
x = "全局变量"
def outer():
# outer 没有定义 x,继续向上找
def inner():
# inner 和 outer 都没有 x
print(x) # G — 找到全局的 x → "全局变量"
inner()
outer()
5.1 global 关键字
在函数内部修改全局变量,需要先用 global 声明:
a = 10
def sum(n):
global a # 声明 a 是全局变量
n += a
a = 11
print("a =", a, ", n =", n)
sum(3) # a = 11, n = 13
print("外 a =", a) # a = 11 — 全局变量被修改了
如果函数内部不使用 global,直接对同名变量赋值会创建一个局部变量,不会影响全局:
a = 10
def test():
a = 20 # 这是局部变量,不是修改全局的 a
print("a =", a)
test() # a = 20
print("外 a =", a) # a = 10 — 全局的没变
5.2 nonlocal 关键字
在嵌套函数中,修改外层函数(不是全局)的变量:
def outer():
count = 0
def inner():
nonlocal count # 声明 count 是外层函数的变量
count += 1
print(count)
inner() # 1
inner() # 2
inner() # 3
outer()
nonlocal 只能修改外层函数的局部变量,不能修改全局变量。如果要修改全局变量,用 global。
六、Lambda 匿名函数
Lambda 是一种只包含一个表达式的小型匿名函数,不需要用 def 定义。
lambda [arg1, arg2, ...]: expression
# 普通函数
def add(a, b):
return a + b
# 等价的 lambda
add = lambda a, b: a + b
print(add(10, 20)) # 30
Lambda 没有函数名,只能通过赋值给变量或作为参数传递:
x = lambda a: a + 10
print(x(5)) # 15
y = lambda a, b: a * b
print(y(5, 6)) # 30
z = lambda: "Hello, world!"
print(z()) # Hello, world!
Lambda 也可以使用关键字参数和默认值:
g = lambda x, y=0: x**2 + y**2
print(g(2, 3)) # 13
print(g(2)) # 4
print(g(y=3)) # 9
6.1 Lambda 与 map()
map() 对序列中的每个元素应用一个函数:
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
等价于列表推导式:
squared = [x**2 for x in numbers]
6.2 Lambda 与 filter()
filter() 筛选满足条件的元素:
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # [2, 4, 6, 8]
等价于:
even = [x for x in numbers if x % 2 == 0]
6.3 Lambda 与 reduce()
reduce() 对序列进行累积计算:
from functools import reduce
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120 — 即 1*2*3*4*5
6.4 Lambda 作为参数传递
Lambda 最常见的用途就是作为其他函数的参数:
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
# 按成绩排序
students.sort(key=lambda s: s[1])
print(students) # [('Charlie', 78), ('Alice', 85), ('Bob', 92)]
# 按姓名排序
students.sort(key=lambda s: s[0])
print(students) # [('Alice', 85), ('Bob', 92), ('Charlie', 78)]
6.5 Lambda 在闭包中
def myfunc(n):
return lambda a: a * n
mydoubler = myfunc(2) # n=2,返回一个乘以 2 的 lambda
mytripler = myfunc(3) # n=3,返回一个乘以 3 的 lambda
print(mydoubler(11)) # 22
print(mytripler(11)) # 33
这里 myfunc 返回了一个 lambda,lambda 捕获了外层的 n 值,这就是闭包。
七、函数作为参数传递
Python 中函数也是对象,可以作为参数传递给其他函数:
def execute(f):
"""执行一个没有参数的函数"""
f()
def hello():
print("Hello, world!")
execute(hello) # Hello, world!
def apply(func, value):
return func(value)
print(apply(lambda x: x * 2, 5)) # 10
print(apply(lambda x: x ** 2, 5)) # 25
八、文档字符串(DocString)
函数的文档说明写在 def 下面,用三引号包裹。可以通过 函数名.__doc__ 查看:
def add(a, b):
"""这是 add 函数文档,计算两个数的和"""
return a + b
print(add.__doc__) # 这是 add 函数文档,计算两个数的和
九、类型注解(Type Hints)
Python 3.5+ 支持在函数参数和返回值上标注类型,不会影响运行,但对 IDE 智能补全很有帮助:
def add(a: int, b: int) -> int:
return a + b
def greet(name: str) -> None:
print(f"Hello, {name}")
# 复杂类型标注
from typing import List, Dict
def process(data: List[int]) -> Dict[str, int]:
return {"sum": sum(data), "len": len(data)}