534 lines
14 KiB
Python
534 lines
14 KiB
Python
# 函数
|
||
# 语法
|
||
# def function_name(parameters):
|
||
# """文档字符串(可选)"""
|
||
# # 函数体
|
||
# return value # 可选
|
||
# 组成部分:
|
||
|
||
# 函数名:标识函数的名称,遵循变量命名规则
|
||
# 参数列表:括号中的变量名,表示调用时可以传递的值
|
||
# 函数体:缩进的代码块,包含具体的实现逻辑
|
||
# 返回值:通过return语句将结果返回给调用者
|
||
|
||
|
||
# 定义
|
||
# Python要求函数体代码必须缩进(通常为4个空格)
|
||
def square(x):
|
||
return x**2
|
||
|
||
|
||
print(square(2))
|
||
|
||
|
||
# 可以在函数内部定义文档字符串(docstring),用于描述函数的作用:
|
||
def square(x):
|
||
"""
|
||
求x的平方
|
||
"""
|
||
return x**2
|
||
|
||
|
||
# 参数类型
|
||
|
||
|
||
# 1.位置参数:位置参数是最基本的参数类型,调用函数时按照参数的位置一一对应传递。
|
||
# 特点:
|
||
# 参数的顺序很重要,不能颠倒
|
||
# 调用时必须提供所有必需参数
|
||
# 参数按位置顺序赋值
|
||
def add(a, b):
|
||
return a + b
|
||
|
||
|
||
result = add(1, 2)
|
||
print(result)
|
||
|
||
|
||
# 2.关键字参数:关键字参数允许你在调用函数时,通过"参数名=值"的形式传递参数,这样参数顺序就可以任意。
|
||
# 优点:
|
||
# 不用记住参数顺序
|
||
# 增加代码可读性
|
||
# 很适合带有默认参数的函数
|
||
def student_info(name, age, city):
|
||
return f"姓名{name},年龄{age},城市{city}"
|
||
|
||
|
||
print(student_info(age=18, name="小明", city="beijing"))
|
||
|
||
|
||
# 3.默认参数:默认参数是指在定义函数时为参数提供默认值。在调用函数时,如果对应参数没有传值,则会使用这个默认值。
|
||
# 注意事项:
|
||
# 默认参数必须出现在非默认参数之后
|
||
# 默认参数常用于大多数情况下使用同一个值的场景
|
||
def greet(name, msg="Hello"):
|
||
print(f"{msg}, {name}!")
|
||
|
||
|
||
greet("Alice") # 输出:Hello, Alice!
|
||
greet("Bob", msg="Hi") # 输出:Hi, Bob!
|
||
|
||
|
||
# 4.可变参数:可变参数用于当函数需要接受不定数量的位置参数时,通过在参数前加*来实现。
|
||
# 特点:
|
||
# *args不是必须叫args,但习惯上都叫args
|
||
# 所有传入的位置参数被收集到一个元组中
|
||
# 特别适合参数数量不确定时使用
|
||
def show_args(*args):
|
||
print(f"参数类型{type(args)}")
|
||
print(f"参数内容{args}")
|
||
|
||
|
||
show_args(1, 2, 4) # 参数类型<class 'tuple'> 参数内容(1, 2, 4)
|
||
show_args([1, 2, 3]) # 参数类型<class 'tuple'> 参数内容([1, 2, 3],)
|
||
show_args("123") # 参数类型<class 'tuple'> 参数内容('123',)
|
||
|
||
|
||
# 应用
|
||
def make_pizza(*foods):
|
||
print("制作一个披萨,添加以下配料:")
|
||
for food in foods:
|
||
print(f"-{food}")
|
||
|
||
|
||
make_pizza("蘑菇", "青椒", "芝士")
|
||
|
||
|
||
# 5.关键字可变参数:关键字可变参数允许函数接收任意数量的"关键字参数"。这些参数会被自动收集到一个字典中。
|
||
# **kwargs不是必须叫kwargs,习惯上都这么命名
|
||
# 多余的关键字参数汇集到一个字典里
|
||
# 常用于不确定会传入哪些命名参数时
|
||
def show_kwargs(**kwargs):
|
||
print(f"参数类型{type(kwargs)}")
|
||
print(f"参数内容{kwargs}")
|
||
|
||
|
||
show_kwargs(name="1", age=18) # 参数类型<class 'dict'> 参数内容{'name': '1', 'age': 18}
|
||
|
||
|
||
# 综合
|
||
def demo(a, *args, b=1, **kwargs):
|
||
print(f"a:{a}") # a:小明
|
||
print(f"args:{args}") # args:(20, 30)
|
||
print(f"b:{b}") # b:北京
|
||
print(f"kwargs:{kwargs}") # kwargs:{'x': '8', 'y': 10}
|
||
|
||
|
||
demo("小明", 20, 30, b="北京", x="8", y=10)
|
||
|
||
|
||
# 参数解包
|
||
# 在Python中,*和**不仅可以用于函数定义时接收可变参数,还可以用于参数解包。
|
||
def greet(name, age, city):
|
||
print(f"姓名: {name}, 年龄: {age}, 城市: {city}")
|
||
|
||
|
||
# 使用 * 解包列表
|
||
person_info = ["Alice", 25, "北京"]
|
||
greet(*person_info) # 等同于 greet("Alice", 25, "北京")
|
||
|
||
# 使用 * 解包元组
|
||
data = ("Bob", 30, "上海")
|
||
greet(*data) # 等同于 greet("Bob", 30, "上海")
|
||
|
||
|
||
# ** 用于解包字典类型
|
||
def create_profile(name, age, city, occupation):
|
||
print(f"姓名: {name}, 年龄: {age}, 城市: {city}, 职业: {occupation}")
|
||
|
||
|
||
# 使用 ** 解包字典
|
||
profile_data = {"name": "Charlie", "age": 28, "city": "广州", "occupation": "工程师"}
|
||
create_profile(**profile_data)
|
||
|
||
|
||
# 混合使用
|
||
def complex_function(a, b, c, d, e):
|
||
print(f"a: {a}, b: {b}, c: {c}, d: {d},e:{e}")
|
||
|
||
|
||
list_data = [1, 2]
|
||
dict_data = {"d": 10, "e": "小明"}
|
||
|
||
complex_function(*list_data, True, **dict_data)
|
||
|
||
|
||
# 返回值
|
||
# 返回单个值
|
||
def get_formatted_name(first_name, last_name):
|
||
full_name = f"{first_name}{last_name}"
|
||
return full_name
|
||
|
||
|
||
musician = get_formatted_name("张", "三")
|
||
print(musician) # 张三
|
||
|
||
|
||
# 返回多个值
|
||
def operate_numbers(a, b):
|
||
sum_result = a + b
|
||
diff_result = a - b
|
||
product_result = a * b
|
||
return sum_result, diff_result, product_result
|
||
|
||
|
||
# 接收元组
|
||
result = operate_numbers(10, 5)
|
||
print(result) # (15, 5, 50)
|
||
|
||
# 解包返回值
|
||
sum_val, diff_val, product_val = operate_numbers(10, 5)
|
||
print(f"Sum: {sum_val}, Difference: {diff_val}, Product: {product_val}")
|
||
|
||
|
||
def create_student_report(name, **scores):
|
||
report = {"name": name, "total_score": 0, "subjects": scores}
|
||
|
||
for key, score in scores.items():
|
||
print(f"{key}:{score}")
|
||
|
||
# # 计算总分
|
||
for score in scores.values():
|
||
report["total_score"] += score
|
||
|
||
# # 计算平均分
|
||
report["average"] = report["total_score"] / len(scores)
|
||
return report
|
||
|
||
|
||
student = create_student_report("Alice", **{"math": 95, "science": 87, "english": 92})
|
||
print(student)
|
||
|
||
|
||
# 函数文档和注释
|
||
# 文档字符串(Docstring)
|
||
# 文档字符串是紧跟在函数定义后,用三引号包裹的字符串,用于描述函数的用途、参数说明、返回值类型等。
|
||
def calculate_circle_area(radius):
|
||
"""
|
||
计算圆的面积
|
||
|
||
参数:
|
||
radius (float): 圆的半径
|
||
|
||
返回:
|
||
float: 圆的面积
|
||
"""
|
||
import math
|
||
|
||
return math.pi * radius**2
|
||
|
||
|
||
# 查看文档字符串
|
||
print(calculate_circle_area.__doc__)
|
||
# 查看函数帮助
|
||
help(calculate_circle_area)
|
||
|
||
|
||
# 注释
|
||
# 注释是用#开头的说明性文字,用于解释代码片段的实现思路、注意事项或特殊用途
|
||
def safe_divide(a, b):
|
||
"""安全的除法函数"""
|
||
# 检查除数是否为零
|
||
if b == 0:
|
||
return "错误:除数不能为零"
|
||
|
||
# 执行除法运算
|
||
result = a / b
|
||
return result
|
||
|
||
|
||
# 变量作用域
|
||
# 作用域类型
|
||
# 作用域类型 描述 示例
|
||
# 局部作用域 变量在函数内部定义,只能在该函数内部使用 函数内的局部变量
|
||
# 全局作用域 变量在所有函数体外部定义,可以在整个模块里访问 模块级别的变量
|
||
# 嵌套作用域 内部函数可以访问外部函数中的变量 闭包中的外部变量
|
||
# 内置作用域 Python预先定义的内置标识符 len, range, print等
|
||
|
||
# LEGB原则
|
||
# Python采用LEGB原则查找变量:
|
||
|
||
# L: Local(本地作用域)
|
||
# E: Enclosing(嵌套函数的外部函数作用域)
|
||
# G: Global(全局作用域)
|
||
# B: Built-in(内置作用域)
|
||
name = "全局变量"
|
||
|
||
|
||
def outer():
|
||
name = "外部函数变量"
|
||
|
||
def inner():
|
||
name = "内部函数变量"
|
||
print(f"inner:{name}") # 输出"内部函数变量"
|
||
|
||
inner()
|
||
print(f"outer:{name}") # 输出"外部函数变量"
|
||
|
||
|
||
outer()
|
||
print(name) # 输出"全局变量"
|
||
|
||
# global关键字
|
||
# global关键字允许我们在函数内部声明某个变量为全局变量,从而可以在函数内部修改它。
|
||
# 注意事项:
|
||
# 没有global关键字时,在函数内对一个变量赋值会将其作为局部变量处理
|
||
# 使用global后,函数内的变量就直接引用外部全局变量
|
||
counter = 0
|
||
|
||
|
||
def increment():
|
||
global counter # 引用外部全局变量
|
||
counter += 1
|
||
|
||
|
||
increment()
|
||
increment()
|
||
print(counter) # 2
|
||
|
||
|
||
# nonlocal关键字
|
||
# nonlocal关键字用于在嵌套函数中声明变量来自最近的一层非全局作用域(外层函数)。
|
||
|
||
|
||
def outer():
|
||
x = "local"
|
||
|
||
def inner():
|
||
nonlocal x # 声明 x 来自外层作用域
|
||
x = "nonlocal"
|
||
print(f"inner:{x}") # inner:nonlocal
|
||
|
||
inner()
|
||
print(f"outer:{x}") # outer:nonlocal
|
||
|
||
|
||
outer()
|
||
|
||
|
||
# 高级函数特性
|
||
# 8.1.函数作为参数
|
||
# 函数不仅可以像变量一样赋值给其他变量,还可以作为参数传递给其他函数。
|
||
def apply_operation(numbers, operation):
|
||
"""对数字列表应用操作"""
|
||
return [operation(x) for x in numbers]
|
||
|
||
|
||
def square(x):
|
||
return x**2
|
||
|
||
|
||
def double(x):
|
||
return x * 2
|
||
|
||
|
||
numbers = [1, 2, 3, 4, 5]
|
||
squared = apply_operation(numbers, square)
|
||
doubled = apply_operation(numbers, double)
|
||
|
||
print(squared) # [1, 4, 9, 16, 25]
|
||
print(doubled) # [2, 4, 6, 8, 10]
|
||
|
||
|
||
# 嵌套函数
|
||
# 嵌套函数是指在一个函数内部定义另一个函数。内部函数可以访问外部函数的参数和局部变量。
|
||
# 用途:
|
||
# 实现闭包(closure)
|
||
# 将某些逻辑封装到本地作用域
|
||
# 作为工厂函数创建带记忆的数据
|
||
def greet(name):
|
||
def format_message():
|
||
return f"Hello, {name}!"
|
||
|
||
return format_message()
|
||
|
||
|
||
print(greet("Alice")) # Hello, Alice!
|
||
|
||
|
||
# 闭包
|
||
# 闭包是指一个函数可以"记住"它被创建时的环境,即使在其外部函数已经执行完毕后,内部函数依然能够访问其外部作用域的变量。
|
||
# 特点:
|
||
# 内部函数引用了外部函数的局部变量
|
||
# 外部函数返回内部函数
|
||
# 内部函数可以访问外部函数的变量,即使外部函数已经执行完毕
|
||
|
||
|
||
def make_multiplier(factor):
|
||
def multiplier(x):
|
||
return x * factor
|
||
|
||
return multiplier
|
||
|
||
|
||
double = make_multiplier(2)
|
||
triple = make_multiplier(3)
|
||
|
||
print(double(5)) # 10
|
||
print(triple(5)) # 15
|
||
|
||
|
||
# 装饰器
|
||
# 装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。装饰器常用于在不修改原函数代码的情况下,动态地为其添加新功能。
|
||
def my_decorator(func):
|
||
def wrapper():
|
||
print("函数执行前")
|
||
func()
|
||
print("函数执行后")
|
||
|
||
return wrapper
|
||
|
||
|
||
@my_decorator
|
||
def say_hello():
|
||
print("Hello")
|
||
|
||
|
||
say_hello() # 函数执行前 Hello 函数执行后
|
||
|
||
|
||
# 带参数的装饰器
|
||
def logger(func):
|
||
def wrapper(*args, **kwargas):
|
||
print(f"调用函数{func.__name__},参数:{args}{kwargas}")
|
||
return func(*args, **kwargas)
|
||
|
||
return wrapper
|
||
|
||
|
||
@logger
|
||
def add(x, y, **kwargs):
|
||
a = x + y
|
||
b = sum(kwargs.values())
|
||
return a + b
|
||
|
||
|
||
print(add(1, 2, a=3, b=4))
|
||
|
||
|
||
# 匿名函数 (Lambda)
|
||
# 匿名函数(lambda表达式)是一种快速定义简单函数的方法,通常用于一些无需命名、只用一次的小函数场景。
|
||
# lambda 参数1, 参数2, ... : 表达式
|
||
# 基本 lambda 函数
|
||
# 特点:
|
||
# 只能包含表达式,不能包含语句
|
||
# 自动返回表达式的值
|
||
# 适合简单的函数逻辑
|
||
square = lambda x: x**2
|
||
print(square(5)) # 25
|
||
|
||
# 在排序中使用
|
||
students = [
|
||
{"name": "Alice", "grade": 85},
|
||
{"name": "Bob", "grade": 92},
|
||
{"name": "Charlie", "grade": 78},
|
||
]
|
||
|
||
# 按成绩排序
|
||
sorted_students = sorted(students, key=lambda x: x["grade"])
|
||
print(sorted_students)
|
||
|
||
# 在 map 中使用
|
||
numbers = [1, 2, 3, 4, 5]
|
||
squared = list(map(lambda x: x**2, numbers))
|
||
print(squared) # [1, 4, 9, 16, 25]
|
||
|
||
# 在 filter 中使用
|
||
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
|
||
print(even_numbers) # [2, 4]
|
||
|
||
|
||
# 递归函数
|
||
# 递归函数是函数调用自身来解决问题的方法,适合解决可以被分解为规模更小、相似子问题的任务。
|
||
# def func(params):
|
||
# if 终止条件:
|
||
# return 结果
|
||
# else:
|
||
# # 递归调用自身
|
||
# return func(更小规模的参数)
|
||
# 注意事项:
|
||
# 必须设置基线条件(终止条件),避免无限递归
|
||
# 递归深度有限制,可能导致栈溢出
|
||
# 对于规模较大的问题效率较低
|
||
def factorial(n):
|
||
"""计算阶乘的递归函数"""
|
||
if n == 0 or n == 1:
|
||
return 1
|
||
else:
|
||
return n * factorial(n - 1)
|
||
|
||
|
||
print(factorial(5)) # 120
|
||
|
||
|
||
def fibonacci(n):
|
||
"""计算斐波那契数列"""
|
||
if n <= 1:
|
||
return n
|
||
else:
|
||
return fibonacci(n - 1) + fibonacci(n - 2)
|
||
|
||
|
||
for i in range(10):
|
||
print(fibonacci(i), end=" ") # 0 1 1 2 3 5 8 13 21 34
|
||
|
||
|
||
# 错误处理
|
||
# 在函数中进行错误处理可以提升程序的健壮性和用户体验。
|
||
# def 函数名(参数):
|
||
# try:
|
||
# # 可能抛出异常的操作
|
||
# except 异常类型1:
|
||
# # 异常类型1的处理代码
|
||
# except 异常类型2:
|
||
# # 异常类型2的处理代码
|
||
# else:
|
||
# # 没有发生异常时的代码
|
||
# finally:
|
||
# # 无论是否发生异常都执行
|
||
def safe_divide(a, b):
|
||
"""安全的除法函数"""
|
||
try:
|
||
result = a / b
|
||
except ZeroDivisionError:
|
||
return "错误:除数不能为零"
|
||
except TypeError:
|
||
return "错误:参数类型不正确"
|
||
else:
|
||
return result
|
||
|
||
|
||
print(safe_divide(10, 2)) # 5.0
|
||
print(safe_divide(10, 0)) # 错误:除数不能为零
|
||
print(safe_divide(10, "a")) # 错误:参数类型不正确
|
||
|
||
|
||
# 最佳实践
|
||
# 14.1.函数设计原则
|
||
# 单一职责:每个函数只做一件事
|
||
# 描述性命名:使用有意义的函数名
|
||
# 保持简短:函数应该简短且专注
|
||
# 使用文档字符串:为函数添加适当的文档
|
||
# 避免副作用:函数不应该修改外部状态,除非明确需要
|
||
# 14.2.参数设计
|
||
# 合理使用默认参数:使函数更灵活
|
||
# 参数顺序:位置参数在前,默认参数在后
|
||
# 使用*args和kwargs**:提高函数灵活性
|
||
# 参数验证:在函数开始处验证参数
|
||
# 14.3.错误处理
|
||
# 处理异常:在适当的地方处理可能的错误
|
||
# 提供有意义的错误信息:帮助用户理解问题
|
||
# 使用try-except:优雅地处理异常情况
|
||
# 14.4.性能考虑
|
||
# 避免不必要的计算:将重复计算提取到函数外部
|
||
# 使用生成器:对于大数据集使用生成器函数
|
||
# 缓存结果:对于昂贵的计算考虑缓存
|
||
# 15.总结
|
||
# 函数是Python编程的核心概念,掌握函数的定义、调用、参数传递、返回值、作用域等概念对于编写高质量的Python代码至关重要。通过合理使用函数,我们可以:
|
||
|
||
# 提高代码的可重用性和可维护性
|
||
# 实现模块化编程
|
||
# 简化复杂问题的解决
|
||
# 提高代码的可读性和可测试性
|
||
# 掌握函数的高级特性如闭包、装饰器、递归等,能够让我们编写更加灵活和强大的Python程序。
|