← 返回 Day 11 正文

Day 11 · 图解

Day 011 — 函数基础图解

一图胜千言 —— 函数机制的可视化


1. 函数的本质

flowchart LR A[输入/参数] --> B[函数体/处理逻辑] B --> C[输出/返回值] style A fill:#e1f5fe style B fill:#fff3e0 style C fill:#e8f5e9
flowchart LR
    A[输入/参数] --> B[函数体/处理逻辑]
    B --> C[输出/返回值]
    
    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e9

函数 = 输入 → 处理 → 输出


2. 函数定义与调用的执行流程

                         调用栈 (Call Stack)
    ┌────────────────────────────────────────────────────┐
    │                                                    │
    │  调用 greet("张三")                                  │
    │                                                    │
    │  步骤:                                              │
    │  ① 查找 greet 函数对象                               │
    │  ② 创建新的栈帧 (Stack Frame)                       │
    │  ③ 绑定参数: name = "张三"                            │
    │  ④ 执行函数体                                       │
    │  ⑤ 销毁栈帧                                         │
    │  ⑥ 返回调用点继续执行                                 │
    │                                                    │
    │  ┌──────────────────────────────────────┐          │
    │  │  [栈顶 - 当前活跃]                     │          │
    │  │  greet_scope                          │          │
    │  │  ┌────────────────────────────────┐   │          │
    │  │  │  name = "张三"                  │   │          │
    │  │  │  result = "你好, 张三!"          │   │          │
    │  │  └────────────────────────────────┘   │          │
    │  ├──────────────────────────────────────┤          │
    │  │  [栈底 - 全局]                        │          │
    │  │  global_scope                        │          │
    │  │  ┌────────────────────────────────┐   │          │
    │  │  │  greet = <function>             │   │          │
    │  │  │  say_hello = <function>         │   │          │
    │  │  │  add = <function>               │   │          │
    │  │  └────────────────────────────────┘   │          │
    │  └──────────────────────────────────────┘          │
    └────────────────────────────────────────────────────┘

3. 参数传递机制详解

不可变类型(int)

调用前:                        调用中:                    调用后:
n = 5                         ┌───────────────┐          n = 5
n ───→ [int: 5]               │ x ──→ [int:5] │          n ───→ [int: 5]
                               │               │
                               │ x = 100       │
                               │ x ──→ [int:100]│
                               └───────────────┘

可变类型(list)—— 修改内容

调用前:                        调用中:                    调用后:
my_list = [1,2,3]             ┌──────────────────┐       my_list = [1,2,3,4]
my_list ──→ [1,2,3]           │ lst ──→ [1,2,3]  │       my_list ──→ [1,2,3,4]
                               │           │      │
                               │     lst.append(4)  │
                               │           │      │
                               │           ▼      │
                               │         [1,2,3,4] │
                               └──────────────────┘

重新绑定 vs 修改对象

重新绑定 (不影响外部):               修改对象 (影响外部):

外部:                      外部:
[1, 2, 3]                  [1, 2, 3]
    ↑                          ↑
    │                          │
lst (参数) ──→ 断开了!     lst (参数) ──→ 仍然指向同一个
    │                          │
    ▼                          ▼
[100, 200, 300]            [1, 2, 3, 999]
(新对象,外部再也看不到了)    (外部能感知到这个修改)

4. return 的执行流程

函数调用
    │
    ▼
┌──────────────────┐
│  执行函数体        │
└──────┬───────────┘
       │
       ▼
┌──────────────────┐
│  遇到 return 语句  │
│       或          │
│  函数体执行完毕     │
└──────┬───────────┘
       │
       ▼
┌──────────────────────────────────┐
│  计算 return 后面的表达式(如果有) │
│  return a + b                    │
│         ↓                        │
│     计算 a + b → 得到结果         │
└──────────────┬───────────────────┘
               │
               ▼
┌──────────────────┐
│  销毁当前栈帧      │  ← 局部变量消失
└──────┬───────────┘
       │
       ▼
┌──────────────────┐
│  返回值传递给调用者  │
└──────────────────┘

5. 文档字符串(docstring)结构

def function_name(param1, param2):
    """
    ┌─────────────────────────────────────────┐
    │  一句话描述                              │  ← 必须
    │                                         │
    │  详细描述——可以多行                        │  ← 可选,复杂函数推荐
    │  解释功能、注意事项、算法简要说明              │
    │                                         │
    │  Args:                                  │  ← 参数说明
    │      param1 (type): 描述                  │
    │      param2 (type, optional): 描述        │
    │                                         │
    │  Returns:                               │  ← 返回值说明
    │      type: 描述                           │
    │                                         │
    │  Raises:                                │  ← 异常说明(可选)
    │      ValueError: 什么情况下抛出            │
    │                                         │
    │  Example:                               │  ← 示例(可选)
    │      >>> func(1, 2)                      │
    │      3                                   │
    └─────────────────────────────────────────┘
    """
    pass

6. 函数是一等公民

Python 中的数据类型 "地位" 对比:

┌─────────────────────────────────────────────────────────┐
│                   数据一等公民                            │
│                                                          │
│  操作               int  str  list  dict  tuple  FUNCTION│
│  ─────────────────────────────────────────────────────  │
│  赋值给变量          ✓    ✓    ✓     ✓     ✓       ✓    │
│  作为参数传递        ✓    ✓    ✓     ✓     ✓       ✓    │
│  作为返回值          ✓    ✓    ✓     ✓     ✓       ✓    │
│  存储在容器中        ✓    ✓    ✓     ✓     ✓       ✓    │
│  比较相等性          ✓    ✓    ✓     ✓     ✓       ✓    │
│                                                          │
│  结论: 函数和 int、str 一样,是一等公民!                   │
└─────────────────────────────────────────────────────────┘

7. 计算器函数库架构

┌──────────────────────────────────────────────────────────┐
│                  计算器函数库 (Calculator Library)         │
│                                                           │
│  函数数据流:                                               │
│                                                           │
│  用户输入                                                  │
│      │                                                    │
│      ▼                                                    │
│  ┌──────────┐     ┌──────────────┐     ┌─────────────┐   │
│  │ 输入解析  │ ──→ │ 命令路由分发   │ ──→ │ 调用对应函数  │   │
│  │ split()  │     │ 判断运算符    │     │ add/sub/... │   │
│  └──────────┘     └──────────────┘     └──────┬──────┘   │
│                                                │         │
│                                                ▼         │
│                                         ┌─────────────┐  │
│                                         │ 返回结果      │  │
│                                         └──────┬──────┘  │
│                                                │         │
│                                                ▼         │
│                                         ┌─────────────┐  │
│                                         │ 格式化输出    │  │
│                                         │ 显示给用户    │  │
│                                         └─────────────┘  │
│                                                           │
└──────────────────────────────────────────────────────────┘

8. 牛顿迭代法求平方根(可视化)

求 sqrt(10)

第一次迭代: x₀ = 10
    切线: f(x) = x² - 10
    x₁ = (10 + 10/10) / 2 = 5.5

第二次迭代: x₁ = 5.5
    x₂ = (5.5 + 10/5.5) / 2 = 3.66

第三次迭代: x₂ = 3.66
    x₃ = (3.66 + 10/3.66) / 2 = 3.20

第四次迭代: x₃ = 3.20
    x₄ = (3.20 + 10/3.20) / 2 = 3.16

第五次迭代: x₄ = 3.16
    x₅ = (3.16 + 10/3.16) / 2 = 3.1623 ✅

收敛过程:
10.00 → 5.50 → 3.66 → 3.20 → 3.16 → 3.1623 (实际: 3.16227766)

9. 函数设计原则(单一职责)

❌ 不好的设计:                           ✅ 好的设计:
                                        │
def process_data(data):                 def clean_data(data):
    # 清洗                                  ...
    # 分析                                  return cleaned
    # 画图                                │
    # 发邮件                              def analyze_data(data):
    # 存数据库                               ...
    pass                                    return results
                                        │
  这个函数做太多事了!                       def plot_data(results):
  任何一个功能的改动都可能影响其他功能:           ...
  测试困难                               │
  复用困难                                def export_results(results):
                                              ...

10. 总结:函数的核心概念关系图

flowchart TD DEF[def 关键字定义函数] --> PARAM[参数列表] DEF --> BODY[函数体] BODY --> DOC[文档字符串 docstring] BODY --> CODE[执行代码] BODY --> RET[return 返回值] PARAM --> IMMUT[不可变: int/str/tuple] PARAM --> MUT[可变: list/dict/set] IMMUT --> VAL[值传递错觉<br/>重新绑定不影响外部] MUT --> REF[引用传递<br/>修改内容影响外部] RET --> SINGLE[单个值] RET --> MULTI[多个值 -> 元组] RET --> NONE[没有return -> None] DEF --> CITIZEN[一等公民 First-class] CITIZEN --> ASSIGN[赋值给变量] CITIZEN --> PASS[作为参数传递] CITIZEN --> RETURNED[作为返回值] DEF --> PRINCIPLE[设计原则] PRINCIPLE --> SRP[单一职责原则] PRINCIPLE --> NAMING[命名规范] PRINCIPLE --> DOC_STR[写 docstring] style DEF fill:#ff9800,color:#fff style BODY fill:#4caf50,color:#fff style RET fill:#f44336,color:#fff style CITIZEN fill:#9c27b0,color:#fff
flowchart TD
    DEF[def 关键字定义函数] --> PARAM[参数列表]
    DEF --> BODY[函数体]
    BODY --> DOC[文档字符串 docstring]
    BODY --> CODE[执行代码]
    BODY --> RET[return 返回值]
    
    PARAM --> IMMUT[不可变: int/str/tuple]
    PARAM --> MUT[可变: list/dict/set]
    
    IMMUT --> VAL[值传递错觉<br/>重新绑定不影响外部]
    MUT --> REF[引用传递<br/>修改内容影响外部]
    
    RET --> SINGLE[单个值]
    RET --> MULTI[多个值 -> 元组]
    RET --> NONE[没有return -> None]
    
    DEF --> CITIZEN[一等公民 First-class]
    CITIZEN --> ASSIGN[赋值给变量]
    CITIZEN --> PASS[作为参数传递]
    CITIZEN --> RETURNED[作为返回值]
    
    DEF --> PRINCIPLE[设计原则]
    PRINCIPLE --> SRP[单一职责原则]
    PRINCIPLE --> NAMING[命名规范]
    PRINCIPLE --> DOC_STR[写 docstring]
    
    style DEF fill:#ff9800,color:#fff
    style BODY fill:#4caf50,color:#fff
    style RET fill:#f44336,color:#fff
    style CITIZEN fill:#9c27b0,color:#fff