跳至正文
来两杯美式
返回

Python 知识系列(三):从 0 开始理解函数

By 来两杯美式
发布于更新于

1. 什么是函数?

想象一下,你正在写一个程序,需要反复计算两个数的和并打印出来。你可能会这样写:

a = 5
b = 10
sum_val = a + b
print(f"{a}{b} 的和是 {sum_val}")

c = 20
d = 30
sum_val = c + d
print(f"{c}{d} 的和是 {sum_val}")
# ... 如果还需要计算,就得再写一遍 ...

你会发现,这段代码很重复。如果计算逻辑稍微复杂一点,比如要计算平均值、方差等,重复的代码会更多,而且一旦逻辑需要修改,你就得去改每一个地方,非常麻烦。

函数(Function) 就是为了解决这个问题而生的。它是一段可重用的代码块,你可以给它起一个名字,然后在需要的时候随时调用(Call)它。

我们可以把上面的代码改写成一个函数:

# 定义一个名为 'add_and_print' 的函数
def add_and_print(num1, num2):
    sum_val = num1 + num2
    print(f"{num1}{num2} 的和是 {sum_val}")

# 现在,需要计算时,只需调用函数即可
add_and_print(5, 10)
add_and_print(20, 30)
add_and_print(100, -50)

输出结果:

5 和 10 的和是 15
20 和 30 的和是 50
100 和 -50 的和是 50

看,代码是不是变得简洁、清晰多了?这就是函数的核心价值:代码复用、提高模块化和可读性


2. 如何定义一个函数

Python中定义函数的语法非常简单,使用 def 关键字。

def function_name(parameter1, parameter2, ...):
    """
    这里是文档字符串(Docstring),用来解释函数的功能。
    这是一个好习惯!
    """
    # 函数体(Function Body)
    # 这里是函数的具体逻辑代码
    return some_value  # 使用 return 关键字返回结果

让我们分解一下这个结构:

组成部分说明
def定义函数的关键字,告诉Python:“嘿,我要创建一个新函数了!”
function_name你给函数起的名字。命名规则和变量一样(通常用小写字母和下划线,例如 calculate_average
()括号是必须的
parameter1, parameter2参数(Parameters),它们是函数接收的输入值。就像是函数内部的临时变量。参数是可选的,函数也可以没有参数
:冒号也是必须的,表示函数定义的开始
文档字符串(Docstring)一个可选的字符串,用于解释函数的作用、参数和返回值。强烈建议为你的函数编写文档字符串,这样你或其他人将来能快速理解它的用途
函数体缩进的代码块,包含了函数要执行的所有操作
returnreturn 关键字用于从函数中返回一个结果。执行到 return 时,函数会立即结束。如果一个函数没有 return 语句,它会默认返回 None

3. 参数(Arguments)的多种形式

参数是函数最灵活的部分,Python提供了多种定义和传递参数的方式。

a. 位置参数 (Positional Arguments)

这是最常见的类型。调用函数时,传递的值会按照位置顺序依次赋值给对应的参数。

def describe_pet(animal_type, pet_name):
    print(f"我有一只{animal_type}。")
    print(f"它的名字叫{pet_name}。")

describe_pet("仓鼠", "哈哈")  # "仓鼠" 对应 animal_type, "哈哈" 对应 pet_name

⚠️ 如果你搞错了顺序,结果也会出错:

describe_pet("哈哈", "仓鼠")  # 输出就会很奇怪

b. 关键字参数 (Keyword Arguments)

为了避免位置混乱,你可以使用 参数名=值 的形式来传递参数。这样就无需关心参数的顺序了。

describe_pet(animal_type="", pet_name="咪咪")
describe_pet(pet_name="旺财", animal_type="")  # 顺序变了,但结果正确

c. 默认参数 (Default Arguments)

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

⚠️ 注意:带默认值的参数必须放在没有默认值的参数后面!!!

def describe_pet(animal_type, pet_name="大黄"):  # pet_name 有了默认值
    print(f"我有一只{animal_type}。")
    print(f"它的名字叫{pet_name}。")

describe_pet("")           # 没有提供 pet_name,使用默认值 "大黄"
describe_pet("兔子", "小白")  # 提供了,就使用 "小白"

d. 限定参数类型

在定义函数时,可以为参数指定一个参数类型。加入参数类型的好处有:

这种限制参数类型的功能,在Python中被称为 类型提示(Type Hints)类型注解(Type Annotations)

⚠️ 注意:类型提示只是一个”提示”或”注解”,Python解释器在运行时不会强制检查类型。如果一个字符串被传递给了期望是整数的参数,代码仍然会运行(并可能在函数内部某处报错)。它的主要目的是给开发者和工具(如IDE、静态类型检查器mypy)看的。

def describe_pet(animal_type: int, pet_name="大黄"):  # animal_type 类型为 int
    print(f"我有一只{animal_type}。")
    print(f"它的名字叫{pet_name}。")

describe_pet("")  # 给 animal_type 传 str,依旧正常运行

e. 可变参数 (*args**kwargs)

有时候,你希望函数能处理任意数量的参数。

*args (非关键字可变参数)

它会把多余的位置参数收集成一个元组(Tuple)

def make_pizza(size, *toppings):
    print(f"制作一个 {size} 寸的披萨,配料如下:")
    for topping in toppings:
        print(f"- {topping}")

make_pizza(12, "蘑菇")
make_pizza(9, "香肠", "青椒", "奶酪")

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

它会把多余的关键字参数收集成一个字典(Dictionary)

def build_profile(first_name, last_name, **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="物理")
print(user_profile)
# 输出: {'first_name': '爱因斯坦', 'last_name': '阿尔伯特', 'location': '普林斯顿', 'field': '物理'}

扩展

Python函数参数的完整顺序规则如下,这是一个不可违反的硬性规定,必须按照顺序定义:

【位置或关键字参数】(无默认值) -> 【位置或关键字参数】(有默认值) -> 【*args】 -> 【仅关键字参数】(可选) -> 【**kwargs】
def complex_function(
    # 1. 位置参数(无默认值,必须传)- 最前面
    mandatory_arg1: int,
    mandatory_arg2: str,

    # 2. 位置或关键字参数(有默认值)- 中间
    default_arg1: int = 100,
    default_arg2: str = "hello",

    # 3. 可变位置参数 *args
    *args: str,

    # 4. 仅关键字参数(无默认值)
    keyword_only1: int,
    keyword_only2: str,

    # 4.1 仅关键字参数(有默认值)
    keyword_only_with_default: int = 200,

    # 5. 可变关键字参数 **kwargs
    **kwargs: str
) -> None:
    """
    一个包含所有类型参数的复杂函数示例
    """
    print(f"必须传的位置参数: {mandatory_arg1}, {mandatory_arg2}")
    print(f"有默认值的位置或关键字参数: {default_arg1}, {default_arg2}")

    if args:
        print(f"可变位置参数 *args: {args}")

    print(f"仅关键字参数: {keyword_only1}, {keyword_only2}, {keyword_only_with_default}")

    if kwargs:
        print(f"可变关键字参数 **kwargs: {kwargs}")

# 正确的调用方式
complex_function(
    10,                  # mandatory_arg1
    "mandatory",         # mandatory_arg2
    # default_arg1 和 default_arg2 有默认值,可以不传
    "arg1", "arg2",      # 这些会被 *args 接收
    keyword_only1=30,    # 必须用关键字传
    keyword_only2="world",  # 必须用关键字传
    # keyword_only_with_default 有默认值,可以省略
    extra1="value1",     # 会被 **kwargs 接收
    extra2="value2"      # 会被 **kwargs 接收
)

另外参数中还可能有 /*,简要了解即可:

def advanced_example(
    positional_only,      # 必须用位置传参
    /,                    # / 之前的参数只能用位置传参
    positional_or_keyword,
    *,
    keyword_only          # * 之后的参数只能用关键字传参
):
    pass

# 以下两种调用方式都可以
advanced_example("positional_only", "keyword", keyword_only="keyword_only")
advanced_example("positional_only", positional_or_keyword="keyword", keyword_only="keyword_only")

4. 返回值 (return)

函数不仅可以做事(比如打印),更重要的是它可以”产出”结果。return 语句就是函数的出口。

a. 返回单个值

def add(a, b):
    return a + b

result = add(10, 20)  # result 的值现在是 30
print(result)

b. 返回多个值

Python函数可以一次返回多个值,它们会被打包成一个元组

def get_name_and_age():
    name = "Tom"
    age = 25
    return name, age  # 实际上是返回了 ('Tom', 25)

user_name, user_age = get_name_and_age()  # 可以用多个变量来接收(解包)
print(f"姓名: {user_name}, 年龄: {user_age}")

c. 返回空值

如果函数执行完毕都没有遇到 return,或者只有一个空的 return,它会默认返回 NoneNone 是Python中一个特殊的值,表示”什么都没有”。

def do_nothing():
    print("我做了一些事,但没有返回任何东西")

result = do_nothing()
print(result)  # 输出: None

5. 函数也是一等公民

Python中的一个核心特性:函数是”一等公民”(First-Class Citizens)

这意味着,函数可以像任何其他数据类型(比如整数、字符串、列表)一样被对待:

简单来说,在Python中,函数和整数、字符串、列表等数据类型拥有完全相同的”权利”。让我们通过具体例子来理解这4个特性:

特性1:函数可以被赋值给变量

def say_hello(name):
    return f"Hello, {name}!"

# 1. 把函数赋值给一个变量
greet = say_hello  # 注意:没有括号,我们是在引用函数本身

print(say_hello("Alice"))    # 输出: Hello, Alice!
print(greet("Bob"))          # 输出: Hello, Bob!
print(type(say_hello))       # 输出: <class 'function'>
print(type(greet))           # 输出: <class 'function'>

say_hello 这个函数被赋值给了 greet 变量,就像你把数字 5 赋值给变量一样自然。

特性2:函数可以作为参数传递给其他函数

这是理解高阶函数回调函数的基础。

def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

def apply_operation(num1, num2, operation_func):
    """
    operation_func 是一个函数参数
    """
    result = operation_func(num1, num2)
    print(f"运算结果是: {result}")

# 把函数作为参数传递
apply_operation(10, 20, add)        # 输出: 运算结果是: 30
apply_operation(10, 20, multiply)   # 输出: 运算结果是: 200

实际应用:自定义排序

students = ["Alice", "Bob", "Charlie", "David"]

# sorted() 函数的 key 参数接收的就是一个函数
sorted_by_length = sorted(students, key=len)
print(sorted_by_length)  # ['Bob', 'Alice', 'David', 'Charlie']

特性3:函数可以作为其他函数的返回值

这个特性是闭包(Closure)装饰器的基础。

def get_greeter(greeting_style):
    """
    这个函数返回另一个函数
    """
    if greeting_style == "formal":
        def formal_greet(name):
            return f"Good day, {name}."
        return formal_greet
    else:
        def casual_greet(name):
            return f"Hey {name}!"
        return casual_greet

# 获得不同的问候函数
formal_greeter = get_greeter("formal")
casual_greeter = get_greeter("casual")

print(formal_greeter("Mr. Smith"))   # 输出: Good day, Mr. Smith.
print(casual_greeter("Tom"))         # 输出: Hey Tom!

特性4:函数可以存储在数据结构中

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

def multiply(a, b):
    return a * b

# 把函数放在列表中
operations = [add, subtract, multiply]
a, b = 15, 5

for operation in operations:
    result = operation(a, b)
    print(f"{operation.__name__}: {result}")

# 输出
# add: 20
# subtract: 10
# multiply: 75

💡 在使用中需要注意区分带不带括号func 是函数本身,func() 是调用函数。


📌 “函数是一等公民”意味着在Python中,函数享有和所有其他数据类型完全相同的权利。这个看似简单的特性,实际上是Python函数式编程能力、装饰器、回调机制等高级特性的基石

理解这个概念,就能明白为什么Python能做这么多灵活而强大的事情!


6. 变量的作用域 (Scope)

这是一个非常重要的概念!变量不是在程序的任何地方都可以访问的,它有自己的”作用范围”。

局部变量 (Local Scope)

函数内部定义的变量,只在该函数内部有效。函数执行结束后,它就会被销毁。

def my_func():
    message = "Hello, I am a local variable."
    print(message)

my_func()
# print(message)  # 这行会报错!因为在函数外部无法访问 message

全局变量 (Global Scope)

所有函数之外定义的变量,可以在程序的任何地方被访问。

app_name = "我的应用"

def print_app_name():
    print(f"欢迎来到 {app_name}")  # 可以读取全局变量

print_app_name()

⚠️ 注意:通常不建议在函数内部直接修改全局变量,这会让代码难以维护。如果确实需要修改,要使用 global 关键字声明。


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
所属专题
Python
第 3 / 8 篇
查看系列全部文章
  1. 01.Python 知识系列(一):极简核心语法
  2. 02.Python 知识系列(二):面向对象编程
  3. 03.Python 知识系列(三):从 0 开始理解函数
  4. 04.Python 知识系列(四):装饰器详解
  5. 05.Python 知识系列(五):模块和包
  6. 06.Python 知识系列(六):异步编程
  7. 07.Python 知识系列(七):虚拟环境与依赖管理
  8. 08.Python 知识系列(八):企业级开发场景最佳实践

上一篇
Python 知识系列(四):装饰器详解
下一篇
Python 知识系列(二):面向对象编程