【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) 时,我立刻就能理解它的用途。

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

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

相关推荐
故事不长丨42 分钟前
C#正则表达式完全攻略:从基础到实战的全场景应用指南
开发语言·正则表达式·c#·regex
梨落秋霜1 小时前
Python入门篇【文件处理】
android·java·python
Java 码农1 小时前
RabbitMQ集群部署方案及配置指南03
java·python·rabbitmq
哈库纳玛塔塔1 小时前
放弃 MyBatis,拥抱新一代 Java 数据访问库
java·开发语言·数据库·mybatis·orm·dbvisitor
phltxy2 小时前
从零入门JavaScript:基础语法全解析
开发语言·javascript
天“码”行空2 小时前
java面向对象的三大特性之一多态
java·开发语言·jvm
张登杰踩2 小时前
VIA标注格式转Labelme标注格式
python
气概2 小时前
法奥机器人学习使用
学习·junit·机器人
Qhumaing3 小时前
C++学习:【PTA】数据结构 7-1 实验7-1(最小生成树-Prim算法)
c++·学习·算法
Learner3 小时前
Python数据类型(四):字典
python