【Python学习打卡-Day26】函数的艺术(上):从基础定义到参数魔法

📋 前言

各位伙伴们,大家好!从今天开始,我们将进入一个全新的专题------函数。如果说之前我们学习的变量、列表、循环是制造零件,那么函数就是教会我们如何设计和组装这些零件,构建出复杂而强大的机器。

在此之前,我们一直在 使用 函数,如 print()len()pd.read_csv()。今天,我们将掌握 创造 函数的能力。这不仅是代码复用的开始,更是我们程序设计思维的一次巨大飞跃。我们将学习:

  • 如何定义自己的函数?
  • 变量在函数内外的"生命周期"(作用域)。
  • 如何设计灵活的函数参数,应对各种输入情况(位置参数、默认参数、可变参数)。

让我们一起揭开函数参数的"魔法",打造属于自己的函数工具箱!


一、函数的核心:定义与作用域

1.1 函数的定义

函数就是一段被命名、可重复使用的代码块,用于执行特定的任务。基本语法如下:

python 复制代码
def function_name(parameter1, parameter2):
    """文档字符串 (Docstring): 解释函数的功能、参数和返回值"""
    # 函数体 (indented code block)
    result = parameter1 + parameter2
    return result # 使用 return 关键字返回值
  • def: 定义函数的关键字。
  • function_name: 函数名,遵循变量命名规则。
  • parameters: 传入函数的输入值。
  • return: 将函数执行的结果返回给调用者。如果省略,函数默认返回 None

1.2 变量作用域

  • 局部变量 (Local Scope): 在函数内部定义的变量,只在该函数内部有效。函数执行结束,它就消失了。
  • 全局变量 (Global Scope): 在所有函数之外定义的变量,在整个程序中都可访问。

原则:函数应尽量使用自己的局部变量和参数,避免修改全局变量,这会让代码更清晰、更易于维护。


二、参数的魔法:玩转函数输入

函数的强大之处在于其灵活性,而这份灵活性主要源于其多样的参数类型。

2.1 位置参数 (Positional Arguments)

最常见的参数类型,调用时必须按顺序提供值。

python 复制代码
def greet(name, message):
    print(f"Hello {name}, {message}")

greet("Alice", "Good morning!") # 'Alice' 对应 name, 'Good morning!' 对应 message

2.2 默认参数 (Default Arguments)

在定义函数时,可以为参数提供一个默认值。如果调用时不提供该参数,它就会使用这个默认值。

python 复制代码
def greet(name, message="How are you?"):
    print(f"Hello {name}, {message}")

greet("Bob") # message 使用默认值
greet("Charlie", "Nice to meet you!") # message 使用提供的新值

注意:默认参数必须放在所有位置参数的后面。

2.3 可变位置参数 (*args)

当你不知道函数会接收多少个位置参数时,可以使用 *args。它会将所有多余的位置参数收集到一个元组 (tuple) 中。

python 复制代码
def calculate_sum(*numbers):
    print(f"Received numbers: {numbers}") # numbers 是一个元组
    return sum(numbers)

calculate_sum(1, 2, 3) # 输出: Received numbers: (1, 2, 3)
calculate_sum(10, 20, 30, 40, 50)

2.4 可变关键字参数 (**kwargs)

当你希望函数能处理任意数量的关键字参数时,使用 **kwargs。它会将这些参数收集到一个字典 (dict) 中。

python 复制代码
def display_user_profile(**details):
    print("User Profile:")
    for key, value in details.items():
        print(f"  - {key.title()}: {value}")

display_user_profile(name="Eve", age=30, city="New York")

2.5 终极组合与顺序

当所有参数类型同时出现时,必须遵循以下严格的顺序:
位置参数, 默认参数, *args, **kwargs

python 复制代码
def master_function(pos1, pos2, default1="default", *args, **kwargs):
    print(f"Positionals: {pos1}, {pos2}")
    print(f"Default: {default1}")
    print(f"Args: {args}")
    print(f"Kwargs: {kwargs}")

编程好习惯:尾随逗号 (Trailing Comma)

在函数调用、列表、字典中,最后一个元素后面加一个逗号是完全合法的,比如 model(source=0, stream=True,)
好处:当未来增加新参数时,版本控制系统(如 Git)只会显示新增了一行,而不是修改了上一行,让代码变更历史更清晰。


三、作业实战:打造你的函数工具箱

现在,让我们用今天学到的知识完成所有练习题!

题目一:计算圆的面积 (位置参数 + 异常处理)

python 复制代码
import math

def calculate_circle_area(radius):
    """
    计算圆的面积,并处理无效的半径值。
    """
    # 使用 try-except 增加健壮性
    try:
        # 检查半径是否为负数
        if radius < 0:
            print("错误:半径不能为负数。")
            return 0
        
        area = math.pi * radius**2
        return area
    except TypeError:
        # 如果传入的不是数字类型,比如字符串
        print("错误:半径必须是一个数字。")
        return 0

# --- 测试 ---
print(f"半径为 5 的圆面积是: {calculate_circle_area(5):.2f}")
print(f"半径为 0 的圆面积是: {calculate_circle_area(0):.2f}")
print(f"半径为 -1 时返回: {calculate_circle_area(-1)}")
print(f"半径为 'text' 时返回: {calculate_circle_area('text')}")

输出:

复制代码
半径为 5 的圆面积是: 78.54
半径为 0 的圆面积是: 0.00
错误:半径不能为负数。
半径为 -1 时返回: 0
错误:半径必须是一个数字。
半径为 'text' 时返回: 0

题目二:计算矩形的面积 (多个位置参数)

python 复制代码
def calculate_rectangle_area(length, width):
    """计算矩形面积,并检查长宽是否为负。"""
    if length < 0 or width < 0:
        print("错误:长度和宽度都不能为负数。")
        return 0
    return length * width

# --- 测试 ---
print(f"长10宽5的矩形面积: {calculate_rectangle_area(10, 5)}")
print(f"长-10宽5的矩形面积: {calculate_rectangle_area(-10, 5)}")

输出:

复制代码
长10宽5的矩形面积: 50
错误:长度和宽度都不能为负数。
长-10宽5的矩形面积: 0

题目三:计算任意数量数字的平均值 (*args)

python 复制代码
def calculate_average(*args):
    """使用 *args 计算任意数量数字的平均值。"""
    # 处理没有输入任何数字的情况
    if not args:
        return 0
    
    total = sum(args)
    return total / len(args)

# --- 测试 ---
print(f"1, 2, 3 的平均值是: {calculate_average(1, 2, 3)}")
print(f"10, 20, 30, 40 的平均值是: {calculate_average(10, 20, 30, 40)}")
print(f"没有任何输入的平均值是: {calculate_average()}")

输出:

复制代码
1, 2, 3 的平均值是: 2.0
10, 20, 30, 40 的平均值是: 25.0
没有任何输入的平均值是: 0

题目四:打印用户信息 (**kwargs)

python 复制代码
def print_user_info(user_id, **kwargs):
    """打印用户ID和任意数量的额外信息。"""
    print(f"User ID: {user_id}")
    if kwargs:
        print("Additional Info:")
        for key, value in kwargs.items():
            print(f"  - {key}: {value}")
    else:
        print("No additional info provided.")

# --- 测试 ---
print_user_info("user123", name="Alice", age=30)
print("-" * 20)
print_user_info("user456", city="London", status="active", role="admin")
print("-" * 20)
print_user_info("user789")

输出:

复制代码
User ID: user123
Additional Info:
  - name: Alice
  - age: 30
--------------------
User ID: user456
Additional Info:
  - city: London
  - status: active
  - role: admin
--------------------
User ID: user789
No additional info provided.

题目五:格式化几何图形描述 (终极组合)

python 复制代码
def describe_shape(shape_name, color="black", **kwargs):
    """
    生成几何图形的描述字符串,结合了位置、默认和可变关键字参数。
    """
    # 描述尺寸的部分
    if kwargs:
        # 使用列表推导式和 join 方法优雅地格式化 kwargs
        dims_parts = [f"{key}={value}" for key, value in kwargs.items()]
        dims_str = " with dimensions: " + ", ".join(dims_parts)
    else:
        dims_str = " with no specific dimensions."

    # 组合成最终的描述
    description = f"A {color} {shape_name}{dims_str}"
    return description

# --- 测试 (与题目要求完全一致) ---
desc1 = describe_shape("circle", radius=5, color="red")
print(desc1)

desc2 = describe_shape("rectangle", length=10, width=4)
print(desc2)

desc3 = describe_shape("triangle", base=6, height=8, color="blue")
print(desc3)

desc4 = describe_shape("point", color="green")
print(desc4)

输出:

复制代码
A red circle with dimensions: radius=5
A black rectangle with dimensions: length=10, width=4
A blue triangle with dimensions: base=6, height=8
A green point with no specific dimensions.

四、总结与心得

Day 26 的学习是编程思维的一次重要转变。

  1. 从"重复"到"抽象" :我不再需要为计算不同尺寸的矩形面积而复制粘贴代码。通过定义一个 calculate_rectangle_area 函数,我将"计算矩形面积"这个概念抽象了出来,实现了代码复用。
  2. 拥抱"不确定性"*args**kwargs 给予了函数极大的灵活性。describe_shape 函数完美地展示了这一点,它不关心一个图形到底需要多少个维度参数(半径、长、宽、高...),它只负责接收并优雅地展示它们。这正是编写通用、可扩展接口的核心思想。
  3. 健壮性是一种习惯:结合 Day 25 的异常处理,我们可以在函数内部构建"安全区",使得函数在面对非预期输入时(如负半径),能给出友好反馈而不是程序崩溃。
  4. 代码即"文档" :一个命名良好、参数清晰的函数本身就是最好的文档。当我看到 calculate_average(*args) 时,我立刻就能理解它的用途。

函数是组织代码、管理复杂度的第一道防线。今天掌握的参数技巧,将成为我们未来构建任何复杂程序(包括机器学习模型和数据处理流程)的坚实基础。

再次感谢 @浙大疏锦行 老师设计的这套由浅入深的练习,让我对函数的理解从"知道"变成了"掌握"。

相关推荐
Coder_Boy_2 小时前
【人工智能应用技术】-基础实战-小程序应用(基于springAI+百度语音技术)智能语音控制-Java部分核心逻辑
java·开发语言·人工智能·单片机
MACKEI2 小时前
业务域名验证文件添加操作手册
java·开发语言
roman_日积跬步-终至千里2 小时前
【源码分析】StarRocks EditLog 写入与 Replay 完整流程分析
java·网络·python
车载测试工程师2 小时前
CAPL学习-AVB交互层-媒体函数1-回调&基本函数
网络·学习·tcp/ip·媒体·capl·canoe
apihz2 小时前
货币汇率换算免费API接口(每日更新汇率)
android·java·开发语言
gf13211112 小时前
python_检测音频人声片段
开发语言·python·音视频
爱笑的眼睛112 小时前
Flask上下文API:从并发陷阱到架构原理解析
java·人工智能·python·ai
richxu202510012 小时前
嵌入式学习之路-->stm32篇-->(0)学习路线
stm32·嵌入式硬件·学习
程序猿追2 小时前
体验LongCat-Image-Edit图像编辑模型:在昇腾NPU上的部署与推理全流程分享
python·大模型·华为云