page contents

Python这些坑别踩:默认参数、闭包、浅拷贝,一次讲透

Python里不少麻烦来自两个时间点:名字什么时候绑定,对象什么时候创建。把这两件事看清,很多行为看着不对,也能解释通。默认参数常出问题。看函数:

attachments-2026-10-hp6cyLjf6adeaf1b628db.png

Python里不少麻烦来自两个时间点:名字什么时候绑定,对象什么时候创建。把这两件事看清,很多行为看着不对,也能解释通。默认参数常出问题。看函数:

def add(item, box=[]):     box.append(item)     return box 

这段代码在定义函数时创建了列表。box 的默认值不是每次调用新建。第一次调用拿到 [1],第二次拿到 [1, 2]。想每次新列表,用 None 当哨兵:

def add(item, box=None):     if box is None:         box = []     box.append(item)     return box 

这里检查 is None,不用 if not box。空列表也是有效输入。用 not 会把传入的空列表替换掉,调用者拿不到自己传的对象。闭包也会遇到求值时机。下面三个函数都返回 2:

funcs = [lambda: i for i in range(3)] print([f() for f in funcs]) 

lambda 里没有保存 i 的值,保存的是名字 i。循环结束后 i 停在 2。要让每个函数记住当前值,把值绑到默认参数上:

funcs = [lambda i=i: i for i in range(3)] print([f() for f in funcs]) 

默认参数在定义 lambda 时求值,i=i 把当前 i 存进函数对象。这个写法够用。代码里出现这种模式,读的人要能看懂。生成器把求值推后。读大文件时别一次 readlines。用逐行迭代:

def read_lines(path):     with open(path, encoding='utf-8') as f:         for line in f:             yield line.rstrip('\n') 

这个函数返回生成器。文件不会一次进内存。for 循环每要一行,函数跑到 yield 停住。适合日志、csv、管道处理。注意生成器只能走一遍。走过一次后再 list 得到空列表。需要重复用,重新创建生成器,或者把结果存下来。存之前想清楚数据量。浅拷贝的坑也跟对象创建有关。下面写法三行指向同一个列表:

grid = [[0] * 3] * 3 grid[0][0] = 1 print(grid) 

外层列表的三个位置放的是同一个内层列表。改一个,三行都变。要独立行,用推导式逐行创建:

grid = [[0] * 3 for _ in range(3)] grid[0][0] = 1 print(grid) 

这里每轮循环新建一个 [0, 0, 0]。外层列表装三个不同对象。二维数组、矩阵初始化都该这么写。这些技巧背后是同一件事:Python 不复制对象,只绑定名字。默认参数在定义时求值。闭包捕获名字。生成器延迟执行。容器乘法复制引用。写代码时多问一句:这个对象在哪创建,谁在引用它。调试能少绕路。

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

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

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2541 篇文章

作家榜 »

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