← [ BACK_TO_NOTES ]

Python 的名字、赋值与作用域入门

Python 的“变量”是名字到对象的绑定;赋值不复制对象,作用域按 LEGB 查找,理解它才能解释可变共享与闭包。

#concept #growing #dev #python #python

[!info] related notes

Python 的名字、赋值与作用域入门

这个概念为什么出现

从 C/Java 直觉进入 Python,最容易把:

a = [1, 2]
b = a

理解成“复制了一份列表”。真正发生的是:名字 b 与 a 绑定到同一个列表对象。后续所有“改了参数内部、外面也变了”“可变默认参数诡异”都建立在这层模型上。

[!abstract] 一句话理解 x = y 将名字 x 绑定到 y 的对象,不复制对象。普通函数中的名字查找可用 LEGB 入门模型理解;属性、下标赋值及类体等作用域另有规则。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:客服系统把同一张工单列表分给两个处理函数

工单服务把“待处理列表”交给自动标签器与 SLA 检查器。若两者共享同一列表对象,一方 append 会影响另一方——有时是 bug,有时是有意共享。必须先看清绑定。

# ticket_binding_demo.py
# 业务意图:演示“两个名字指向同一工单列表”时的共享副作用。
# 教学点:
# - 赋值绑定对象,不复制 list;
# - is 比较身份,== 比较值;
# - 重新赋值(rebinding)与原地修改(mutate)不是一回事。

def tag_urgent(tickets: list[str]) -> None:
    # 原地修改:往共享列表追加标签工单。
    tickets.append("T-URGENT")


def main() -> None:
    open_tickets = ["T-100", "T-101"]
    queue_view = open_tickets  # 绑定到同一对象,不是拷贝

    print("same object?", queue_view is open_tickets)
    tag_urgent(queue_view)
    print("after tag:", open_tickets)

    # rebinding:名字 open_tickets 指向新列表,不影响 queue_view 仍指向旧对象
    open_tickets = ["T-200"]
    print("open_tickets now:", open_tickets)
    print("queue_view still:", queue_view)


if __name__ == "__main__":
    main()

建议运行:

python ticket_binding_demo.py

期望输出:

same object? True
after tag: ['T-100', 'T-101', 'T-URGENT']
open_tickets now: ['T-200']
queue_view still: ['T-100', 'T-101', 'T-URGENT']

结合场景再看三个关注点

  1. 共享可变对象时,函数参数写入会穿透到调用方。
  2. open_tickets = [...] 是换绑名字,不是清空旧列表。
  3. API 若不想共享,应显式拷贝或返回新对象——见后续可变性笔记。

核心概念与准确模型

1. 名字 → 对象

  • 对象有身份(id/is)、类型与值。
  • 名字在命名空间里指向对象。
  • 多个名字可以指向同一对象。

2. 赋值与增强赋值

  • x = y:让 x 指向 y 当前指向的对象。
  • x += y:对可变对象可能是原地修改;对不可变对象通常是产生新对象再绑定。

3. 作用域 LEGB(入门)

层级含义
Local函数内赋值的名字
Enclosing外层函数(闭包)
Global模块全局
Built-inlen/str 等内建
  • 函数体内对某名字赋值,会使该名字在整个函数块中被判为局部名(除非有 global/nonlocal);即使赋值前读取,也不会回退到外层,而可能抛 UnboundLocalError。
  • global/nonlocal 用于重新绑定对应作用域的名字;通过 obj.attr 或 items.append 修改已有对象,不等于重新绑定 obj/items。
  • 阶段一只要求建立直觉;闭包细节在函数笔记展开。

4. 删除名字

  • del x 删除的是绑定,不一定立刻销毁对象(还要看引用计数/其他引用)。

边界情况与反直觉行为

  1. 小整数/短字符串驻留是实现优化,is 比较字面量不可当作业务逻辑。判断两个引用是否指向同一对象(包括 None、专用哨兵和共享可变对象)可用 is;比较业务值用 ==。
  2. 默认参数在定义时求值且绑定一次:可变默认参数会跨调用共享(经典坑)。
  3. 类体不是普通方法的 enclosing 函数作用域,方法中访问实例/类属性通常需 self.x 或类名;LEGB 不是覆盖所有 Python 作用域的完整规则。

常见误区

[!warning] 常见误区:把 Python 变量当成“装值的盒子” 错误理解:赋值总是复制内容。
正确模型:赋值复制的是引用/绑定;对象是否复制要看类型与是否显式 copy。

[!warning] 常见误区:用 is 比较字符串或数字相等 错误理解:a is b 表示值相等。
正确模型:值相等用 ==;is 是身份。

工程实践

  • 函数文档写清:参数是否被原地修改。
  • 对外返回的可变对象,考虑拷贝或不可变结构。
  • None 用 is None / is not None。

本节总结

“变量”在 Python 里首先是名字绑定问题。先建立这层心智,list/dict 的共享、闭包捕获、默认参数坑才会变得可预测。

自测题

代码推理题

def add_tag(items, tag):
    items = items + [tag]
    return items

base = ["a"]
out = add_tag(base, "b")
print(base, out)

base 是否变化?为什么?

参考答案

base 仍是 ["a"]。函数里 items = items + [tag] 重新绑定本地名 items 到新列表,没有原地修改调用方列表;out 是 ["a", "b"]。

延伸阅读与资料来源

资料类型支撑内容
Naming and binding规范绑定与作用域
Tutorial – Data Structures教程列表操作与共享直觉
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本