Python 教程 / 函数基础

函数基础

函数是Python中最基本的代码抽象单元。通过函数,我们可以将复杂的计算过程封装成可重用的代码块,让程序更加模块化、易于理解和维护。

为什么需要函数?

假设要计算圆的面积,公式是 S = πr²。如果不用函数,我们需要重复写计算代码:

Python
# 每次计算都要写这个公式
r1 = 12.34
s1 = 3.14 * r1 * r1
print(s1)

r2 = 9.08
s2 = 3.14 * r2 * r2
print(s2)

r3 = 73.1
s3 = 3.14 * r3 * r3
print(s3) 

问题:

  • 代码重复,写起来麻烦
  • 如果要改公式(比如把π改成更精确的值),需要改很多地方
  • 代码可读性差

用函数封装后:

Python
def area_of_circle(r):
    return 3.14 * r * r

print(area_of_circle(12.34))
print(area_of_circle(9.08))
print(area_of_circle(73.1)) 
函数的优点
  • 代码复用:一次定义,多次调用
  • 易于维护:修改只需改一处
  • 提高可读性:函数名表达业务含义
  • 模块化:将大问题拆成小问题

抽象:程序设计的核心思想

┌─────────────────────────────────────────────────────────────────────┐
│                         抽象的力量                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  数学中的抽象:                                                       │
│                                                                     │
│    ∑(n=1 to 100) n²   ─── 抽象记法,一眼看出是"求平方和"              │
│         │                                                           │
│         ▼                                                           │
│    (1²+1) + (2²+1) + ... + (100²+1)  ─── 具体计算过程               │
│                                                                     │
│  程序中的抽象:                                                       │
│                                                                     │
│    area_of_circle(r)  ─── 抽象,一眼看出是"计算圆面积"               │
│         │                                                           │
│         ▼                                                           │
│    3.14 * r * r       ─── 具体实现细节                             │
│                                                                     │
│  关键: 抽象让你关心"做什么",不关心"怎么做"                           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘ 

定义函数

使用def关键字定义函数,语法格式为:

┌─────────────────────────────────────────────────────────────────────┐
│                        def 函数名(参数):                              │
│                            函数体                                    │
│                            [return 返回值]                           │
└─────────────────────────────────────────────────────────────────────┘ 
Python
# 无参数函数
def say_hello():
    print("Hello, World!")

# 带一个参数的函数
def greet(name):
    print(f"你好,{name}!")

# 带多个参数的函数
def add(a, b):
    return a + b

# 调用函数
say_hello()              # Hello, World!
greet("张三")          # 你好,张三!
result = add(1, 2)   # result = 3
print(result) 

返回值详解

函数可以使用return语句返回值。如果没有return语句或return后没有值,函数返回None

┌─────────────────────────────────────────────────────────────────────┐
│                        return 执行流程                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  def func(x):                                                       │
│      if x < 0:                                                      │
│          return None    ◀── 提前返回                                │
│      return x * 2        ◀── 正常返回                                │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐   │
│  │  调用 func(-5)  ──▶  x = -5  ──▶  x < 0  ──▶  return None    │   │
│  │  调用 func(10)  ──▶  x = 10  ──▶  x >= 0 ──▶  return 20     │   │
│  └───────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘ 
Python
# 单返回值
def square(x):
    return x ** 2

print(square(5))       # 25

# 多返回值 (实际返回的是元组)
def divide(a, b):
    quotient = a // b
    remainder = a % b
    return quotient, remainder

result = divide(17, 5)
print(result)         # (3, 2)
print(result[0])     # 3
print(result[1])     # 2

# 简洁的元组解包
q, r = divide(17, 5)
print(f"商={q}, 余数={r}")  # 商=3, 余数=2

# 没有 return 语句
def do_nothing():
    pass

print(do_nothing())  # None

# return 后没有值
def maybe_return(x):
    if x < 0:
        return
    return x * 2

print(maybe_return(-5))   # None
print(maybe_return(10))   # 20 

函数调用的内存模型

┌─────────────────────────────────────────────────────────────────────┐
│                     函数调用内存模型                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  代码:                                                              │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  x = 10                     # 变量 x = 10                   │   │
│  │  y = square(x)             # 调用函数                       │   │
│  │                              # 1. 创建栈帧                  │   │
│  │                              # 2. 参数 x=10 传入            │   │
│  │                              # 3. 计算 x**2 = 100           │   │
│  │                              # 4. 返回 100                   │   │
│  │                              # 5. 栈帧销毁                    │   │
│  │  y = 100                    # 结果赋值给 y                  │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  栈帧示意图:                                                        │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │   调用前:          调用中 (square函数):     调用后:         │   │
│  │                                                             │   │
│  │   ┌─────┐         ┌─────────────────┐        ┌─────┐        │   │
│  │   │ x=10│         │ square栈帧:     │        │ x=10│        │   │
│  │   └─────┘         │   参数: x=10   │        │ y=100│       │   │
│  │                    │   局部: 无     │        └─────┘        │   │
│  │                    │   返回: 100    │                      │   │
│  │                    └─────────────────┘                      │   │
│  │                                                             │   │
│  │                    主调者栈帧    被调者栈帧                  │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  关键点:                                                            │
│  • 每个函数调用都会创建一个新的栈帧                                  │
│  • 函数执行完毕,栈帧被销毁                                          │
│  • 局部变量在函数外部无法访问                                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘ 

默认参数

为参数指定默认值,调用时可以省略该参数。

Python
def greet(name, greeting="你好", punctuation="!"):
    return f"{greeting},{name}{punctuation}"

# 使用所有参数
print(greet("张三", "早上好", "~"))   # 早上好,张三~

# 省略默认参数
print(greet("李四"))                 # 你好,李四!

# 只提供部分默认参数
print(greet("王五", "晚上好"))      # 晚上好,王五! 
默认参数陷阱

不要使用可变对象作为默认参数!

# 错误示例 (可变对象作为默认参数)
def bad_func(items=[]):   # 危险![] 是可变对象
    items.append(1)
    return items

print(bad_func())  # [1]
print(bad_func())  # [1, 1]  -- 不是 [1]!

# 正确示例
def good_func(items=None):
    if items is None:
        items = []
    items.append(1)
    return items

print(good_func())  # [1]
print(good_func())  # [1]  -- 正确! 
┌─────────────────────────────────────────────────────────────────────┐
│                   默认参数陷阱原因分析                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  def bad_func(items=[]):  # 默认参数在函数定义时创建                  │
│                                                                     │
│  调用bad_func()第一次:                                               │
│    items = [] (使用默认的同一个列表对象)                              │
│    items.append(1)  ──▶  items = [1]                                │
│    函数结束,items引用被删除,但默认列表对象还在!                     │
│                                                                     │
│  调用bad_func()第二次:                                               │
│    items = [] (使用的是同一个默认列表对象,已经有[1])                 │
│    items.append(1)  ──▶  items = [1, 1]                             │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  内存中:                                                     │   │
│  │  ┌─────────────────┐                                        │   │
│  │  │ 默认列表对象:   │  ← 第一次调用后被修改                  │   │
│  │  │   [1]          │  ← 第二次调用时直接使用                 │   │
│  │  └─────────────────┘                                        │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  解决: 使用 None 作为默认值,在函数内部创建新列表                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘ 

变量作用域

Python中有四种作用域,从内到外是:局部( Local ) → 闭包( Enclosing ) → 全局( Global ) → 内置( Built-in )

┌─────────────────────────────────────────────────────────────────────┐
│                        LEGB 作用域规则                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌─────────────────────────────────────────────────────────────┐  │
│   │ Built-in:    len, print, range 等内置函数/常量               │  │
│   ├─────────────────────────────────────────────────────────────┤  │
│   │ Global:      模块顶部定义的变量                              │  │
│   ├─────────────────────────────────────────────────────────────┤  │
│   │ Enclosing:   嵌套函数的外层函数的变量                        │  │
│   ├─────────────────────────────────────────────────────────────┤  │
│   │ Local:       函数内部定义的变量                              │  │
│   └─────────────────────────────────────────────────────────────┘  │
│                                                                     │
│   查找顺序: L → E → G → B                                          │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘ 
Python
x = 10          # 全局变量
z = 100         # 全局变量

def outer():
    y = 20      # 闭包变量 (enclosing)

    def inner():
        x = 30  # 局部变量 (local)
        print(f"局部变量 x = {x}")     # 30
        print(f"闭包变量 y = {y}")     # 20
        print(f"全局变量 z = {z}")     # 100

    inner()
    # print(x)  # 错误!x 是 inner 的局部变量

outer()
print(f"全局变量 x = {x}")         # 10 
Python
# global 语句: 在函数内修改全局变量
counter = 0

def increment():
    global counter    # 声明要使用全局变量
    counter += 1
    return counter

print(increment())  # 1
print(increment())  # 2
print(counter)       # 2

# nonlocal 语句: 在嵌套函数中修改闭包变量
def outer():
    count = 0

    def inner():
        nonlocal count
        count += 1
        return count

    return inner

func = outer()
print(func())  # 1
print(func())  # 2
print(func())  # 3 
慎用 global

过度使用global会使代码难以理解和维护。推荐的做法是:

  • 尽量通过参数传递和返回值处理数据
  • 使用类来封装相关的数据和函数
  • global只用于保存全局配置等少数场景

pass语句

pass是空操作语句,不做任何事情,用作占位符。

Python
# 定义空函数 (还没想好函数体)
def todo_func():
    pass    # TODO: 实现这个函数

# 条件分支占位
if condition:
    pass    # 暂时什么都不做
else:
    print("执行其他操作")

# 类定义占位
class EmptyClass:
    pass 

文档字符串 (Docstring)

在函数开头使用三引号添加函数说明,这些内容可以通过__doc__属性访问。

Python
def calculate_area(radius, pi=3.14159):
    """
    计算圆的面积

    参数:
        radius: 圆的半径 (必须)
        pi: 圆周率 (可选,默认 3.14159)

    返回:
        圆的面积 (float)

    示例:
        >>> calculate_area(5)
        78.53975
        >>> calculate_area(5, 3.14)
        78.5
    """
    return pi * radius ** 2

# 查看文档
print(calculate_area.__doc__)

# 使用 help 函数查看格式化后的文档
help(calculate_area) 
┌─────────────────────────────────────────────────────────────────────┐
│                      Docstring 风格指南                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  单行 Docstring:                                                    │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │ def add(a, b):                                                │  │
│  │     """Return the sum of a and b."""                          │  │
│  │     return a + b                                              │  │
│  └───────────────────────────────────────────────────────────────┘  │
│                                                                     │
│  多行 Docstring:                                                    │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │ def func(arg1, arg2):                                         │  │
│  │     """Summary line.                                          │  │
│  │                                                                │  │
│  │     Extended description of the function.                     │  │
│  │                                                                │  │
│  │     Args:                                                     │  │
│  │         arg1: Description of arg1                            │  │
│  │         arg2: Description of arg2                             │  │
│  │                                                                │  │
│  │     Returns:                                                  │  │
│  │         Description of return value                          │  │
│  │     """                                                       │  │
│  └───────────────────────────────────────────────────────────────┘  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘ 

函数是一等公民

在Python中,函数和其他值一样,可以:

  • 赋值给变量
  • 作为参数传递给其他函数
  • 作为函数的返回值
Python
# 函数赋值给变量
def square(x):
    return x ** 2

my_func = square    # 不用括号,只是把函数赋值给变量
print(my_func(5))      # 25

# 函数作为参数
def apply_twice(func, x):
    return func(func(x))

def double(x):
    return x * 2

print(apply_twice(double, 5))   # 20  (5*2*2)

# 函数作为返回值
def make_multiplier(factor):
    def multiply(x):
        return x * factor
    return multiply

times_3 = make_multiplier(3)
times_5 = make_multiplier(5)

print(times_3(10))    # 30
print(times_5(10))    # 50 

章节测验

问题1: 下面代码的输出是什么?

def func(x, y=5):
    return x + y

print(func(3)) 

问题2: 以下哪个是Python函数的正确返回值?

问题3: 函数内部可以修改全局变量吗?

问题4: 下面哪个是使用可变对象作为默认参数的问题?

练习题

练习1: 温度转换函数

编写华氏温度转摄氏温度的函数,公式:C = (F - 32) * 5 / 9

输入: fahrenheit_to_celsius(100)
输出: 37.77777777777778 
练习2: 回文判断

编写函数判断一个字符串是否是回文(正读和反读都一样)。

输入: is_palindrome("上海自来水来自海上")
输出: True 
练习3: 计算阶乘

编写函数计算 n 的阶乘(使用递归或循环实现)。

输入: factorial(5)
输出: 120 (5! = 5*4*3*2*1) 
🐍 Python 在线代码编辑器
📝 运行结果