零基础学Python——第三章:函数与模块

第三章:函数与模块

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值