目录
[1. 什么是函数?](#1. 什么是函数?)
[2. 函数的定义](#2. 函数的定义)
[2.1 基本语法](#2.1 基本语法)
[2.2 带参数的函数](#2.2 带参数的函数)
[2.3 带返回值的函数](#2.3 带返回值的函数)
[2.4 空函数(占位)](#2.4 空函数(占位))
[3. 函数的调用](#3. 函数的调用)
[4. 参数类型详解](#4. 参数类型详解)
[4.1 位置参数(必须参数)](#4.1 位置参数(必须参数))
[4.3 关键字参数](#4.3 关键字参数)
[4.4 可变长度参数](#4.4 可变长度参数)
[4.5 参数组合顺序](#4.5 参数组合顺序)
[5. 返回值详解](#5. 返回值详解)
[5.1 单个返回值](#5.1 单个返回值)
[5.2 多个返回值(本质是返回元组)](#5.2 多个返回值(本质是返回元组))
[5.3 return 语句省略时返回 None](#5.3 return 语句省略时返回 None)
[5.4 return 提前退出函数](#5.4 return 提前退出函数)
[6. 作用域](#6. 作用域)
[6.1 局部变量与全局变量](#6.1 局部变量与全局变量)
[6.2 nonlocal 关键字(用于嵌套函数)](#6.2 nonlocal 关键字(用于嵌套函数))
[7. 函数也是对象](#7. 函数也是对象)
[8. 匿名函数(lambda)](#8. 匿名函数(lambda))
[9. 递归函数](#9. 递归函数)
[10. 类型注解(Type Hints)](#10. 类型注解(Type Hints))
[11. 常见内置函数示例](#11. 常见内置函数示例)
在编程中,我们经常需要重复执行某段代码。如果每次都复制粘贴,不仅代码冗长,而且修改起来非常麻烦。函数就是解决这个问题的利器:它将一段具有独立功能的代码打包成一个"积木块",需要时只需"调用"即可。
1. 什么是函数?
函数是一段可重复使用的代码块,它接收输入(参数),进行处理,并可能返回输出(返回值)。函数可以帮你:
-
避免重复:同样的逻辑只需写一次。
-
模块化:将复杂程序拆分成小功能块。
-
易维护:修改函数内部实现,所有调用处自动生效。
Python 内置了很多函数(如 print()、len()),你也可以自定义函数。
2. 函数的定义
2.1 基本语法
使用 def 关键字定义函数,后面跟函数名、括号 () 和冒号 :,函数体缩进。
def function_name(parameters):
"""可选的文档字符串"""
函数体代码
return 返回值 # 可选
eg
def greet():
"""打印欢迎信息"""
print("Hello, welcome to Python!")
# 调用函数
greet()
-
def greet()::def是定义函数的关键字,greet是函数名,空括号表示该函数不接受任何参数,冒号表示函数体开始。 -
"""打印欢迎信息""":这是一个文档字符串(docstring),用三个双引号包围。它是对函数功能的说明,可以通过help(greet)或greet.__doc__查看。虽然不是必需的,但强烈建议添加。 -
print("Hello, welcome to Python!"):这是函数体,缩进表示属于函数的一部分。当函数被调用时,这一行代码会被执行。 -
greet():调用函数,执行函数体内的代码。
2.2 带参数的函数
def greet_person(name):
"""向指定的人打招呼"""
print(f"Hello, {name}!")
greet_person("Alice") # Hello, Alice!
-
def greet_person(name)::定义了一个形参name,它将在函数内部作为变量使用。 -
print(f"Hello, {name}!"):函数体内使用了name变量,它的值由调用时传入。 -
greet_person("Alice"):调用时传入实参"Alice",它会被赋值给形参name,因此函数体内输出Hello, Alice!。
2.3 带返回值的函数
使用 return 语句返回结果。
def add(a, b):
"""返回两个数的和"""
result = a + b
return result
sum_val = add(3, 5) # 返回 8
print(sum_val)
代码解析:
-
def add(a, b)::定义两个形参a和b。 -
result = a + b:计算两个参数的和,赋值给局部变量result。 -
return result:将result的值返回给调用者。函数执行到return语句时会立即结束,并将返回值传递给调用方。 -
sum_val = add(3, 5):调用add函数,实参3和5分别传给a和b,函数返回8,然后赋值给变量sum_val。 -
print(sum_val):输出8。
2.4 空函数(占位)
如果函数体还未实现,可以用 pass 占位。
def not_ready():
pass # 稍后实现
代码解析:
-
def not_ready()::定义函数。 -
pass:是一个空语句,什么都不做。语法上需要一个语句,但逻辑上还未实现,用pass避免语法错误。
3. 函数的调用
调用函数就是使用函数名加上括号和实际参数。
# 定义
def multiply(x, y):
return x * y
# 调用
result = multiply(4, 5)
print(result) # 20
代码解析:
-
def multiply(x, y)::定义乘法函数。 -
return x * y:返回两数乘积。 -
result = multiply(4, 5):调用函数,实参4对应x,5对应y,返回值20存入result。 -
print(result):输出20。
调用时的细节:
-
函数名后面必须跟括号,即使没有参数也要写
()。 -
实参会按照形参的位置一一对应(位置参数)。
-
调用时传递的实参个数必须与形参个数匹配(除非有默认参数)。
4. 参数类型详解
4.1 位置参数(必须参数)
最普通的形式,调用时按顺序传递。
def greet_with_default(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet_with_default("Alice") # Hello, Alice!
greet_with_default("Bob", "Hi") # Hi, Bob!
代码解析:
-
greeting="Hello":为greeting参数指定默认值"Hello"。调用时若不提供第二个参数,则使用默认值。 -
greet_with_default("Alice"):只传一个参数,greeting使用默认值"Hello",输出Hello, Alice!。 -
greet_with_default("Bob", "Hi"):传入两个参数,greeting被覆盖为"Hi",输出Hi, Bob!。
注意:默认参数必须放在非默认参数之后。
# 错误写法
def wrong(a=1, b): # SyntaxError
pass
陷阱:默认参数的值只在函数定义时计算一次,因此不能使用可变对象作为默认值(如列表)。
# 不推荐
def add_item(item, lst=[]):
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] ← 同一个列表被重复使用
代码解析:
-
因为默认参数
lst在函数定义时被创建(一个空列表),之后每次调用若不提供lst,都会使用同一个列表对象,导致累积。 -
解决办法是使用
None作为默认值,然后在函数内部创建新列表。
正确做法:
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
代码解析:
-
lst=None:默认值设为不可变对象None。 -
if lst is None: lst = []:每次调用时若未传入列表,则新建一个空列表,保证了独立性。
4.3 关键字参数
调用时通过形参名指定实参,可以改变顺序。
def describe_pet(name, species):
print(f"{name} is a {species}")
describe_pet(species="cat", name="Tom") # Tom is a cat
代码解析:
describe_pet(species="cat", name="Tom"):显式指定每个参数的名字,顺序可以与定义不同。这样调用时不会受位置影响,增强了可读性。
4.4 可变长度参数
-
*args:接收任意多个位置参数,打包成元组。 -
**kwargs:接收任意多个关键字参数,打包成字典。def sum_all(*args):
return sum(args)print(sum_all(1, 2, 3, 4)) # 10
代码解析:
-
*args:收集所有传入的位置参数为一个元组,这里args是(1,2,3,4)。 -
sum(args):内置函数sum对元组求和,返回10。def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")print_info(name="Alice", age=30, city="Paris")
代码解析:
-
**kwargs:收集所有传入的关键字参数为一个字典,这里kwargs是{'name':'Alice','age':30,'city':'Paris'}。 -
遍历字典并打印。
4.5 参数组合顺序
在函数定义中,参数的顺序必须是:
-
位置参数
-
默认参数
-
*args -
关键字参数(仅限
*之后) -
**kwargsdef complex_func(a, b=1, *args, c=2, **kwargs):
pass
代码解析:
-
a是普通位置参数。 -
b=1是默认参数。 -
*args接收多余的位置参数。 -
c=2是仅限关键字参数(必须通过关键字传递)。 -
**kwargs接收多余的关键字参数。
5. 返回值详解
5.1 单个返回值
def square(x):
return x ** 2
代码解析 :函数返回 x 的平方,返回后调用方可以接收。
5.2 多个返回值(本质是返回元组)
def get_stats(numbers):
return min(numbers), max(numbers), sum(numbers)/len(numbers)
low, high, avg = get_stats([1,2,3,4,5])
print(low, high, avg) # 1 5 3.0
代码解析:
-
return min(...), max(...), avg(...):实际上返回了一个元组(min, max, avg)。 -
调用方通过
low, high, avg = ...进行拆包,分别接收三个值。
5.3 return 语句省略时返回 None
def do_nothing():
pass
print(do_nothing()) # None
代码解析 :函数没有显式 return,默认返回 None。
5.4 return 提前退出函数
def is_positive(n):
if n > 0:
return True
return False
代码解析:
-
如果
n > 0,执行return True,函数立即结束,不会执行后面的return False。 -
否则执行
return False。这种写法可避免使用else。
6. 作用域
6.1 局部变量与全局变量
-
在函数内部赋值的变量是局部变量,只在函数内有效。
-
在函数外部定义的变量是全局变量 ,可在函数内读取,但若要修改需用
global关键字。x = 10 # 全局变量
def func():
global x # 声明要修改全局变量
x = 20
y = 5 # 局部变量func()
print(x) # 20
代码解析:
-
x = 10:全局变量。 -
def func():内部global x:告诉 Python 这里的x是全局变量,而不是创建局部变量。 -
x = 20:修改全局x的值为 20。 -
y = 5:未声明global,因此是局部变量,函数外部无法访问。 -
调用
func()后,全局x变为 20。
注意 :避免过多使用 global,推荐通过参数传递和返回值来通信。
6.2 nonlocal 关键字(用于嵌套函数)
在内层函数中修改外层函数(非全局)的变量。
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
counter = outer()
print(counter()) # 1
print(counter()) # 2
代码解析:
-
outer函数中定义了局部变量count和内层函数inner。 -
inner中使用nonlocal count声明要修改的是外层函数outer的count变量,而不是创建一个新的局部变量。 -
count += 1修改外层变量。 -
outer返回inner函数对象,形成一个闭包。每次调用counter()都会增加并返回count的值。
7. 函数也是对象
在 Python 中,函数是一等公民,可以赋值给变量、作为参数传递、作为返回值。
def add(a, b):
return a + b
my_func = add # 赋值
print(my_func(3, 4)) # 7
代码解析 :将函数 add 赋值给变量 my_func,my_func 现在指向同一个函数对象,可以像 add 一样调用。
def apply(func, x, y):
return func(x, y)
print(apply(add, 5, 6)) # 11
代码解析 :apply 函数接收一个函数作为参数 func,然后在内部调用它。这体现了高阶函数的特性。
8. 匿名函数(lambda)
lambda 表达式用于创建简单的、单表达式的匿名函数。
square = lambda x: x ** 2
print(square(5)) # 25
代码解析:
-
lambda x: x ** 2定义了一个匿名函数,等价于def square(x): return x**2。 -
但
lambda只能包含一个表达式,不能包含语句。常用于 sort 的 key 参数
pairs = [(1, 'one'), (2, 'two'), (3, 'three')]
pairs.sort(key=lambda pair: pair[1]) # 按字符串排序
代码解析 :sort 的 key 参数接收一个函数,用于从每个元素中提取比较键。这里用 lambda 取每个元组的第二个元素(字符串)作为排序依据。
限制 :lambda 只能有一个表达式,不能包含语句(如 print、return 等)。
9. 递归函数
函数调用自身称为递归。需要明确的终止条件。
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120
代码解析:
-
if n <= 1: return 1:递归的终止条件,防止无限递归。 -
return n * factorial(n-1):递归调用,每次减少n的值。 -
调用过程:
factorial(5)→5 * factorial(4)→5 * 4 * factorial(3)→ ... →5 * 4 * 3 * 2 * 1。
注意:Python 递归深度有限制(默认约 1000),深层递归建议改用循环。
10. 类型注解(Type Hints)
Python 3.5+ 支持类型注解,提高代码可读性(不强制检查)。
def greet(name: str) -> str:
return f"Hello, {name}"
代码解析:
-
name: str:表示参数name应为字符串类型。 -
-> str:表示返回值应为字符串类型。 -
这些注解只是提示,不会在运行时检查类型错误。可以用
mypy工具进行静态类型检查。
11. 常见内置函数示例
-
print():输出 -
len():返回长度 -
type():返回类型 -
input():读取用户输入 -
range():生成整数序列 -
sum()、max()、min()等
这些内置函数无需定义,直接使用。
12.常见陷阱与最佳实践
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 使用可变对象作为默认参数 | 函数多次调用共享同一对象 | 使用 None 并在函数内创建新对象 |
| 在函数内修改全局变量未声明 | 会创建同名局部变量 | 使用 global 或通过参数/返回值传递 |
| 混淆位置参数和关键字参数 | 调用时顺序错误 | 明确指定关键字参数名 |
| 递归深度过大导致栈溢出 | 递归超过系统限制 | 改用循环或增加 sys.setrecursionlimit |
忘记 return 导致返回 None |
预期有返回值但实际为 None |
检查函数所有分支是否都有 return |
lambda 内使用复杂逻辑 |
无法使用语句,代码难读 | 改用普通函数 |
感谢你的观看,期待我们下次再见!