page contents

Python 基础里最值钱的直觉:变量不是盒子,是标签

你写了一个小函数,传进去一个 list。函数里只是 append 了一个值,结果调用方的数据也变了。然后你又试了一次:在函数里写 lst = [...],调用方却完全没受影响。如果你把这两件事都看成「我在改变量」,就会越写越不踏实。更稳的方式是:把 Python 里的变量理解成「标签」,把对象理解成「数据」。这篇文章我们用一个贯穿全文的锚点来讲:标签贴在数据上。一旦你能分清「改数据」和「换标签」,大多数引用相关的问题都会自动消失。

attachments-2026-01-19tOd4oI697d5cc74bc73.png你写了一个小函数,传进去一个 list函数里只是 append 了一个值,结果调用方的数据也变了。然后你又试了一次:在函数里写 lst = [...],调用方却完全没受影响。如果你把这两件事都看成「我在改变量」,就会越写越不踏实。更稳的方式是:把 Python 里的变量理解成「标签」,把对象理解成「数据」。这篇文章我们用一个贯穿全文的锚点来讲:标签贴在数据上一旦你能分清「改数据」和「换标签」,大多数引用相关的问题都会自动消失。

2先把世界拆成两件东西:数据和标签

在 Python 里,你可以把运行时发生的事拆成两类角色:

  • 数据:真正存放内容、可能被修改的对象(比如 listdict
  • 标签:一个名字,用来指向某份数据(比如变量名、函数参数名)

例如:

nums = [123]

更接近真实的描述是:

  • 内存里出现了一份数据 [1, 2, 3]
  • 一个叫 nums 的标签,贴在了这份数据上

标签本身不保存数据,它只是「指向」数据。

这句话先记住,后面所有推导都会变得简单。


3赋值 =:不是改数据,而是换标签贴的位置

再看一行很普通的代码:

nums = [789]

如果你用「标签」模型理解,它做的事情是:

  • 创建了一份新的数据 [7, 8, 9]
  • 把 nums 这个标签,从旧数据上撕下来
  • 贴到新数据上

旧的 [1, 2, 3] 并没有被修改,它只是暂时没有标签指向了(之后可能被回收)。

结论可以更干脆一点:= 永远只影响标签,不会直接修改数据。

这在写 Skills 或工具脚本时非常关键,因为很多「我以为我改了」其实只是「我换了指向」。

4函数参数:只是多贴了一张标签(并没有自动复制数据)

来看一个函数调用:

defdemo(lst):
    ...

demo(nums)

这里最容易被误会的点是:以为传参会复制一份数据。

但按「标签」模型,发生的是:

  • nums 标签贴在某份数据上
  • 调用函数时,lst 也贴到了同一份数据上

所以你会看到两个现象同时成立:

  • lst 和 nums 是两张不同的标签
  • 但它们指向的是同一份数据

于是,当你在函数里做「原地修改」时,外面当然也会看到变化。

函数参数不是「拷贝品」,它更像是「临时别名」。

5什么时候是在「改数据本身」:看你用的是否是原地操作

下面这些操作的共同点是:它们直接修改了那份数据本身。

lst.append(4)
lst[0] = 100
lst.sort()
meta['title'] = '新标题'

因此只要有多个标签同时指向那份数据,大家看到的都会是同一个最新结果。

判断标准也可以很简单:这行代码到底是在「动对象」,还是在「换指向」。

如果你正在写 Skills,这会直接影响你的函数设计:你写的是「会改入参」的函数,还是「生成新数据」的函数。


6为什么 lst = [...] 不会影响外部:你只是把函数内部的标签换贴了

defdemo(lst):
    lst = [789]

结合前面的模型,这段代码发生的事很直白:

  • 创建了一份新数据 [7, 8, 9]
  • 把 lst 这个标签,换贴到新数据上

而调用方的 nums 还贴在原来的那份数据上,所以当然不受影响。

在函数里对参数使用 =,影响的是函数内部的标签指向,不是调用方的数据。


7.copy() 复制了什么:只复制外壳,不递归复制里面的东西

在 Skills 开发里,.copy() 很常见,也很容易被「想当然」。

copy_nums = nums.copy()

它做的事情可以概括成一句话:

.copy() 会创建一份新的外层容器,但里面的元素仍可能指向同一份数据。

所以它为什么「有时安全,有时不安全」?

如果元素本身不可变(例如字符串),你即便共享也改不了它,感觉就很安全:

nums = ['a''b''c']
copy_nums = nums.copy()

但如果元素本身是可变对象,浅拷贝就会把「共享」带到下一层:

data = [
    ['title''Vite'],
    ['author''Iris'],
]
copy_data = data.copy()

这里复制的是外层 list

内层两个小 list 仍然是同一份数据,所以你在 copy_data 里改内层,原来的 data 也会一起变。

到这里你不需要急着背 deepcopy、也不需要把每种情况都记住。

你只要记住边界:浅拷贝只到这一层,下一层仍可能共享。

8写 Skills 时,一个很实用的函数划分:修改型 vs 生成型

把函数分成两类,会让你更容易控制副作用。

第一类是修改型函数:它直接改入参,不返回新数据。

def normalize(lines):
    lines.append('')

第二类是生成型函数:它基于输入生成新数据,不修改原始数据,并且一定 return

def normalize(lines):
    new_lines = lines.copy()
    new_lines.append('')
return new_lines

两种风格都能用,关键是要一致。

最容易让人踩坑的不是「用修改型」,而是「同一个函数里混着来」。

9最后留一个可以长期使用的自检问题

每次你写 Python 脚本、Skills 或自动化工具时,都可以问自己一句:

我现在这行代码,是在改数据,还是在换标签?

能立刻回答出来,你就会发现很多问题其实不需要「调试」才知道原因。

到这里,这套「标签贴在数据上」的模型已经够你在大多数工程场景里写得很稳了。你后面遇到更复杂的结构(嵌套 dict、对象引用链)也可以继续用同样的问法往下推。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2026-01-31 09:37
  • 阅读 ( 23 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1783 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 1783 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章