Python 中函数完全指南
本文档面向零基础新手,详细讲解 函数 是什么、形参与实参、位置实参与关键字实参、默认值、返回值、可选实参、返回字典、传递列表与禁止修改列表、任意数量实参,以及 *args 与 **kwargs 的区别,并配有大量示例。
第一部分:函数是什么?
一、函数的概念
函数 是一段有名字的、可重复使用的代码块。你可以把“要做的一件事”写进函数,以后需要时调用这个函数名即可,不用重复写同样一段代码。
好处:
- 复用:写一次,多处调用
- 清晰:给代码块起名,逻辑更易读
- 维护:改一处,所有调用处都生效
定义函数的基本语法:
def 函数名(参数1, 参数2, ...):
函数体(缩进的代码)
return 返回值 # 可选,没有则返回 None
注意:
- 定义用 def,后面跟函数名和一对圆括号,括号里可以写参数(也可以不写)
- 括号后要有冒号
: - 函数体必须缩进(一般 4 个空格)
调用函数: 写 函数名(实参1, 实参2, ...) 即可。
def say_hello():
print("你好!")
say_hello() # 调用,输出:你好!
say_hello() # 再次调用, again 输出:你好!
二、形参和实参
- 形参(parameter):定义函数时,写在
def 函数名(...)括号里的变量名,用来“占位”,表示“这个函数需要这几个数据”。 - 实参(argument):调用函数时,你真正传进去的具体值,会赋给对应的形参。
可以简单记:
- 形参 = 函数定义里的“占位符”
- 实参 = 调用时传入的“实际值”
示例:
def greet(name): # name 是形参
print("你好,", name)
greet("小明") # "小明" 是实参,传给形参 name
greet("小红") # "小红" 是实参
输出:
你好, 小明
你好, 小红
这里:形参是 name,实参是 "小明"、"小红"。调用时,实参按顺序(或按名字)传给形参,函数体内用到的就是传入的值。
第二部分:传递实参——位置实参与关键字实参
三、位置实参(按顺序对应)
位置实参:调用时按形参定义的顺序依次传入实参,第 1 个实参给第 1 个形参,第 2 个给第 2 个,以此类推。
def describe_pet(animal, name):
print("我有一只", animal, ",它叫", name)
describe_pet("狗", "旺财") # animal="狗", name="旺财"
describe_pet("猫", "咪咪") # animal="猫", name="咪咪"
输出:
我有一只 狗 ,它叫 旺财
我有一只 猫 ,它叫 咪咪
注意: 顺序写反,含义就反了:
describe_pet("旺财", "狗") # 会变成“我有一只 旺财 ,它叫 狗”,不符合本意
所以位置实参要严格按形参顺序传。
四、关键字实参(按名字对应)
关键字实参:调用时写成 形参名=值 的形式,这样顺序可以打乱,也不会搞混。
def describe_pet(animal, name):
print("我有一只", animal, ",它叫", name)
describe_pet(name="旺财", animal="狗") # 顺序和定义不同也可以
describe_pet(animal="猫", name="咪咪")
效果与按顺序传 describe_pet("狗", "旺财") 一样。适合参数较多或容易记错顺序时使用。
五、位置实参与关键字实参混用
同一次调用里,可以前面用位置实参,后面用关键字实参。但位置实参必须写在关键字实参前面,否则会报错。
describe_pet("狗", name="旺财") # 正确:第一个位置给 animal,name 用关键字
# describe_pet(animal="狗", "旺财") # 错误:关键字实参不能出现在位置实参前面
第三部分:默认值
六、给形参指定默认值
在定义函数时,可以给某些形参赋一个默认值。调用时如果不传这个参数,就使用默认值;传了则用你传的值。
语法: 形参名 = 默认值
def describe_pet(name, animal="狗"):
print("我有一只", animal, ",它叫", name)
describe_pet("旺财") # 只传 name,animal 用默认值 "狗"
describe_pet("咪咪", "猫") # 两个都传,animal 为 "猫"
输出:
我有一只 狗 ,它叫 旺财
我有一只 猫 ,它叫 咪咪
要点: 带默认值的形参一般放在没有默认值的形参后面,否则调用时容易混淆(Python 允许把默认参数放前面,但那样调用就必须用关键字,不推荐新手这样写)。
推荐:
def func(a, b, c=0, d=1): # 无默认值的 a、b 在前
pass
第四部分:返回值
七、用 return 返回结果
函数可以通过 return 把计算结果(或任意值)“还给”调用者。调用处可以把这个结果赋给变量或参与运算。
语法: return 表达式。执行到 return 后,函数立即结束,后面的代码不会执行。
def add(a, b):
s = a + b
return s
result = add(3, 5)
print(result) # 8
无 return 或只写 return: 函数默认返回 None。
def no_return():
print("只打印,不返回")
x = no_return()
print(x) # None
八、返回多个值(实质是元组)
Python 允许一次 return 多个值,用逗号分隔。实际上返回的是一个元组,调用处可以解包成多个变量。
def get_name_and_age():
return "小明", 18
name, age = get_name_and_age()
print(name, age) # 小明 18
# 也可以整体接收
t = get_name_and_age()
print(t) # ('小明', 18)
第五部分:让实参变成可选的
九、用默认值实现“可选实参”
有时某个参数有时传、有时不传,可以给该形参设一个默认值(例如 None 或空字符串),在函数里再判断:若未传(即等于默认值),就按“未提供”处理。
示例:中间名可选
def get_full_name(first_name, last_name, middle_name=None):
if middle_name is None:
return first_name + " " + last_name
else:
return first_name + " " + middle_name + " " + last_name
print(get_full_name("张", "三")) # 张 三
print(get_full_name("张", "三", "小")) # 张 小 三
这样 middle_name 就是“可选实参”:不传就用 None,函数内部再分支。
第六部分:返回字典
十、函数返回一个字典
return 可以返回任意类型,包括字典。适合“组装一条记录”再返回给调用者。
def build_person(name, age):
person = {
"name": name,
"age": age,
}
return person
p = build_person("小明", 18)
print(p) # {'name': '小明', 'age': 18}
可选字段用默认值: 若某些键“有时有、有时没有”,可以先用默认值(如 None),只在有值时写入字典。
def build_person(name, age, city=None):
person = {"name": name, "age": age}
if city is not None:
person["city"] = city
return person
print(build_person("小明", 18)) # {'name': '小明', 'age': 18}
print(build_person("小红", 19, "北京")) # {'name': '小红', 'age': 19, 'city': '北京'}
第七部分:传递列表
十一、把列表作为实参传入函数
实参可以是列表(或任何对象)。函数内部通过形参拿到的是同一个列表对象的引用,所以可以在函数里遍历、读取、修改列表内容。
def greet_all(names):
for name in names:
print("你好,", name)
users = ["小明", "小红", "小刚"]
greet_all(users)
输出:
你好, 小明
你好, 小红
你好, 小刚
十二、在函数内修改列表会影响原列表
因为传的是引用,函数里对列表做 append、remove、修改元素 等操作,都会作用在调用者手里的那个列表上。
def append_one(lst):
lst.append(1)
nums = [10, 20]
append_one(nums)
print(nums) # [10, 20, 1]
这是 Python 的“可变对象”行为:形参和实参指向同一块数据,所以“在函数里改列表”就等于“改外面的列表”。
第八部分:禁止函数修改列表(传副本)
十三、若不想让函数改原列表,可传副本
如果希望函数只对列表做操作,但不改变调用者原来的列表,可以在调用时传入列表的副本,而不是原列表本身。
做法: 用 切片 列表[:] 或 list(列表) 造一个副本,再把这个副本传给函数。
def append_one(lst):
lst.append(1)
nums = [10, 20]
append_one(nums[:]) # 传的是副本,原列表不变
print(nums) # [10, 20]
或者:
append_one(list(nums))
print(nums) # [10, 20]
要点: 函数内部拿到的若是副本,对副本的修改不会影响外面的原列表。需要“禁止函数修改列表”时,就在调用处传副本。
第九部分:传递任意数量的实参
十四、用 *形参名 接收“任意个位置实参”
有时希望函数能接受任意多个位置实参(个数不固定),可以在形参前加一个 *,例如 *args。这样,多出来的所有位置实参会被收进一个元组里,在函数内通过这个形参使用。
示例: 求任意个数的和
def add_many(*args):
total = 0
for n in args:
total = total + n
return total
print(add_many(1, 2, 3)) # 6
print(add_many(1, 2, 3, 4, 5)) # 15
这里 args 在函数里就是一个元组,例如 add_many(1, 2, 3) 时,args == (1, 2, 3)。
注意: 名字不一定要叫 args,习惯上常用 *args 表示“多出来的位置实参”,但 * 后面的名字可以自己取,例如 *numbers。
十五、用 **形参名 接收“任意个关键字实参”
如果希望函数能接受任意多个关键字实参(键=值 形式),可以用 **形参名,例如 **kwargs。多出来的所有关键字实参会被收进一个字典里:键是参数名,值是对应的实参值。
def build_profile(**kwargs):
profile = {}
for key, value in kwargs.items():
profile[key] = value
return profile
p = build_profile(name="小明", age=18, city="北京")
print(p) # {'name': '小明', 'age': 18, 'city': '北京'}
十六、*args 和 **kwargs 的区别(重要)
| 写法 | 含义 | 调用时对应写法 | 函数内得到的类型 |
|---|---|---|---|
| *args | 接收多余的位置实参 | func(1, 2, 3) 中 1,2,3 |
元组 tuple |
| **kwargs | 接收多余的关键字实参 | func(a=1, b=2) 中 a=1,b=2 |
字典 dict |
记忆:
- *args:多出来的按顺序的实参 → 收成元组
- **kwargs:多出来的名字=值 → 收成字典
可以同时使用: 定义时顺序一般为:普通形参、*args、**kwargs。
def mix(a, b, *args, **kwargs):
print("a =", a)
print("b =", b)
print("args =", args)
print("kwargs =", kwargs)
mix(1, 2, 3, 4, x=5, y=6)
输出:
a = 1
b = 2
args = (3, 4)
kwargs = {'x': 5, 'y': 6}
常见用法:
- *args:写“可以接受任意个参数”的函数(如求和、打印多行等)
- **kwargs:写“可以接受任意个键值对”的函数(如组装配置、构造字典、包装其他函数等)
十七、综合示例:位置、默认值、*args、**kwargs 混用
推荐形参顺序: 普通位置形参 → 默认值形参 → *args → **kwargs
def example(a, b, c=0, *args, **kwargs):
print("a:", a, "b:", b, "c:", c)
print("args:", args)
print("kwargs:", kwargs)
example(1, 2)
# a: 1 b: 2 c: 0
# args: ()
# kwargs: {}
example(1, 2, 3, 4, 5, x=10, y=20)
# a: 1 b: 2 c: 3
# args: (4, 5)
# kwargs: {'x': 10, 'y': 20}
第十部分:小结与常见注意点
十八、形参与实参、传参方式小结
| 概念/写法 | 说明 |
|---|---|
| 形参 | 定义时括号里的变量,占位用 |
| 实参 | 调用时传入的具体值 |
| 位置实参 | 按顺序传,第 1 个实参给第 1 个形参 |
| 关键字实参 | 形参名=值,顺序可打乱 |
| 默认值 | 形参 = 默认值,调用可不传该参数 |
| return | 返回一个值(或元组、字典等),不写则返回 None |
十九、传递列表与“禁止修改”
| 需求 | 做法 |
|---|---|
| 函数内可以改列表 | 直接传列表:func(my_list) |
| 函数内不能改原列表 | 传副本:func(my_list[:]) 或 func(list(my_list)) |
二十、*args 与 **kwargs 速查
| 形参写法 | 接收什么 | 类型 |
|---|---|---|
| *args | 多余的位置实参 | 元组 |
| **kwargs | 多余的关键字实参 | 字典 |
二十一、新手常见陷阱
-
默认值用可变对象(列表、字典)
默认值在定义时只求值一次,若写def f(a=[]),多次调用且不传 a 时,会共用同一个列表。应写成def f(a=None),函数里再if a is None: a = []。 -
return 之后还有代码
return 后面的语句不会执行,若逻辑要分多步,注意 return 写对位置。 -
实参顺序
混用位置与关键字时,位置实参必须在关键字实参前面。