一、为什么需要函数?

  • 代码复用:写一次,用无数次。
  • 模块化:将复杂问题分解为小的、可管理的部分。
  • 易于维护:修改一个功能,只需修改对应的函数。
  • 提高可读性:好的函数名本身就是一种注释。

二、函数参数

函数大家应该都会,就不从头细讲了,说一些容易忽略的点

2.1 位置参数和关键字参数

def describe_person(name, age, city): 
"""描述一个人的信息""" 
print(f"姓名: {name}, 年龄: {age}, 城市: {city}")

# 混合使用:位置参数必须在关键字参数之前
describe_person("王五", city="广州", age=28) # 输出: 姓名: 王五, 年龄: 28, 城市: 广州
describe_person(name="王五", 28, city="广州") # 这会报错!

2.2 重要陷阱:可变默认参数

永远不要将可变对象(如列表、字典)作为默认参数!

# 错误示范 - 可变默认参数
def add_item_to_list(item, my_list=[]):
    """向列表中添加一个项目(错误示范)。"""
    my_list.append(item)
    return my_list

print(add_item_to_list(1)) # 输出: [1]
print(add_item_to_list(2)) # 输出: [1, 2]  <- 意想不到!我们期望的是 [2]
print(add_item_to_list(3)) # 输出: [1, 2, 3] <- 问题持续恶化

原因: 默认参数 my_list=[] 在函数定义时只被创建一次。后续所有调用,如果不提供 my_list,都会使用同一个列表对象。

正确做法: 使用 None 作为默认值,然后在函数内部创建新对象。

# 代码 2.4: 正确示范
def add_item_to_list_correctly(item, my_list=None):
    """向列表中添加一个项目(正确示范)。"""
    if my_list is None:
        my_list = []  # 每次调用都创建一个新的列表
    my_list.append(item)
    return my_list

print(add_item_to_list_correctly(1)) # 输出: [1]
print(add_item_to_list_correctly(2)) # 输出: [2]
print(add_item_to_list_correctly(3)) # 输出: [3]

2.3 可变长度参数

当不确定会传递多少个参数时,可变长度参数就派上用场了。

  • *args:接收任意数量的位置参数,它们被组装成一个元组
  • **kwargs:接收任意数量的关键字参数,它们被组装成一个字典
# *args 和 **kwargs
def log_message(*args, **kwargs):
    """记录任意数量的消息和配置信息。"""
    print("--- 位置参数 ---")
    for arg in args:
        print(f"- {arg}")
    
    print("n--- 关键字参数 ---")
    for key, value in kwargs.items():
        print(f"- {key}: {value}")

log_message("系统启动", "加载模块", level="INFO", user="admin")

2.4 参数解包

2.4.1 函数定义与参数

def introduce(name, age, city):
    print(f"我叫{name},今年{age}岁,来自{city}。")

2.4.2 列表/元组解包 (*args)

接下来,我们看这段代码:

args = ["李四", 28, "广州"]  # args 是一个列表
introduce(*args)            # 使用 * 解包列表
  • args 是一个列表(或者也可以是元组),它包含了三个元素:"李四"28"广州"
  • introduce(*args) 中的 *解包操作符
  • *args 的作用是把列表 args 解开,把里面的元素依次作为位置参数传递给 introduce 函数。
  • 所以,introduce(*args) 这行代码,等价于 introduce("李四", 28, "广州")
  • 这样就成功地把列表里的数据传递给了函数,而不需要手动写三个参数。

2.4.3 字典解包 (**kwargs)

最后,我们看这段代码:

kwargs = {"name": "王五", "age": 32, "city": "深圳"}  # kwargs 是一个字典
introduce(**kwargs)                                   # 使用 ** 解包字典
  • kwargs 是一个字典。它的(key)分别是 name, age, city,这正好与 introduce 函数的参数名完全一致。
  • introduce(**kwargs) 中的 **字典解包操作符
  • **kwargs 的作用是把字典 kwargs 解开,把里面的键值对作为关键字参数(keyword arguments)传递给 introduce 函数。
  • 所以,introduce(**kwargs) 这行代码,等价于 introduce(name="王五", age=32, city="深圳")
  • 同样,这也成功地把字典里的数据传递给了函数。

总结

  • 普通调用introduce("张三", 25, "北京") - 直接传入三个值。
  • 列表/元组解包 (*) :当数据存储在一个序列(如列表或元组)中,并且顺序与函数参数顺序一致时,可以使用 * 来解包,方便地将序列中的元素作为位置参数传递。
  • 字典解包 (**) :当数据存储在一个字典中,并且字典的键与函数参数名一致时,可以使用 ** 来解包,方便地将字典中的键值对作为关键字参数传递。

这两种解包方式在处理动态数据或使代码更简洁时非常有用!

三、返回值

3.1 返回多个值

def calculate(a, b):
    """返回多个值(实际上是返回元组)"""
    sum_result = a + b
    difference = a - b
    product = a * b
    quotient = a / b if b != 0 else None
    return sum_result, difference, product, quotient

result = calculate(10, 5)
print(result)  # 输出: (15, 5, 50, 2.0)

# 解包多个返回值
sum_val, diff_val, prod_val, quot_val = calculate(8, 4)
print(f"和: {sum_val}, 差: {diff_val}, 积: {prod_val}, 商: {quot_val}")

在 Python 中,当在 return 语句后面用逗号 , 分隔多个值时,Python 会自动将它们打包成一个元组(tuple) 再返回。

所以,return sum_result, difference, product, quotient这行代码等价于

return (sum_result, difference, product, quotient)

括号 () 是可选的,不写括号 Python 也会创建元组。

四、匿名函数

lambda 函数是一种匿名函数(anonymous function)。它是一种非常简洁的创建简单函数的方式,通常用于需要一个一次性、简单的函数,而不想正式定义一个完整函数(使用 def)的场合。

4.1 lambda 函数的基本语法

lambda 参数1, 参数2, ... : 表达式
  • lambda:定义匿名函数的关键字。
  • 参数1, 参数2, ...:函数接收的参数,可以没有,也可以有一个或多个。
  • ::分隔参数和函数体。
  • 表达式一个表达式,lambda 函数会自动返回这个表达式的结果注意:这里不能是语句(如 print, for, while 等),只能是一个计算结果的表达式。

4.2 lambda 函数 vs 普通函数 (def)

普通函数 (def)

def add(x, y):
    return x + y

result = add(3, 5)
print(result)  # 输出: 8

lambda 函数

# 创建一个 lambda 函数,并将其赋值给变量 add
add = lambda x, y: x + y

result = add(3, 5)
print(result)  # 输出: 8
  • 我们将这个匿名函数赋值给了变量 add,然后就可以像普通函数一样调用它了。

4.3 排序中的应用

students = [("Alice", 88), ("Bob", 95), ("Charlie", 78)]

# 按照元组的第二个元素(成绩)进行排序
sorted_by_grade = sorted(students, key=lambda student: student[1])

print(sorted_by_grade)  # 输出: [('Charlie', 78), ('Alice', 88), ('Bob', 95)]

sorted() 是 Python 的一个内置函数,它的作用是对一个可迭代对象(如列表)进行排序,并返回一个新的、已排序的列表

它的基本语法是:

sorted(要排序的列表, key=一个函数)
  • 要排序的列表:在这里就是 students
  • key=:这是一个关键字参数。它的值是一个函数。这个函数告诉 sorted 应该按照什么规则来排序

五、作用域

5.1 局部作用域

def my_function():
    local_var = "我是局部变量"
    print(local_var)  # 在函数内部可以访问

my_function()
# print(local_var)  # 报错: NameError,在函数外部无法访问局部变量

5.2 全局作用域

global_var = "我是全局变量"

def access_global():
    """访问全局变量"""
    print(global_var)  # 可以读取全局变量

def modify_global():
    """修改全局变量"""
    global global_var  # 使用global关键字声明
    global_var = "全局变量已被修改"

access_global()  # 输出: 我是全局变量
modify_global()
access_global()  # 输出: 全局变量已被修改

当 Python 在函数内部遇到 variable_name = value 这样的赋值语句时,它会默认认为你想要创建一个局部变量 variable_name

使用 global 关键字

  • global global_var 这一行代码告诉 Python

    • “嘿,Python,我在函数内部要使用的 global_var不是一个新的局部变量,而是那个在全局作用域下定义的 global_var。”
    • “当我对 global_var 进行赋值时,请直接修改全局的那个 global_var。”
  • 因此,global_var = "全局变量已被修改" 这行代码现在的作用是:

    • 修改全局作用域下的 global_var 变量的值。

5.3 嵌套函数与非局部变量

def outer_function():
    outer_var = 1
    
    def inner_function():
        nonlocal outer_var  # 使用nonlocal关键字
        outer_var = 2
        inner_var = 3
        print(f"内部函数: {outer_var}")
    
    inner_function()
    print(f"外部函数: {outer_var}")
    # print(inner_var)  # 报错: 无法访问内部函数的变量

outer_function()

5.4 闭包

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

闭包(Closure)的概念

当内部函数 multiplier 记住了它外部的变量 factor(即使 make_multiplier 已经运行结束),我们就说 multiplier 函数形成了一个闭包

  • 闭包 = 函数 + 函数定义时的环境(变量)
  • multiplier 不仅是一个函数,它还"封闭"了 factor 这个变量。

这种模式叫做工厂模式函数工厂,它有很多用途,具体用途略~

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]