第三章:函数与模块
3.1 函数基础
3.1.1 函数的概念
函数是Python中组织和重用代码的基本方式,就像生活中的各种工具一样,能够帮助我们高效完成特定任务。
# 函数的基本概念
# 生活中的函数类比:洗衣机
# 输入:脏衣服、洗衣粉、水
# 处理过程:浸泡、搓洗、漂洗、脱水
# 输出:干净衣服
# Python内置函数示例
print("这是一个内置函数的例子") # print是Python的内置函数
length = len("Python编程") # len()计算对象的长度
print(f"字符串长度:{length}")
max_value = max(10, 45, 32, 89, 12) # max()找出最大值
print(f"最大值是:{max_value}")
# 函数的优势
# 1. 代码复用:一次编写,多次使用
# 2. 模块化:将复杂问题分解为小问题
# 3. 可维护性:修改函数而不影响其他代码
# 4. 抽象化:使用函数而不需要了解其内部实现
3.1.2 定义与调用函数
在Python中,我们使用def
关键字来定义函数,就像给一系列操作起一个名字,以便随时调用。
# 函数定义与调用
# 定义一个简单的问候函数
def greet(name):
"""这是函数的文档字符串,用于说明函数的功能
本函数用于向指定的人发出问候
参数:
name:要问候的人的名字
"""
print(f"你好,{name}!祝你今天愉快!")
# 调用函数
greet("小明") # 输出:你好,小明!祝你今天愉快!
greet("小红") # 输出:你好,小红!祝你今天愉快!
# 函数命名规范
# 1. 使用小写字母和下划线(snake_case)
# 2. 名称应当反映函数的功能
# 3. 避免使用Python的关键字和内置函数名
# 好的函数名示例
def calculate_total_price(unit_price, quantity):
return unit_price * quantity
def is_adult(age):
return age >= 18
# 文档字符串的重要性
def calculate_circle_area(radius):
"""计算圆的面积
参数:
radius:圆的半径,单位为米
返回:
圆的面积,单位为平方米
"""
return 3.14159 * radius ** 2
# 查看函数的文档
help(calculate_circle_area) # 显示函数的文档字符串
3.1.3 函数的返回值
函数可以通过return
语句返回结果,就像我们在商店购物后收到商品一样。
# 函数的返回值
# 返回单个值的函数
def calculate_discount_price(original_price, discount_rate):
"""计算折扣后的价格
参数:
original_price:商品原价
discount_rate:折扣率,如0.8表示八折
返回:
折扣后的价格
"""
discount_price = original_price * discount_rate
return discount_price
# 调用有返回值的函数
shirt_original_price = 120
shirt_discount_price = calculate_discount_price(shirt_original_price, 0.8)
print(f"衬衫折扣价:{shirt_discount_price}元") # 输出:衬衫折扣价:96.0元
# 返回多个值的函数
def calculate_rectangle_properties(length, width):
"""计算矩形的周长和面积
参数:
length:矩形的长
width:矩形的宽
返回:
包含周长和面积的元组
"""
perimeter = 2 * (length + width)
area = length * width
return perimeter, area # 返回多个值(实际上是返回一个元组)
# 接收多个返回值
living_room_length = 4
living_room_width = 3.5
living_room_perimeter, living_room_area = calculate_rectangle_properties(living_room_length, living_room_width)
print(f"客厅周长:{living_room_perimeter}米,客厅面积:{living_room_area}平方米")
# 无返回值函数(实际上返回None)
def display_separator(character="-", length=30):
"""打印一条分隔线
参数:
character:分隔线使用的字符
length:分隔线的长度
"""
print(character * length)
result = display_separator("=", 20) # 打印:====================
print(f"函数返回值:{result}") # 输出:函数返回值:None
# 提前返回
def calculate_square_root(number):
"""计算非负数的平方根
参数:
number:要计算平方根的数字
返回:
如果是非负数,返回其平方根;否则返回None
"""
if number < 0:
print("不能计算负数的平方根")
return None # 提前返回
return number ** 0.5
print(calculate_square_root(16)) # 输出:4.0
print(calculate_square_root(-4)) # 输出:不能计算负数的平方根 然后是 None
3.2 函数参数
3.2.1 位置参数
位置参数是最基本的参数类型,它们的值由调用函数时的位置决定,就像我们排队一样,位置很重要。
# 位置参数
# 基本位置参数示例
def calculate_rectangle_area(length, width):
"""计算长方形的面积
参数:
length:长方形的长
width:长方形的宽
返回:
长方形的面积
"""
return length * width
# 按照位置传递参数
living_room_area = calculate_rectangle_area(4, 3.5)
print(f"客厅面积:{living_room_area}平方米") # 输出:客厅面积:14.0平方米
# 参数顺序很重要
bedroom_area = calculate_rectangle_area(3, 4) # 与上面相同的结果,因为乘法满足交换律
kitchen_area = calculate_rectangle_area(2.5, 2) # 长=2.5,宽=2
# 参数数量必须匹配
# 错误示例:calculate_rectangle_area(3) # 会报错:缺少一个必需的位置参数'width'
# 错误示例:calculate_rectangle_area(3, 4, 5) # 会报错:给了3个位置参数,但只接受2个
# 参数传递机制:值传递与引用传递
def modify_number(x):
x = x + 10
print(f"函数内部x的值:{x}")
number = 5
modify_number(number)
print(f"函数调用后number的值:{number}") # 输出:函数调用后number的值:5(不变)
def modify_list(lst):
lst.append(4)
print(f"函数内部列表的值:{lst}")
my_list = [1, 2, 3]
modify_list(my_list)
print(f"函数调用后列表的值:{my_list}") # 输出:函数调用后列表的值:[1, 2, 3, 4](改变)
3.2.2 默认参数
默认参数让函数在未提供某些参数时使用预设的值,就像餐厅的套餐一样,有默认的配菜,但你也可以选择更换。
# 默认参数
# 带默认参数的函数
def calculate_interest(principal, annual_rate=0.05, years=1):
"""计算单利
参数:
principal:存款金额
annual_rate:年利率,默认为5%
years:存款年数,默认为1年
返回:
本金和利息之和
"""
interest = principal * annual_rate * years
return principal + interest
# 使用默认参数
deposit1 = 10000
after_one_year = calculate_interest(deposit1) # 使用默认年利率5%和默认年数1年
print(f"一年后金额:{after_one_year}元") # 输出:一年后金额:10500.0元
# 覆盖部分默认参数
after_two_years = calculate_interest(deposit1, years=2) # 覆盖年数参数,使用默认年利率
print(f"两年后金额:{after_two_years}元") # 输出:两年后金额:11000.0元
# 覆盖所有默认参数
high_rate_two_years = calculate_interest(deposit1, 0.08, 2) # 本金10000,年利率8%,2年
print(f"高利率两年后金额:{high_rate_two_years}元") # 输出:高利率两年后金额:11600.0元
# 默认参数的陷阱:使用可变对象作为默认值
# 错误示例
def add_to_list(element, target_list=[]):
target_list.append(element)
return target_list
result1 = add_to_list(1) # 创建列表[1]
result2 = add_to_list(2) # 预期是[2],但实际是[1, 2],因为默认列表被修改了
print(f"结果1:{result1},结果2:{result2}") # 输出:结果1:[1, 2],结果2:[1, 2]
# 正确做法
def safe_add_to_list(element, target_list=None):
if target_list is None:
target_list = []
target_list.append(element)
return target_list
result3 = safe_add_to_list(3) # 创建新列表[3]
result4 = safe_add_to_list(4) # 创建另一个新列表[4]
print(f"结果3:{result3},结果4:{result4}") # 输出:结果3:[3],结果4:[4]
3.2.3 关键字参数
关键字参数允许通过参数名指定参数值,使函数调用更清晰,就像在点餐时明确指定每道菜的名称。
# 关键字参数
# 使用关键字参数调用函数
def make_milk_tea(cup_size="中杯", sweetness="半糖", temperature="常温", topping="无"):
"""制作奶茶
参数:
cup_size:小杯、中杯或大杯
sweetness:无糖、微糖、半糖、全糖
temperature:热饮、温饮、常温、去冰、少冰
topping:珍珠、椰果、布丁等
返回:
奶茶描述
"""
return f"{cup_size}奶茶,{sweetness},{temperature},加料:{topping}"
# 使用位置参数(不推荐,因为参数多时容易混淆)
milk_tea1 = make_milk_tea("大杯", "微糖", "少冰", "珍珠")
print(f"奶茶1:{milk_tea1}")
# 使用关键字参数(更清晰)
milk_tea2 = make_milk_tea(cup_size="大杯", sweetness="微糖", temperature="少冰", topping="珍珠")
print(f"奶茶2:{milk_tea2}")
# 关键字参数的顺序不重要
milk_tea3 = make_milk_tea(temperature="热饮", topping="布丁", cup_size="小杯", sweetness="无糖")
print(f"奶茶3:{milk_tea3}")
# 混合使用位置参数和关键字参数
# 位置参数必须在关键字参数之前
milk_tea4 = make_milk_tea("中杯", "全糖", temperature="热饮", topping="椰果")
print(f"奶茶4:{milk_tea4}")
# 错误示例:关键字参数后面不能有位置参数
# milk_tea5 = make_milk_tea(cup_size="大杯", "全糖", temperature="热饮", topping="椰果") # 会报错
# 关键字参数的优势
# 1. 提高代码可读性
# 2. 可以跳过有默认值的参数
# 3. 参数顺序灵活
milk_tea6 = make_milk_tea(topping="珍珠") # 只指定加料,其他使用默认值
print(f"奶茶6:{milk_tea6}")
3.2.4 可变参数
可变参数允许函数接收不定数量的参数,就像自助餐一样,你可以根据需要取用不同数量和种类的食物。
# 可变参数
# *args:接收任意数量的位置参数
def calculate_average(*scores):
"""计算多个分数的平均值
参数:
*scores:任意数量的分数
返回:
所有分数的平均值,如果没有分数则返回0
"""
if not scores: # 如果没有提供分数
return 0
return sum(scores) / len(scores)
# 调用带可变位置参数的函数
chinese_average = calculate_average(85, 92, 78, 90, 88)
print(f"语文平均分:{chinese_average}") # 输出:语文平均分:86.6
math_scores = [76, 89, 95, 82]
math_average = calculate_average(*math_scores) # 解包列表作为位置参数
print(f"数学平均分:{math_average}") # 输出:数学平均分:85.5
# **kwargs:接收任意数量的关键字参数
def create_profile(**info):
"""创建个人档案
参数:
**info:个人信息,如姓名、年龄、职业等
返回:
格式化的个人档案字符串
"""
profile = "个人档案:\n"
for key, value in info.items():
profile += f"- {key}: {value}\n"
return profile
# 调用带可变关键字参数的函数
xiaoming_profile = create_profile(姓名="小明", 年龄=25, 职业="工程师", 城市="北京")
print(xiaoming_profile)
xiaohong_info = {"姓名": "小红", "年龄": 28, "职业": "设计师"}
xiaohong_profile = create_profile(**xiaohong_info, 城市="上海") # 解包字典并添加额外参数
print(xiaohong_profile)
# 混合使用各类参数
def make_pizza(size, crust_type="普通", *toppings, **options):
"""制作披萨
参数:
size:披萨尺寸(必需的位置参数)
crust_type:面饼类型(默认参数)
*toppings:任意数量的配料(可变位置参数)
**options:其他选项如是否加厚芝士等(可变关键字参数)
返回:
披萨描述
"""
pizza_description = f"{size}寸{crust_type}面饼披萨\n"
if toppings:
pizza_description += "配料:\n"
for topping in toppings:
pizza_description += f"- {topping}\n"
if options:
pizza_description += "其他选项:\n"
for option, value in options.items():
pizza_description += f"- {option}: {value}\n"
return pizza_description
# 调用混合参数类型的函数
my_pizza = make_pizza(12, "全麦", "火腿", "蘑菇", "青椒", "洋葱", 加厚芝士=True, 加双倍酱料=True)
print(my_pizza)
# 参数顺序规则:位置参数 -> 默认参数 -> 可变位置参数 -> 可变关键字参数
3.2.5 参数解包
参数解包允许我们将序列或字典中的值分别传递给函数的参数,就像拆开一个包裹,将里面的物品分别摆放。
# 参数解包
# 序列解包为位置参数
def calculate_triangle_area(base, height):
"""计算三角形面积
参数:
base:三角形的底边长
height:三角形的高
返回:
三角形的面积
"""
return base * height / 2
# 使用元组解包
triangle_dimensions = (6, 4) # 底=6,高=4
area1 = calculate_triangle_area(*triangle_dimensions) # 等同于calculate_triangle_area(6, 4)
print(f"三角形面积:{area1}平方米") # 输出:三角形面积:12.0平方米
# 使用列表解包
another_triangle = [8, 5]
area2 = calculate_triangle_area(*another_triangle)
print(f"另一个三角形面积:{area2}平方米") # 输出:另一个三角形面积:20.0平方米
# 字典解包为关键字参数
def create_user(username, email, age=18):
"""创建用户信息
参数:
username:用户的名称
email:用户的邮箱地址
age:用户的年龄,默认为18
返回:
用户信息字符串
"""
return f"用户名:{username},邮箱:{email},年龄:{age}"
# 使用字典解包
user_info = {"username": "张三", "email": "zhangsan@example.com", "age": 25}
user1 = create_user(**user_info) # 等同于create_user(username="张三", email="zhangsan@example.com", age=25)
print(user1)
# 部分解包
basic_info = {"username": "李四", "email": "lisi@example.com"}
user2 = create_user(**basic_info) # 使用默认年龄
print(user2)
# 解包与额外参数结合
user3 = create_user(**basic_info, age=30) # 解包基本信息并覆盖年龄
print(user3)
# 解包的应用场景
# 1. 函数参数传递
# 2. 配置选项管理
# 3. API调用
# 示例:配置选项管理
def connect_database(host="localhost", port=3306, username="root", password="", database="test"):
"""连接到数据库
参数:
host:数据库主机地址
port:数据库端口
username:数据库用户名
password:数据库密码
database:要连接的数据库名
返回:
连接信息字符串(实际应返回连接对象)
"""
return f"连接到 {host}:{port}/{database},用户名:{username}"
# 从配置文件加载的配置
dev_config = {"host": "dev-db", "username": "dev_user", "password": "dev123", "database": "dev_db"}
prod_config = {"host": "prod-db", "port": 5432, "username": "prod_user", "password": "prod456", "database": "prod_db"}
# 根据环境使用不同配置
environment = "开发" # 可以是"开发"或"生产"
if environment == "开发":
connection_info = connect_database(**dev_config)
else:
connection_info = connect_database(**prod_config)
print(connection_info)
3.3 函数作用域
3.3.1 变量作用域
变量作用域定义了变量在程序中的可见范围,就像不同房间里的物品,每个房间只能看到和使用自己房间里的东西。
# 变量作用域
# 全局作用域
global_var = "我是全局变量" # 在函数外定义的变量
def test_scope():
# 局部作用域
local_var = "我是局部变量" # 在函数内定义的变量
print(f"在函数内访问全局变量:{global_var}")
print(f"在函数内访问局部变量:{local_var}")
test_scope()
print(f"在函数外访问全局变量:{global_var}")
# print(f"在函数外访问局部变量:{local_var}") # 错误:局部变量在函数外不可见
# 嵌套作用域
def outer_function():
outer_var = "我是外层变量"
def inner_function():
inner_var = "我是内层变量"
print(f"在内层函数中访问外层变量:{outer_var}")
print(f"在内层函数中访问内层变量:{inner_var}")
inner_function()
print(f"在外层函数中访问外层变量:{outer_var}")
# print(f"在外层函数中访问内层变量:{inner_var}") # 错误:内层变量在外层函数不可见
outer_function()
# 变量屏蔽
count = 10 # 全局变量
def display_count():
count = 5 # 局部变量,与全局变量同名但不同
print(f"函数内的计数:{count}") # 访问局部变量
display_count() # 输出:函数内的计数:5
print(f"全局计数:{count}") # 输出:全局计数:10(全局变量未被修改)
3.3.2 变量查找规则(LEGB规则)
Python使用LEGB规则来查找变量,按照Local(局部)、Enclosing(嵌套)、Global(全局)和Built-in(内置)的顺序。
# LEGB规则
# Built-in(内置)作用域
# 包含Python的内置函数和变量,如print, len, sum等
# Global(全局)作用域
global_x = "全局X" # 全局变量
def outer_function():
# Enclosing(嵌套)作用域
enclosing_y = "嵌套Y" # 嵌套作用域变量
def inner_function():
# Local(局部)作用域
local_z = "局部Z" # 局部变量
print(f"局部变量:{local_z}")
print(f"嵌套变量:{enclosing_y}")
print(f"全局变量:{global_x}")
print(f"内置函数:{len}")
inner_function()
outer_function()
# 变量查找顺序示例
x = 100 # 全局变量
def test_variable_lookup():
# x = 200 # 取消注释这行会改变输出结果
def inner():
# x = 300 # 取消注释这行会改变输出结果
print(f"x的值是:{x}")
inner()
test_variable_lookup() # 输出:x的值是:100(如果取消注释,结果会不同)
# 名称冲突示例
len = "这不是内置函数len" # 全局变量覆盖了内置函数
# print(len([1, 2, 3])) # 错误:字符串对象不可调用
print(len) # 输出:这不是内置函数len
# 恢复内置函数
del len # 删除全局变量
print(len([1, 2, 3])) # 现在可以使用内置函数了,输出:3
3.3.3 全局变量修改
在函数内部修改全局变量需要使用global
关键字,就像告诉Python我们想要修改的是整个家庭共享的物品,而不是只在自己房间里使用的物品。
# 全局变量修改
# 不使用global关键字(创建局部变量)
total_sales = 1000 # 全局变量:总销售额
def update_sales(new_sale):
total_sales = total_sales + new_sale # 错误:引用未赋值的局部变量
print(f"函数内部:总销售额为{total_sales}元")
# 尝试调用函数会导致错误
# update_sales(500) # 错误:局部变量'total_sales'在赋值前被引用
# 使用global关键字(修改全局变量)
total_visitors = 100 # 全局变量:访客数量
def update_visitors(new_visitors):
global total_visitors # 声明使用全局变量
total_visitors = total_visitors + new_visitors # 修改全局变量
print(f"函数内部:总访客数为{total_visitors}人")
update_visitors(50) # 输出:函数内部:总访客数为150人
print(f"函数外部:总访客数为{total_visitors}人") # 输出:函数外部:总访客数为150人
# 在嵌套函数中修改外层函数的变量
def outer_function():
counter = 0 # 外层函数的局部变量
def inner_function():
nonlocal counter # 声明使用外层函数的变量
counter += 1
print(f"内层函数:计数器值为{counter}")
inner_function()
print(f"外层函数:计数器值为{counter}")
outer_function() # 输出:内层函数:计数器值为1 然后是 外层函数:计数器值为1
# 全局变量的最佳实践
# 1. 尽量减少使用全局变量
# 2. 如果必须使用,将其集中在一处定义
# 3. 使用大写字母命名全局常量
# 全局常量示例
MAX_USERS = 1000
DEFAULT_TIMEOUT = 30
API_BASE_URL = "https://api.example.com/v1"
def validate_user_count(count):
if count > MAX_USERS:
print(f"错误:用户数量超过最大限制{MAX_USERS}")
return False
return True
3.4 闭包与装饰器
3.4.1 闭包
闭包是一个函数,它记住了创建时的环境变量,即使这些变量已经不在当前作用域中,就像一个带着记忆的信封,里面保存着创建时的环境信息。
# 闭包
# 闭包的基本概念
def create_counter():
count = 0 # 外部函数的局部变量
def increment():
nonlocal count # 声明使用外部函数的变量
count += 1
return count
return increment # 返回内部函数
# 创建一个计数器
counter = create_counter()
print(counter()) # 输出:1
print(counter()) # 输出:2
print(counter()) # 输出:3
# 创建另一个独立的计数器
counter2 = create_counter()
print(counter2()) # 输出:1(独立的计数)
print(counter()) # 输出:4(第一个计数器继续计数)
# 闭包的实际应用:创建不同的倍率计算器
def create_multiplier(factor):
"""创建一个乘法器
参数:
factor:乘法因子
返回:
一个将输入值乘以指定因子的函数
"""
def multiply(number):
return number * factor
return multiply
# 创建不同的乘法器
double = create_multiplier(2) # 创建一个将数字翻倍的函数
triple = create_multiplier(3) # 创建一个将数字变为三倍的函数
print(f"10的两倍是:{double(10)}") # 输出:10的两倍是:20
print(f"10的三倍是:{triple(10)}") # 输出:10的三倍是:30
# 闭包的优势
# 1. 数据隐藏:内部状态对外部不可见
# 2. 避免使用全局变量
# 3. 提供一种创建有状态函数的方法
# 闭包应用:创建温度转换器
def create_temperature_converter(from_unit, to_unit):
"""创建一个温度转换函数
参数:
from_unit:源温度单位('C'表示摄氏度,'F'表示华氏度)
to_unit:目标温度单位
返回:
一个温度转换函数
"""
def celsius_to_fahrenheit(celsius):
return celsius * 9/5 + 32
def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5/9
if from_unit == 'C' and to_unit == 'F':
return celsius_to_fahrenheit
elif from_unit == 'F' and to_unit == 'C':
return fahrenheit_to_celsius
else:
# 相同单位,直接返回原值
return lambda x: x
# 创建不同的温度转换器
c_to_f = create_temperature_converter('C', 'F')
f_to_c = create_temperature_converter('F', 'C')
# 使用温度转换器
beijing_celsius = 35
beijing_fahrenheit = c_to_f(beijing_celsius)
print(f"{beijing_celsius}°C = {beijing_fahrenheit}°F") # 输出:35°C = 95.0°F
new_york_fahrenheit = 77
new_york_celsius = f_to_c(new_york_fahrenheit)
print(f"{new_york_fahrenheit}°F = {new_york_celsius}°C") # 输出:77°F = 25.0°C
3.4.2 装饰器
装饰器是一种特殊的函数,它可以修改其他函数的功能,就像给房子装修一样,不改变房子的结构,但增加了新的功能或美观度。
# 装饰器
# 基本装饰器示例
def simple_decorator(func):
"""一个简单的装饰器函数
参数:
func:要装饰的函数
返回:
装饰后的函数
"""
def wrapper():
print("函数执行前的操作")
func() # 调用原始函数
print("函数执行后的操作")
return wrapper
# 使用装饰器(手动方式)
def say_hello():
print("你好!")
decorated_say_hello = simple_decorator(say_hello) # 手动装饰
decorated_say_hello() # 输出:函数执行前的操作 然后是 你好! 然后是 函数执行后的操作
# 使用@语法糖(自动方式)
@simple_decorator
def say_goodbye():
print("再见!")
say_goodbye() # 输出:函数执行前的操作 然后是 再见! 然后是 函数执行后的操作
# 带参数的装饰器
def log_function_call(func):
"""记录函数调用的装饰器
参数:
func:要装饰的函数
返回:
装饰后的函数
"""
def wrapper(*args, **kwargs):
print(f"调用函数:{func.__name__},参数:{args},关键字参数:{kwargs}")
result = func(*args, **kwargs) # 调用原始函数并获取返回值
print(f"函数{func.__name__}执行完毕,返回值:{result}")
return result # 返回原始函数的返回值
return wrapper
# 装饰带参数和返回值的函数
@log_function_call
def calculate_area(length, width):
"""计算矩形面积"""
return length * width
area = calculate_area(5, 3) # 输出函数调用信息和结果
# 带参数的装饰器
def repeat(times):
"""创建一个重复执行指定次数的装饰器
参数:
times:重复次数
返回:
装饰器函数
"""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator
# 使用带参数的装饰器
@repeat(3)
def greet(name):
return f"你好,{name}!"
results = greet("小明") # 调用函数3次
print(results) # 输出:['你好,小明!', '你好,小明!', '你好,小明!']
# 多个装饰器
def bold(func):
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper
def italic(func):
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper
# 应用多个装饰器(从下到上执行)
@bold
@italic
def format_text(text):
return text
formatted_text = format_text("Hello, World!")
print(formatted_text) # 输出:<b><i>Hello, World!</i></b>
# 装饰器的实际应用
# 1. 计时装饰器
import time
def measure_time(func):
"""测量函数执行时间的装饰器"""
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数{func.__name__}执行时间:{end_time - start_time:.6f}秒")
return result
return wrapper
@measure_time
def slow_function():
"""一个耗时的函数"""
time.sleep(1) # 模拟耗时操作
return "操作完成"
slow_function() # 输出执行时间
# 2. 缓存装饰器
def cache_result(func):
"""缓存函数结果的装饰器"""
cache = {}
def wrapper(*args):
if args in cache:
print(f"使用缓存结果:{args}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"计算新结果:{args}")
return result
return wrapper
@cache_result
def fibonacci(n):
"""计算斐波那契数列的第n项"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 第一次计算
print(fibonacci(10)) # 使用缓存结果
3.5 匿名函数
匿名函数(lambda函数)是一种简洁的、一行式的函数定义方式,适合简单的函数逻辑,就像快餐一样,简单快捷但功能有限。
# 匿名函数(lambda函数)
# 基本语法
# lambda 参数: 表达式
# 普通函数与lambda函数对比
def square(x):
return x ** 2
# 等价的lambda函数
square_lambda = lambda x: x ** 2
print(f"普通函数结果:{square(5)}") # 输出:普通函数结果:25
print(f"Lambda函数结果:{square_lambda(5)}") # 输出:Lambda函数结果:25
# 多参数lambda函数
rectangle_area = lambda length, width: length * width
print(f"矩形面积:{rectangle_area(4, 3)}") # 输出:矩形面积:12
# 无参数lambda函数
greet = lambda: "你好,世界!"
print(greet()) # 输出:你好,世界!
# lambda函数的应用场景
# 1. 作为函数参数
products = [
{"name": "手机", "price": 3999},
{"name": "笔记本电脑", "price": 6999},
{"name": "耳机", "price": 999},
{"name": "平板电脑", "price": 4599}
]
# 按价格排序
sorted_by_price = sorted(products, key=lambda product: product["price"])
print("按价格排序的商品:")
for product in sorted_by_price:
print(f"{product['name']}: ¥{product['price']}")
# 2. 结合内置函数使用
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 使用filter筛选偶数
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"偶数列表:{even_numbers}") # 输出:偶数列表:[2, 4, 6, 8, 10]
# 使用map将每个数字翻倍
doubled_numbers = list(map(lambda x: x * 2, numbers))
print(f"翻倍后的列表:{doubled_numbers}") # 输出:翻倍后的列表:[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# 3. 在GUI编程中作为回调函数
# 示例(伪代码):
# button.on_click(lambda: print("按钮被点击了!"))
# lambda函数的限制
# 1. 只能包含单个表达式
# 2. 不能包含复杂的逻辑或多行代码
# 3. 可读性可能较差
# 何时使用lambda函数
# 1. 函数逻辑简单,一行就能表达
# 2. 函数只使用一次,不需要重复使用
# 3. 作为高阶函数的参数
# 何时避免使用lambda函数
# 1. 函数逻辑复杂
# 2. 需要文档字符串说明
# 3. 需要多次重用
3.6 模块导入与使用
模块是Python中组织和重用代码的方式,就像图书馆中的不同书籍,每本书包含特定领域的知识,我们可以根据需要借阅使用。
# 模块导入与使用
# 导入整个模块
import math
# 使用模块中的函数和变量
radius = 5
circle_area = math.pi * radius ** 2
print(f"圆的面积:{circle_area:.2f}平方单位") # 输出:圆的面积:78.54平方单位
square_root = math.sqrt(16)
print(f"16的平方根:{square_root}") # 输出:16的平方根:4.0
# 导入特定函数或变量
from random import randint, choice
# 生成1到100之间的随机整数
random_number = randint(1, 100)
print(f"随机数:{random_number}")
# 从列表中随机选择一个元素
fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"]
random_fruit = choice(fruits)
print(f"随机水果:{random_fruit}")
# 使用别名导入模块
import datetime as dt
current_time = dt.datetime.now()
print(f"当前时间:{current_time}")
# 导入模块中的所有内容(不推荐)
# from math import *
# 自定义模块
# 假设我们有一个名为my_utils.py的文件,内容如下:
"""
# my_utils.py内容
def calculate_tax(amount, rate=0.1):
"""计算税额"""
return amount * rate
def format_currency(amount):
"""格式化货币"""
return f"¥{amount:.2f}"
TAX_RATE = 0.13 # 增值税率
"""
# 导入自定义模块(需要先创建my_utils.py文件)
# import my_utils
#
# product_price = 100
# tax = my_utils.calculate_tax(product_price, my_utils.TAX_RATE)
# formatted_price = my_utils.format_currency(product_price + tax)
# print(f"含税价格:{formatted_price}")
# 模块搜索路径
import sys
print("Python模块搜索路径:")
for path in sys.path:
print(f"- {path}")
# 添加自定义路径到模块搜索路径
# sys.path.append("/path/to/your/modules")
# 包(Package)
# 包是一种特殊的模块,它包含多个子模块
# 例如:
# my_package/
# __init__.py
# module1.py
# module2.py
# subpackage/
# __init__.py
# module3.py
# 导入包中的模块
# import my_package.module1
# from my_package import module2
# from my_package.subpackage import module3
# 标准库模块示例
# 1. os模块:操作系统接口
import os
current_dir = os.getcwd() # 获取当前工作目录
print(f"当前工作目录:{current_dir}")
files = os.listdir(".") # 列出当前目录下的文件和文件夹
print("当前目录内容:")
for file in files[:5]: # 只显示前5个
print(f"- {file}")
# 2. datetime模块:日期和时间处理
from datetime import datetime, timedelta
now = datetime.now()
print(f"当前日期和时间:{now}")
one_week_later = now + timedelta(days=7)
print(f"一周后:{one_week_later}")
# 3. json模块:JSON数据处理
import json
person = {
"name": "张三",
"age": 30,
"city": "北京",
"skills": ["Python", "JavaScript", "SQL"]
}
# 将Python对象转换为JSON字符串
json_str = json.dumps(person, ensure_ascii=False)
print(f"JSON字符串:{json_str}")
# 将JSON字符串转换回Python对象
parsed_person = json.loads(json_str)
print(f"解析后的对象:{parsed_person}")
# 4. random模块:随机数生成
import random
# 生成0到1之间的随机浮点数
random_float = random.random()
print(f"随机浮点数:{random_float}")
# 随机打乱列表
numbers = list(range(1, 11))
random.shuffle(numbers)
print(f"打乱后的列表:{numbers}")
# 5. re模块:正则表达式
import re
text = "我的电话号码是13812345678,邮箱是example@python.org"
# 查找电话号码
phone_pattern = r'1\d{10}'
phone = re.search(phone_pattern, text)
if phone:
print(f"找到电话号码:{phone.group()}")
# 查找邮箱地址
email_pattern = r'\w+@\w+\.\w+'
email = re.search(email_pattern, text)
if email:
print(f"找到邮箱地址:{email.group()}")
# 模块的__name__属性
# 当模块被直接运行时,__name__等于'__main__'
# 当模块被导入时,__name__等于模块名
print(f"当前模块的__name__:{__name__}")
# 常见的模块使用模式
def main():
print("这是主函数")
# 主程序逻辑
if __name__ == "__main__":
main() # 只有直接运行此脚本时才会执行
3.7 常用内置函数
Python提供了许多内置函数,它们就像随身携带的工具箱,可以帮助我们完成各种常见任务,无需自己编写这些功能。
# 常用内置函数
# 1. map函数:对可迭代对象中的每个元素应用函数
def celsius_to_fahrenheit(celsius):
"""将摄氏度转换为华氏度"""
return celsius * 9/5 + 32
# 城市及其摄氏温度
cities_temp_c = [
("北京", 32),
("上海", 30),
("广州", 35),
("哈尔滨", 22)
]
# 使用map转换温度
fahrenheit_temps = list(map(lambda city_temp: (city_temp[0], celsius_to_fahrenheit(city_temp[1])), cities_temp_c))
print("城市温度(华氏度):")
for city, temp in fahrenheit_temps:
print(f"{city}: {temp:.1f}°F")
# 2. filter函数:筛选满足条件的元素
scores = [85, 92, 78, 65, 90, 76, 88, 59, 95]
# 筛选优秀成绩(90分及以上)
excellent_scores = list(filter(lambda score: score >= 90, scores))
print(f"优秀成绩:{excellent_scores}") # 输出:优秀成绩:[92, 90, 95]
# 3. zip函数:将多个可迭代对象打包成元组
students = ["小明", "小红", "小张", "小李"]
math_scores = [95, 87, 92, 78]
chinese_scores = [88, 95, 89, 92]
# 将学生姓名和成绩打包
student_scores = list(zip(students, math_scores, chinese_scores))
print("学生成绩:")
for student, math, chinese in student_scores:
average = (math + chinese) / 2
print(f"{student} - 数学: {math}, 语文: {chinese}, 平均: {average:.1f}")
# 4. sorted函数:排序
# 对学生按数学成绩排序
sorted_by_math = sorted(student_scores, key=lambda x: x[1], reverse=True)
print("\n按数学成绩排序:")
for student, math, chinese in sorted_by_math:
print(f"{student} - 数学: {math}")
# 5. enumerate函数:同时获取索引和值
fruits = ["苹果", "香蕉", "橙子", "葡萄"]
print("\n水果列表:")
for index, fruit in enumerate(fruits, start=1):
print(f"{index}. {fruit}")
# 6. any和all函数
# any:只要有一个元素为True,结果就为True
# all:所有元素都为True,结果才为True
test_results = [True, True, False, True]
print(f"是否有通过的测试:{any(test_results)}") # 输出:是否有通过的测试:True
print(f"是否全部测试通过:{all(test_results)}") # 输出:是否全部测试通过:False
# 7. sum, min, max函数
prices = [42.5, 19.9, 55.0, 35.8, 128.0]
print(f"总价:{sum(prices):.2f}元") # 输出:总价:281.20元
print(f"最低价:{min(prices)}元") # 输出:最低价:19.9元
print(f"最高价:{max(prices)}元") # 输出:最高价:128.0元
# 8. round, abs, pow函数
print(f"3.1415926四舍五入到两位小数:{round(3.1415926, 2)}") # 输出:3.14
print(f"-10的绝对值:{abs(-10)}") # 输出:10
print(f"2的10次方:{pow(2, 10)}") # 输出:1024
# 9. isinstance和type函数
value1 = 42
value2 = "Hello"
value3 = [1, 2, 3]
print(f"value1是整数吗?{isinstance(value1, int)}") # 输出:True
print(f"value2的类型:{type(value2)}") # 输出:<class 'str'>
print(f"value3是列表吗?{isinstance(value3, list)}") # 输出:True
# 10. dir和help函数
# dir:列出对象的所有属性和方法
# help:显示对象的帮助信息
# 查看字符串对象的所有方法
string_methods = [method for method in dir(str) if not method.startswith('__')]
print(f"字符串的部分方法:{string_methods[:5]}...") # 只显示前5个
# 11. input和print函数
# 这两个函数是最基本的输入输出函数
# 示例:简单的温度转换器
def convert_temp():
try:
celsius = float(input("请输入摄氏温度:")) # 获取用户输入
fahrenheit = celsius * 9/5 + 32
print(f"{celsius}°C = {fahrenheit:.2f}°F") # 格式化输出
except ValueError:
print("请输入有效的数字!")
# 注释掉以避免实际运行
# convert_temp()
# 12. open函数:文件操作
# 示例:写入和读取文件
def file_demo():
# 写入文件
with open("demo.txt", "w", encoding="utf-8") as file:
file.write("这是一个示例文件。\n")
file.write("Python编程很有趣!")
# 读取文件
with open("demo.txt", "r", encoding="utf-8") as file:
content = file.read()
print("文件内容:")
print(content)
# 注释掉以避免实际创建文件
# file_demo()
# 内置函数的组合使用
data = ["10", "20", "30", "40", "50"]
# 将字符串列表转换为整数列表并计算总和
total = sum(map(int, data))
print(f"总和:{total}") # 输出:总和:150
# 找出列表中的偶数并计算平均值
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
even_average = sum(even_numbers) / len(even_numbers)
print(f"偶数平均值:{even_average}") # 输出:偶数平均值:6.0