一、函数基本结构:
def 函数名(参数列表):
函数体
return 返回值
二、参数类型顺序
- 位置参数必须在默认参数之前:这是因为位置参数没有默认值,而默认参数有,所以位置参数需要先被解析。
*args
应该在位置参数和默认参数之后:因为*args
收集所有额外的位置参数,它应该出现在所有普通位置参数之后。**kwargs
应该在最后:因为它收集所有的额外关键字参数,自然应该放在所有其他参数之后。
def func(位置参数, 默认参数, *args, **kwargs):
pass
三、装饰器
本质上是一个函数,可以让我们在不修改原函数的情况下,为其增加新的功能,就像给房子装修一样,不改变房子的结构,但是增加了新的功能
1、工作原理:
- 装饰器是一个函数
- 它接收一个函数作为参数
- 在内部定义一个新函数来包装原函数
- 返回这个新函数
"""
一个最经典的装饰器示例
功能:记录函数的执行时间
"""
import time# 用于记录时间
import functools # functools 模块提供了一些用于高阶函数的工具,包括 wraps 装饰器
def timer(func):# 装饰器函数,用于记录函数的执行时间
@functools.wraps(func) # 保留原函数的信息(名称、文档等)
def wrapper(*args, **kwargs): # 包装函数,用于在函数执行前后添加日志 *args,**kwargs表示可以接受任意数量的位置参数和关键字参数
# 1. 装饰器要做的事情:记录开始时间
start_time = time.time()
# 2. 执行被装饰的原函数
result = func(*args, **kwargs)
# 3. 装饰器要做的事情:计算执行时间
end_time = time.time()
print(f'函数 {func.__name__} 执行时间:{end_time - start_time:.2f} 秒')
# 4. 返回原函数的结果
return result
return wrapper
# 使用装饰器
@timer
def slow_function(sleep_time):
"""这是一个测试函数"""
time.sleep(sleep_time)# 睡眠sleep_time秒
return "完成"
@timer
def add(a,b):
return a+b
# 测试
if __name__ == "__main__":
print("开始测试...")
result = slow_function(1) # 睡眠1秒
print(f"函数返回值: {result}")
print(f"函数名称: {slow_function.__name__}") # 因为用了 wraps,这里会显示 'slow_function' 而不是 'wrapper'
print("--------------------------------")
result2=add(1,2)
print(f"函数返回值: {result2}")
2、记忆要点:
- 装饰器就是一个函数包装器
- 执行顺序:装饰器外层代码 -> 被装饰函数 -> 装饰器内层代码
- 装饰器在定义时就会执行,而不是在调用时
详细执行顺序如下:
def my_decorator(func):
print("1. 装饰器外层代码:定义时就执行") # 第1步执行
def wrapper():
print("3. 装饰器内层代码:函数调用前") # 第3步执行
func() # 第4步执行
print("5. 装饰器内层代码:函数调用后") # 第5步执行
print("2. 装饰器外层代码:返回wrapper前") # 第2步执行
return wrapper
@my_decorator
def hello():
print("4. 被装饰函数执行") # 第4步执行
print("开始调用函数")
hello()
print("函数调用结束")
执行结果:
1. 装饰器外层代码:定义时就执行
2. 装饰器外层代码:返回wrapper前
开始调用函数
3. 装饰器内层代码:函数调用前
4. 被装饰函数执行
5. 装饰器内层代码:函数调用后
函数调用结束
3、执行顺序总结:
"""
演示装饰器的执行顺序
"""
def my_decorator(func):
print("1. 装饰器外层代码:定义时就执行")
def wrapper():
print("3. 装饰器内层代码:函数调用前")
func()
print("5. 装饰器内层代码:函数调用后")
print("2. 装饰器外层代码:返回wrapper前")
return wrapper
@my_decorator
def hello():
print("4. 被装饰函数执行")
print("\n=== 函数定义已完成 ===")
print("开始调用函数")
hello()
print("函数调用结束")
执行结果:
1. 装饰器外层代码:定义时就执行
2. 装饰器外层代码:返回wrapper前
=== 函数定义已完成 ===
开始调用函数
3. 装饰器内层代码:函数调用前
4. 被装饰函数执行
5. 装饰器内层代码:函数调用后
函数调用结束
详细解释顺序:
1. 定义阶段(程序加载时)
当Python解释器读到 @my_decorator 时:
- 执行装饰器外层代码(第1行输出)
- 定义内部的 wrapper 函数(但还不执行)
- 执行 return wrapper 前的代码(第2行输出)
- 将 hello 函数替换为 wrapper 函数
2. 调用阶段(执行 hello() 时)
当执行 hello() 时,实际上是在执行 wrapper():
- 执行装饰器内层代码(第3行输出)
- 调用原始的 hello 函数(第4行输出)
- 执行装饰器内层代码(第5行输出)
其他基本函数定义详解:
"""
Python函数定义详解
目的:展示Python中函数定义的各种方式、参数传递和使用技巧
"""
#######################
# 1. 基本函数定义 #
#######################
def basic_function_demo():
print("1. 基本函数定义示例")
print("-" * 50)
# 1.1 最简单的函数 - 无参数无返回值
def say_hello():
print("Hello, World!")
# 1.2 带参数的函数
def greet(name):
print(f"Hello, {name}!")
# 1.3 带返回值的函数
def add(a, b):
return a + b
# 1.4 多返回值函数
def get_person_info():
name = "张三"
age = 25
city = "北京"
return name, age, city # 返回元组
# 测试这些函数
print("基本函数调用:")
say_hello()
greet("小明")
print(f"1 + 2 = {add(1, 2)}")
name, age, city = get_person_info() # 解包返回值
print(f"个人信息:{name}, {age}岁, 来自{city}")
#######################
# 2. 参数类型 #
#######################
def parameter_types_demo():
print("\n2. 参数类型示例")
print("-" * 50)
# 2.1 位置参数
def position_args(name, age):
print(f"姓名:{name}, 年龄:{age}")
# 2.2 默认参数
def default_args(name, age=18, city="北京"):
print(f"姓名:{name}, 年龄:{age}, 城市:{city}")
# 2.3 可变参数(*args)
def variable_args(
*args,
): # *args 表示可变参数,可以传入任意数量的参数,这些参数会被打包成一个元组
print(f"收到的参数:{args}")
for arg in args:
print(f"参数值:{arg}")
# 2.4 关键字参数(**kwargs)
def keyword_args(
**kwargs,
): # **kwargs 表示关键字参数,可以传入任意数量的参数,这些参数会被打包成一个字典
print(f"收到的参数:{kwargs}")
for key, value in kwargs.items():
print(f"{key}: {value}")
# 2.5 混合使用各种参数
def mixed_args(
name, age=18, *args, **kwargs
): # 混合使用各种参数,name,age作为位置参数,*args作为可变参数,**kwargs作为关键字参数
print(f"姓名:{name}")
print(f"年龄:{age}")
print(f"可变参数:{args}")
print(f"关键字参数:{kwargs}")
# 测试这些函数
print("不同参数类型的使用:")
position_args("小明", 20)
default_args("小红") # 使用默认参数
default_args("小李", 25, "上海") # 覆盖默认参数
variable_args(1, 2, 3, "hello") # 可变参数
keyword_args(
name="小王", age=22, city="广州"
) # 关键字参数,name,age,city作为键,小王,22,广州作为值 ; 使用名称=值的形式传参
mixed_args("小张", 20, 1, 2, 3, city="深圳", hobby="编程") # 混合参数
#######################
# 3. 参数检查 #
#######################
def parameter_checking_demo():
print("\n3. 参数检查示例")
print("-" * 50)
# 3.1 基本类型检查
def check_type(value: int) -> bool:
"""
检查参数是否为整数
:param value: 要检查的值
:return: 是否为整数
"""
if not isinstance(value, int):
raise TypeError(f"参数必须是整数,而不是{type(value)}")
return True
# 3.2 值范围检查
def check_age(age: int) -> None:
"""
检查年龄是否合法
:param age: 年龄
:raises ValueError: 年龄不在有效范围内
"""
if not 0 <= age <= 150:
raise ValueError(f"年龄必须在0-150之间,当前值:{age}")
# 3.3 参数默认值检查
def connect_database(host="localhost", port=3306, timeout=30):
"""
检查数据库连接参数
"""
if not isinstance(port, int):
raise TypeError("端口必须是整数")
if not 0 < port < 65536:
raise ValueError("端口必须在1-65535之间")
if timeout <= 0:
raise ValueError("超时时间必须大于0")
# 测试参数检查
print("参数检查示例:")
try:
check_type("123") # 应该抛出TypeError
except TypeError as e:
print(f"类型检查错误:{e}")
try:
check_age(200) # 应该抛出ValueError
except ValueError as e:
print(f"值检查错误:{e}")
try:
connect_database(port="8080") # 应该抛出TypeError
except TypeError as e:
print(f"参数检查错误:{e}")
#######################
# 4. 空函数和装饰器 #
#######################
def empty_and_decorator_demo():
print("\n4. 空函数和装饰器示例")
print("-" * 50)
# 4.1 空函数
def pass_demo():
pass # 空函数,什么都不做
# 4.2 简单装饰器
def log_decorator(func): # 装饰器,用于在函数调用前后添加日志
def wrapper(*args, **kwargs):
print(f"调用函数:{func.__name__}")
result = func(*args, **kwargs)
print(f"函数调用完成:{func.__name__}")
return result
return wrapper
# 使用装饰器
@log_decorator
def test_function(x, y):
return x + y
# 测试空函数和装饰器
print("空函数和装饰器示例:")
pass_demo() # 不做任何事
result = test_function(10, 20) # 带日志的函数调用
print(f"计算结果:{result}")
#######################
# 5. 函数使用技巧 #
#######################
def function_tips():
print("\n5. 函数使用技巧")
print("-" * 50)
print("1) 函数命名规范:")
print(" - 使用小写字母和下划线")
print(" - 动词开头:get_xxx, set_xxx, is_xxx")
print(" - 见名知义:calculate_total而不是calc")
print("\n2) 参数设计原则:")
print(" - 参数数量不要太多(建议不超过5个)")
print(" - 必选参数在前,可选参数在后")
print(" - 灵活使用*args和**kwargs")
print("\n3) 返回值建议:")
print(" - 保持返回值类型一致")
print(" - 使用元组返回多个值")
print(" - 错误时使用异常而不是返回特殊值")
print("\n4) 文档和注释:")
print(" - 使用文档字符串说明功能")
print(" - 注释复杂的逻辑")
print(" - 说明参数类型和返回值")
# 运行所有示例
if __name__ == "__main__":
basic_function_demo()
parameter_types_demo()
parameter_checking_demo()
empty_and_decorator_demo()
function_tips()
打印结果
1. 基本函数定义示例
--------------------------------------------------
基本函数调用:
Hello, World!
Hello, 小明!
1 + 2 = 3
个人信息:张三, 25岁, 来自北京
2. 参数类型示例
--------------------------------------------------
不同参数类型的使用:
姓名:小明, 年龄:20
姓名:小红, 年龄:18, 城市:北京
姓名:小李, 年龄:25, 城市:上海
收到的参数:(1, 2, 3, 'hello')
参数值:1
参数值:2
参数值:3
参数值:hello
收到的参数:{'name': '小王', 'age': 22, 'city': '广州'}
name: 小王
age: 22
city: 广州
姓名:小张
年龄:20
可变参数:(1, 2, 3)
关键字参数:{'city': '深圳', 'hobby': '编程'}
3. 参数检查示例
--------------------------------------------------
参数检查示例:
类型检查错误:参数必须是整数,而不是<class 'str'>
值检查错误:年龄必须在0-150之间,当前值:200
参数检查错误:端口必须是整数
4. 空函数和装饰器示例
--------------------------------------------------
空函数和装饰器示例:
调用函数:test_function
函数调用完成:test_function
计算结果:30
5. 函数使用技巧
--------------------------------------------------
1) 函数命名规范:
- 使用小写字母和下划线
- 动词开头:get_xxx, set_xxx, is_xxx
- 见名知义:calculate_total而不是calc
2) 参数设计原则:
- 参数数量不要太多(建议不超过5个)
- 必选参数在前,可选参数在后
- 灵活使用*args和**kwargs
3) 返回值建议:
- 保持返回值类型一致
- 使用元组返回多个值
- 错误时使用异常而不是返回特殊值
4) 文档和注释:
- 使用文档字符串说明功能
- 注释复杂的逻辑
- 说明参数类型和返回值