day 26 函数专题

发布于:2025-05-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、函数的基本写法

def function_name(parameter1, parameter2, ...):
    """
    Docstring: 描述函数的功能、参数和返回值 (可选但强烈推荐)
    """
    # 函数体: 实现功能的代码
    # ...
    return value # 可选,用于返回结果
  • def 关键字:用于声明开始定义一个函数。
  • function_name(函数名):遵循 Python 命名约定,通常由小写字母和下划线组成。
  • parameter1, parameter2, …(参数):也称为形参,是函数在被调用时接收输入值的变量。参数是可选的。
  • ()(括号):参数列表需放在括号内,即使无参数,括号也不可省略。
  • :(冒号):标志函数定义头部结束,随后缩进的代码块为函数体。
  • Docstring(文档字符串):可选,位于函数定义第一行的多行字符串,用于解释函数作用、参数及返回值等信息。可通过 help(function_name) 或 function_name.__doc__ 查看。
  • 函数体(Function Body):缩进的代码块,包含实现函数功能的语句。
  • return value(返回语句)return 语句用于从函数返回一个值。若无 return 语句或 return 后无值,函数自动返回 None。函数可在不同条件分支中包含多个 return 语句。

1、不带参数的函数

# 定义一个简单的问候函数
def greet():
    """打印一句问候语。"""
    message = "大家好!欢迎学习Python函数定义!"
    print(message)
greet()
# 查看文档字符串,方便查看函数的使用,这个方法可以不掌握
print(greet.__doc__)
# 实际上,当你在py文件中,鼠标悬停在函数上时按住ctrl即可点击函数跳转到其内部查看函数的定义

2、带参数的函数

形参(Parameters):函数定义中列出的变量名,如 namefeature1feature2 等。形参相当于一个占位符,代表函数在被调用时将会接收到的数据。形参在函数内部有自己的作用域,它们就像函数内部定义的局部变量。

实参(Arguments):函数调用时传递给函数的实际值,如 "张三"1025 等。实参是实实在在的数据,它们会被赋值给函数定义中的形参。实参本身在函数外部有自己的作用域。

传递关系:当实参的值传递给形参后,在函数内部对形参的修改,一般不会影响到函数外部实参的值(对于不可变数据类型,如整数、字符串、元组等),如:

def change_number(num):
    num = num + 1
    return num

original_num = 5
new_num = change_number(original_num)
print(original_num)  
print(new_num)  
# 输出:
5
6

在这个例子中,形参 num 在函数 change_number 内部被修改,但函数外部的 original_num 值并没有改变。 

然而,对于可变数据类型(如列表、字典),如果在函数内部通过形参修改了其内容,会影响到函数外部的实参。例如:

def change_list(lst):
    lst.append(4)
    return lst

my_list = [1, 2, 3]
new_list = change_list(my_list)
print(my_list)  
print(new_list)  
# 输出:
[1, 2, 3, 4]
[1, 2, 3, 4]

这里函数内部对形参 lst(指向外部传入的 my_list)的修改,导致my_list本身内容的改变。

3、带返回值的函数

# 定义一个计算和并返回结果的函数
def calculate_sum(a, b):
    """计算两个数的和并返回结果。

    Args:
        a (float or int): 第一个数。
        b (float or int): 第二个数。

    Returns:
        float or int: 两个数的和。
    """
    result = a + b
    return result
    print("hhh")  # 此代码不会执行,因为函数遇到 return 语句后立即返回
calculate_sum(2, 3)
# 输出:5

二、变量作用域

局部变量(Local Variables):在函数内部定义的变量,仅在该函数内部有效,函数执行完毕后通常会被销毁。

全局变量(Global Variables):在所有函数外部定义的变量,可在程序任何地方访问(但在函数内部修改全局变量需使用 global 关键字声明,初学者可先避免)。

print("\n--- 变量作用域示例 ---")
global_var = "我是一个全局变量"

def scope_test():
    # 声明使用的是全局变量
    global global_var  
    local_var = "我是一个局部变量"
    print(f"在函数内部,可以看到局部变量: '{local_var}'")
    print(f"在函数内部,也可以看到全局变量: '{global_var}'")
    global_var = "尝试在函数内修改全局变量" 
    print(f"在函数内部,修改后的 '全局' 变量: '{global_var}'")

scope_test()

print(f"\n在函数外部,可以看到全局变量: '{global_var}'")
# 会产生 NameError,因为 local_var 只在函数内存在
print(f"在函数外部,不能看到局部变量: {local_var}")

三、函数的参数类型

位置参数(Positional Arguments):

调用函数时按顺序匹配参数。

def describe_pet(animal_type, pet_name):
    """显示宠物的信息。"""
    print(f"\n我有一只 {animal_type}.")
    print(f"我的 {animal_type} 的名字叫 {pet_name}.")
describe_pet("猫", "咪咪")
# 输出:
我有一只 猫.
我的 猫 的名字叫 咪咪.

为提高可读性,推荐使用关键字参数传递所有参数。例如对于复杂函数:

# 位置参数写法不清晰
plot_data(data, x_col, y_col, "blue", "-", True, False, "My Plot", "X-axis", "Y-axis") 

# 关键字参数写法更清晰
plot_data(data=my_data, x_column='time', y_column='value',
          color='blue', linestyle='-', show_grid=True, use_log_scale=False,
          title="My Awesome Plot", xlabel="Time (s)", ylabel="Value") 

在 Python 函数调用中,关键字参数通过 “参数名 = 值” 的形式传递。这样一来,Python 解释器根据参数名来匹配值,而不是位置,即此时与赋值顺序无关。

默认参数值(Default Parameter Values)

定义函数时给参数指定默认值,调用时若未提供该参数,则使用默认值。注意,带默认值的参数必须放在无默认值的参数之后。

# animal_type 有默认值
def describe_pet_default(pet_name, animal_type="狗"): 
    """显示宠物的信息,动物类型默认为狗。"""
    print(f"我有一只 {animal_type}.")
    print(f"我的 {animal_type} 的名字叫 {pet_name.title()}.")
# animal_type 使用默认值 "狗"
describe_pet_default(pet_name="小黑") 
# 提供 animal_type时,覆盖默认值
describe_pet_default(pet_name="雪球", animal_type="仓鼠") 

可变数量参数(*args 和 **kwargs):

*args(收集位置参数):将多余的位置参数收集为一个元组。函数调用时,Python 先尝试用位置参数填充明确定义的、非关键字的形参(即普通无*或**前缀的参数)。若填充后还有剩余位置参数,这些参数将被收集成元组赋值给 *args 指定的变量(通常为 args)。若位置参数数量正好或少于明确定义的形参数量且满足必需参数,*args 为空元组 ()

def make_pizza(size, *toppings):
    """概述要制作的比萨。
    *toppings 会将所有额外的位置参数收集到一个元组中。
    """
    print(f"\n制作一个 {size} 寸的比萨,配料如下:")
    if toppings: # 只要toppings不为空元组,就会执行
        for topping in toppings:
            print(f"- {topping}")
    else:
        print("- 原味 (无额外配料)")

make_pizza(12, "蘑菇")
make_pizza(16, "香肠", "青椒", "洋葱")
make_pizza(9) # toppings 会是空元组

 

**kwargs(收集关键字参数):将多余的关键字参数收集为一个字典。函数调用时,Python 先处理位置参数,再尝试用关键字参数(形如 name=value)填充明确定义的同名形参。若填充后还有剩余关键字参数,这些参数将被收集成字典赋值给 **kwargs 指定的变量(通常为 kwargs)。若所有关键字参数都能匹配明确形参名,**kwargs 为空字典 {}

def build_profile(first_name, last_name, **user_info):
    """创建一个字典,其中包含我们知道的有关用户的一切。
    **user_info 会将所有额外的关键字参数收集到一个字典中。
    """
    profile = {}
    profile['first_name'] = first_name
    profile['last_name'] = last_name
    for key, value in user_info.items():
        profile[key] = value
    return profile

user_profile = build_profile('爱因斯坦', '阿尔伯特',
                             location='普林斯顿',
                             field='物理学',
                             hobby='小提琴')
print(f"\n用户信息: {user_profile}")
# 输出: {'first_name': '爱因斯坦', 'last_name': '阿尔伯特', 'location': '普林斯顿', 'field': '物理学', 'hobby': '小提琴'}

当函数 build_profile 被调用时,Python 解释器首先将位置参数 '爱因斯坦' 和 '阿尔伯特' 分别赋值给 first_name  last_name。然后,对于剩下的 location='普林斯顿', field='物理学', hobby='小提琴' 关键字参数,由于在函数定义中没有与之直接对应的普通形参,所以会被收集到 user_info 字典中。

*args **kwargs 核心目的是让函数能接收不定数量的参数,并以元组和字典的形式在函数内部处理。即当位置参数用完就自动变成 *args,当关键词参数用完就自动变成**kwarges

# 同时出现 *args 和 **kwargs,注意参数的传入顺序
def process_data(id_num, name, *tags, status="pending", **details): # 注意,这里的status 是仅关键字参数,必须通过关键词传值
    print(f"ID: {id_num}")
    print(f"Name: {name}")
    print(f"Tags (*args): {tags}")
    print(f"Status: {status}")          # status 是一个有默认值的普通关键字参数
    print(f"Details (**kwargs): {details}")
    print("-" * 20)

# 调用1:
process_data(101, "Alice", "vip", "new_user", location="USA", age=30)
# ID: 101
# Name: Alice
# Tags (*args): ('vip', 'new_user')  <-- "vip", "new_user" 是多余的位置参数,被 *tags 收集
# Status: pending                    <-- status 使用默认值,因为调用中没有 status=...
# Details (**kwargs): {'location': 'USA', 'age': 30} <-- location 和 age 是多余的关键字参数,被 **details 收集
# --------------------

# 调用2:
process_data(102, "Bob", status="active", department="Sales")
# ID: 102
# Name: Bob
# Tags (*args): ()                   <-- 没有多余的位置参数
# Status: active                     <-- status 被关键字参数 'active' 覆盖
# Details (**kwargs): {'department': 'Sales'} <-- department 是多余的关键字参数
# --------------------

# 调用3:
process_data(103, "Charlie", "admin") # 'admin' 会被 *tags 捕获
# ID: 103
# Name: Charlie
# Tags (*args): ('admin',)
# Status: pending
# Details (**kwargs): {}
# --------------------

# 调用4: (演示关键字参数也可以用于定义中的位置参数)
process_data(name="David", id_num=104, profession="Engineer")
# ID: 104
# Name: David
# Tags (*args): ()
# Status: pending
# Details (**kwargs): {'profession': 'Engineer'}
# --------------------

@浙大疏锦行


网站公告

今日签到

点亮在社区的每一天
去签到