Python:函数(一)
python函数相关的知识点
1. 函数定义与调用
定义:使用 def
关键字,后接函数名和参数列表。
def greet(name):
"""打印问候语(文档字符串)"""
print(f"Hello, {name}!")
调用:
greet("Alice") # 输出:Hello, Alice!
2. 参数与返回值
- 位置参数:按顺序传递参数。
- 默认参数:为参数提供默认值。
def power(base, exponent=2):
return base ** exponent
print(power(3)) # 输出:9(使用默认exponent=2)
print(power(2, 4)) # 输出:16
- 返回值:使用
return
返回结果,无返回值时默认返回None
。
def add(a, b):
return a + b
result = add(3, 5) # result = 8
1. 参数传递的基本机制
Python中所有参数的传递都是 对象引用的传递:
不可变对象(如整数、字符串、元组):函数内对形参的修改会创建新对象,不影响实参。
可变对象(如列表、字典):函数内对形参的修改会影响实参(因为它们指向同一对象)。解决:传副本
示例
def modify(a, b,c): a = 2 # 修改不可变对象(创建新对象) b.append(3) # 修改可变对象 c.append(4) x = 1 # 不可变 y = [1, 2] # 可变 c=[1,2,3] modify(x, y,c.copy()) print(x) # 输出:1(未改变) print(y) # 输出:[1, 2, 3](已改变) print(c) # 输出:[1, 2, 3](不改变)
2. 参数传递的方式
Python支持多种参数传递方式,具体取决于函数定义和调用时的语法。
(1) 位置参数(Positional Arguments)
按参数定义顺序传递,最常见的方式。
def add(a, b): return a + b print(add(3, 5)) # 输出:8
(2) 关键字参数(Keyword Arguments)
通过参数名指定值,顺序可以打乱。
print(add(b=5, a=3)) # 输出:8
(3) 默认参数(Default Arguments)
为参数提供默认值,调用时可省略。
def greet(name, msg="Hello"): print(f"{msg}, {name}!") greet("Alice") # 输出:Hello, Alice! greet("Bob", "Hi") # 输出:Hi, Bob!
(4) 可变参数(*args 和 kwargs)**
*args
:接收任意数量的位置参数(元组形式)(写最后)。
**kwargs
:接收任意数量的关键字参数(字典形式)(写最后)。def print_all(*args, **kwargs): print("位置参数:", args) print("关键字参数:", kwargs) print_all(1, 2, name="Alice", age=25) # 输出: # 位置参数: (1, 2) # 关键字参数: {'name': 'Alice', 'age': 25}
3. 参数解包(Unpacking)
在调用函数时,可以使用
*
和**
解包序列或字典作为参数。示例
def func(a, b, c): print(a, b, c) # 解包列表/元组(按位置传递) args = [1, 2, 3] func(*args) # 输出:1 2 3 # 解包字典(按关键字传递) kwargs = {"a": 1, "b": 2, "c": 3} func(**kwargs) # 输出:1 2 3
4. 注意事项
(1) 默认参数的陷阱
默认参数的值在函数定义时计算一次,若默认值是可变对象,多次调用会共享该对象。
def append_to(item, lst=[]): lst.append(item) return lst # 第一次调用 print(append_to(1)) # 输出:[1] # 第二次调用 print(append_to(2)) # 输出:[1, 2](而不是预期的[2])
原因分析
- Python 在函数定义时(即代码加载时)就创建了默认参数
lst=[]
,并将其存储在函数对象的__defaults__
属性中。- 所有未显式传递
lst
参数的调用,都会共享同一个列表对象。- 每次调用
append_to
时,修改的是同一个列表,而不是创建新列表。修正方法:使用不可变对象(如
None
)占位。def append_to(item, lst=None): if lst is None: lst = [] # 每次调用时创建新列表 lst.append(item) return lst # 第一次调用 print(append_to(1)) # 输出:[1] # 第二次调用 print(append_to(2)) # 输出:[2](符合预期)
关键点
- 默认参数设为
None
(不可变对象)。- 在函数内部检查
lst is None
,若为真则创建一个新的空列表。- 每次调用都会生成新的列表对象,避免共享问题。
默认参数是字典、集合等可变对象时,同样需要警惕:
# 错误示例:默认值为空字典 def update_dict(key, value, d={}): d[key] = value return d print(update_dict("a", 1)) # {'a': 1} print(update_dict("b", 2)) # {'a': 1, 'b': 2} # 正确写法 def update_dict(key, value, d=None): if d is None: d = {} d[key] = value return d
通过
__defaults__
属性查看函数的默认参数值:def func(a, lst=[]): pass print(func.__defaults__) # 输出:([],) # 调用函数后,默认列表被修改 func(1) print(func.__defaults__) # 输出:([1],)
- 规则:默认参数的值在函数定义时被计算一次,并存储在函数对象中。
- 陷阱:若默认值是可变对象(列表、字典等),所有未显式传递该参数的调用会共享同一个对象。
- 解决:用
None
作为默认值,在函数内部初始化可变对象。# 错误写法 ❌ def func(arg=[]): pass # 正确写法 ✅ def func(arg=None): if arg is None: arg = []
(2) 避免意外修改可变对象
若函数需要处理可变对象但不希望影响原始数据,应创建副本。
def process_list(lst): lst = lst.copy() # 创建副本 lst.append(100) return lst original = [1, 2, 3] modified = process_list(original) print(original) # [1, 2, 3](未被修改) print(modified) # [1, 2, 3, 100]
5. 总结
参数类型
特点
位置参数
按顺序传递,简单直接
关键字参数
按参数名指定,提高可读性
默认参数
提供默认值,简化调用
可变参数(
*args
)接收任意数量的位置参数
可变参数(
**kwargs
)接收任意数量的关键字参数
参数解包
使用
*
和**
将序列或字典解包为参数
返回值
1. 基本返回值
使用
return
语句返回结果。如果没有return
或return
后无值,函数默认返回None
。def add(a, b): return a + b result = add(3, 5) print(result) # 输出:8 # 无返回值的函数 def greet(name): print(f"Hello, {name}!") result = greet("Alice") # 输出:Hello, Alice! print(result) # 输出:None
2. 返回多个值
Python 允许通过 返回元组 实现多值返回,调用时可以直接解包。
def min_max(numbers): return min(numbers), max(numbers) values = [4, 2, 9, 7] min_val, max_val = min_max(values)#解包 print(f"最小值: {min_val}, 最大值: {max_val}") # 输出:最小值: 2, 最大值: 9
本质
函数返回的多个值实际上是一个元组,解包是隐式操作:
result = min_max(values) print(result) # 输出:(2, 9)
3. 返回复杂对象
函数可以返回列表、字典、类实例等任意对象。
示例 1:返回字典
def create_person(name, age): return {"name": name, "age": age} person = create_person("Bob", 30) print(person) # 输出:{'name': 'Bob', 'age': 30}
示例 2:返回函数(闭包)
def outer(): def inner(): print("内部函数被调用") return inner func = outer() func() # 输出:内部函数被调用
4. 返回生成器(
yield
)使用
yield
关键字定义生成器函数,返回一个生成器对象,支持迭代。def count_up_to(max): count = 1 while count <= max: yield count count += 1 generator = count_up_to(3) for num in generator: print(num) # 依次输出:1, 2, 3
5. 返回
None
的情况以下情况函数返回
None
:
没有
return
语句。
return
语句没有指定返回值。
return
后仅写None
。def func1(): pass def func2(): return def func3(): return None print(func1()) # None print(func2()) # None print(func3()) # None
6. 提前返回与多条件返回
函数中可以存在多个
return
语句,执行到第一个return
时函数终止。示例:根据条件返回不同结果
def check_number(num): if num < 0: return "负数" elif num == 0: return "零" else: return "正数" print(check_number(-5)) # 输出:负数 print(check_number(0)) # 输出:零 print(check_number(10)) # 输出:正数
7. 注意事项
(1) 返回值与可变对象
如果返回的是可变对象(如列表、字典),多次调用可能共享同一对象(需谨慎)。
def get_list(): return [] list1 = get_list() list2 = get_list() list1.append(1) print(list2) # 输出:[](安全,因为每次返回新列表) # 但若函数内部缓存了可变对象: def get_cached_list(lst=[]): return lst list3 = get_cached_list() list4 = get_cached_list() list3.append(1) print(list4) # 输出:[1](危险!共享同一列表)
(2) 类型提示(Python 3+)
可以为返回值添加类型注解,提高代码可读性。
def add(a: int, b: int) -> int: return a + b
8. 总结
返回类型
示例
说明
单个值
return 10
直接返回数据
多个值(元组解包)
return a, b
返回元组,调用时可解包
复杂对象
return {"data": [...]}
返回字典、列表、实例等
函数或闭包
return inner
返回内部函数,形成闭包
生成器
yield value
返回生成器,支持惰性计算
None
return
或return None
默认返回值
合理使用返回值可以让函数更灵活,例如:
通过返回生成器处理大数据集(节省内存)。
返回函数实现装饰器或策略模式。
返回元组简化多值传递。
3. 作用域与 global
- 函数内部默认不能修改全局变量,需使用
global
声明。
x = 10
def modify_global():
global x
x = 20
modify_global()
print(x) # 输出:20
5. 嵌套函数、闭包与装饰器
- 嵌套函数:在函数内部定义另一个函数。
- 闭包:内部函数记住外部作用域的变量。
def outer(msg):
def inner():
print(msg) # 闭包保留外部变量msg
return inner
closure = outer("Hello")
closure() # 输出:Hello
装饰器用于增强函数功能,不修改原函数代码。
def my_decorator(func):
def wrapper():
print("装饰器:函数执行前")
func()
print("装饰器:函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# 输出:
# 装饰器:函数执行前
# Hello!
# 装饰器:函数执行后
在 Python 中,闭包(Closure) 和 装饰器(Decorator) 是两个紧密关联且强大的概念,它们通过函数的高阶特性实现代码复用和动态扩展功能。
一、闭包(Closure)
1. 定义
闭包是 一个函数与其外部作用域变量的绑定关系。即使外部函数已执行完毕,内部函数仍能访问和修改外部函数的变量。
2. 核心机制
捕获变量:内部函数会“记住”外部函数的作用域(即闭包环境)。
延迟绑定:闭包中的变量引用在函数调用时才解析,可能导致意外行为(需注意循环变量问题)。
3. 示例
def outer(msg): def inner(): print(msg) # inner捕获了外部变量msg return inner closure = outer("Hello") closure() # 输出:Hello(即使outer函数已执行完毕,msg仍被保留)
4. 实际应用
工厂函数:动态生成不同行为的函数。
def power_factory(exponent): def power(base): return base ** exponent # 捕获exponent return power square = power_factory(2) print(square(3)) # 9(3^2)
延迟绑定问题与解决:
# 错误示例:所有闭包共享循环变量i的最终值 def create_buttons(): buttons = [] for i in range(3): def button(): print(f"Button {i} clicked") buttons.append(button) return buttons # 解决:通过默认参数或嵌套函数绑定当前值 def create_buttons_fixed(): buttons = [] for i in range(3): def wrapper(x): # 捕获当前x的值 def button(): print(f"Button {x} clicked") return button buttons.append(wrapper(i)) return buttons
1. 状态保持计数器
def counter(start=0): count = start # 闭包保存状态 def increment(): nonlocal count # 声明为非局部变量 count += 1 return count return increment c = counter(10) print(c()) # 11 print(c()) # 12(闭包记住count的值)
2. 动态配置函数
def configure_printer(prefix): def printer(message): print(f"[{prefix}] {message}") # 闭包捕获prefix return printer error_log = configure_printer("ERROR") info_log = configure_printer("INFO") error_log("File not found!") # [ERROR] File not found! info_log("Process started.") # [INFO] Process started.
3. 缓存机制
def cached(func): cache = {} # 闭包保存缓存数据 def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper @cached def factorial(n): return 1 if n <= 1 else n * factorial(n-1) print(factorial(5)) # 120(后续调用直接返回缓存结果)
二、装饰器(Decorator)
1. 定义
装饰器是 一个修改其他函数行为的函数,通过接受目标函数作为参数,返回增强后的新函数,而无需修改原函数代码。
2. 核心机制
高阶函数:装饰器本身是一个返回函数的函数。
语法糖:使用
@decorator
简化装饰器调用。3. 基本示例
def logger(func): def wrapper(*args, **kwargs): print(f"调用函数: {func.__name__}") return func(*args, **kwargs) return wrapper @logger def add(a, b): return a + b print(add(3, 5)) # 输出: # 调用函数: add # 8
4. 带参数的装饰器
需三层嵌套函数:
def repeat(n): def decorator(func): def wrapper(*args, **kwargs): result = None for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def greet(name): print(f"Hello, {name}!") greet("Alice") # 输出: # Hello, Alice! # Hello, Alice! # Hello, Alice!
5. 类装饰器
通过实现
__call__
方法:class Timer: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): import time start = time.time() result = self.func(*args, **kwargs) end = time.time() print(f"{self.func.__name__} 执行时间: {end - start:.2f}s") return result @Timer def slow_func(): time.sleep(1) slow_func() # 输出:slow_func 执行时间: 1.00s
6. 实际应用
缓存(Memoization):
def memoize(func): cache = {} def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper @memoize def fibonacci(n): return n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)
权限验证:
def login_required(func): def wrapper(user, *args, **kwargs): if user.is_authenticated: return func(user, *args, **kwargs) else: raise PermissionError("用户未登录") return wrapper
7. 注意事项
多个装饰器的顺序:装饰器从下往上应用,但执行顺序从上到下。
@decorator1 @decorator2 def func(): pass # 等效于 func = decorator1(decorator2(func))
保留原函数元信息:使用
functools.wraps
保留原函数名和文档。from functools import wraps def logger(func): @wraps(func) def wrapper(*args, **kwargs): print(f"调用函数: {func.__name__}") return func(*args, **kwargs) return wrapper
三、闭包与装饰器的关系
装饰器依赖闭包:装饰器通常通过闭包保存目标函数和装饰器参数。
闭包是装饰器的基础:装饰器返回的增强函数本质上是一个闭包,捕获了原函数和装饰逻辑。
四、总结
概念
特点
应用场景
闭包
内部函数捕获外部变量,保留状态
工厂函数、延迟计算、回调
装饰器
动态扩展函数功能,不修改原代码,支持多层装饰
日志、缓存、权限、性能测试
7. Lambda 函数
匿名函数,适用于简单操作。
square = lambda x: x ** 2
print(square(4)) # 输出:16
# 结合map使用
numbers = [1, 2, 3]
squared = list(map(lambda x: x**2, numbers)) # [1, 4, 9]
8. 生成器函数
使用 yield
逐个返回值,节省内存。
def count_up_to(max):
count = 1
while count <= max:
yield count
count += 1
for num in count_up_to(3):
print(num) # 输出:1 2 3
9. 递归函数
函数调用自身,需设置终止条件。
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n-1)
print(factorial(5)) # 输出:120
10. 参数传递的注意事项
- 可变对象(如列表)作为参数时,函数内修改会影响原始对象。
def append_item(lst, item):
lst.append(item)
my_list = [1, 2]
append_item(my_list, 3)
print(my_list) # 输出:[1, 2, 3]
11. 函数注解
提供类型提示(Python 3+)。
def greet(name: str, age: int) -> str:
return f"{name} is {age} years old."