
大家好,我是花姐。
这篇文章想写很久了。为啥?就因为我每天后台、评论区、群聊......总能看到不少刚入门Python的小伙伴,犯了跟我当年一模一样的错。 有的错误吧,说大不大,说小不小,浪费的时间是真心多,一不小心还会让人误以为"自己不适合学编程",结果就被劝退了------太可惜了🙃。
所以这篇文章,我想聊点实在的,从一个学了多年Python的老程序员视角,说说Python初学者常犯的错误,我也踩过,我懂那种懵逼感。
一、变量名乱起,调试想哭😭
你有没有写过这样的代码:
python
list = [1, 2, 3]
print(list)
看起来没毛病对吧?运行也能输出。
然后你继续写:
python
list.append(4)
print(sum(list))
还是OK的!你会想:嘿,真香!
可问题来了。
你在某个函数里,想用 list()
把元组转成列表:
python
nums = list((4, 5, 6))
突然就炸了------TypeError: 'list' object is not callable
为啥?因为你把Python内置函数 list()
给"顶掉"了!等你再写 list()
的时候,它不是函数了,是个你自己定义的变量!Python傻不拉几地就报错了。
这个问题其实很典型,新手常常把内置的关键词当变量名用(什么list
、str
、id
、sum
,甚至还有input
)------调试的时候,那叫一个酸爽。
聪明的你一定会想到办法的,比如我现在起名字有洁癖,恨不得加后缀加前缀,就为了避开这种内置名😂
💡建议:起变量名别偷懒,用有意义又"避坑"的,比如 my_list
、user_input
,或者前缀 data_
、tmp_
都可以。
二、缩进不一致,一脸懵逼
Python的语法设计真的很干净,但也很"严格"。缩进错误,新手基本上都中招过。
python
def say_hi():
print("Hi")
print("Hello")
有些编辑器看不出来问题,还能运行一行,然后就报错了。最恶心的是空格和Tab混用------眼睛根本看不出来!
我以前就是因为一个文件里混用了Tab和空格,debug到凌晨两点,差点怀疑人生。
所以一定要设置编辑器默认用空格缩进(4个空格就够),别碰Tab!
三、==和is傻傻分不清
你可能在想:"不都是比较吗?我看教程说都能用啊。"
然后你写:
python
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True
print(a is b) # False
为啥一个True一个False?
原因很简单:==
比的是"值"是否相等,is
比的是"是不是同一个对象"。
这就像你有两个一模一样的杯子,但一个在你家,一个在我家。你说它们长得一样 ------对,那是==
;你说它们是同一个杯子 ------错,那是is
。
别死记硬背,理解了你就会用了。
四、函数参数默认值是坑,真的坑🕳️
这个是我最想说的。新手99%都会在这里翻车:
python
def add_item(item, my_list=[]):
my_list.append(item)
return my_list
第一次调用:
python
add_item("a") # ['a']
再来一次?
python
add_item("b") # ['a', 'b']
???怎么上次的"a"还在?
这是因为默认参数是函数定义时就创建 的,my_list=[]
这个列表是同一个!每次调用都在往它上面加。不是你以为的"每次都新建一个"。
所以正确写法是:
python
def add_item(item, my_list=None):
if my_list is None:
my_list = []
my_list.append(item)
return my_list
你现在应该懂了吧?🎉聪明的你肯定记住了这个"传参黑洞"。
五、数据类型一换就懵,尤其是None
很多时候,我们对 None
这个东西理解太简单了。
python
x = None
if x == False:
print("Nope")
它不会打印任何东西,因为 None
不是 False
。你得写:
python
if x is None:
print("Yep")
别图省事用 ==
,用 is
判断 None 才是"Pythonic"。
还有一个例子,大家常写:
python
if len(my_list) > 0:
do_something()
其实可以简写成:
python
if my_list:
do_something()
因为空列表会被当作False,非空则True。这种写法看着就干净、舒服、pythonic。
六、while写成死循环,CPU嗷嗷叫💀
你写个while循环,一不留神忘记加break或者条件永远为真,程序就挂在那儿了。
python
while True:
x = input("输入exit退出:")
if x == "exit":
break
这还好,有条件退出。可有时候你写:
python
while some_flag:
# do something
结果 some_flag
永远没变,CPU热得都能煎蛋。
我的建议是,调试阶段你就用print()
打点,别嫌麻烦,甚至可以加个计数器,每10次强制break一次,别等到系统都卡死。
七、复制列表翻车,一改全变!
你可能觉得复制列表超简单:
python
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]?
然后你一脸懵:"我改的是b啊,a怎么也变了?"
因为你不是复制,而是"赋值了引用"。a和b其实是指向同一个对象。
聪明的你一定会想到------要复制得深一点。
最简单的复制方式其实是:
python
a = [1, 2, 3]
b = a[:]
或者用 list.copy()
:
python
b = a.copy()
更狠一点,如果是嵌套列表,要用:
python
import copy
b = copy.deepcopy(a)
尤其在处理 DataFrame、嵌套结构、传参数的时候,这个坑非常深!
八、文件没关,资源泄露...
我当年也写过这样的代码:
python
f = open("data.txt", "r")
data = f.read()
# 然后忘了 close()
Python 不主动提醒你,结果就是文件资源没释放,有些系统里甚至会报 too many open files
,开多了系统直接罢工!
聪明的你会想到------with
!
python
with open("data.txt", "r") as f:
data = f.read()
这个结构叫上下文管理器,你只要缩进写好,它自动给你关掉文件,干净利落不出错。
而且不仅文件,数据库连接、网络连接、锁......都推荐这么写。
九、for循环里修改列表,玩火🔥
你想遍历一个列表,然后删掉里面的某些元素:
python
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n)
你以为删的是2和4,其实结果是 [1, 3, 5]
......有时候还少删一个。
为什么?因为你在遍历的同时改变了原列表,指针跳着跳着就错位了。
解决方法有很多:
- 遍历副本删原件:
python
for n in nums[:]:
if n % 2 == 0:
nums.remove(n)
- 用列表推导:
python
nums = [n for n in nums if n % 2 != 0]
更优雅也不容易出错。
十、逻辑判断顺序错,全局炸裂🤯
有个经典的判断写法:
python
if user['name'] == "花姐":
但万一 user = None
呢?程序就炸了:TypeError: 'NoneType' object is not subscriptable
你想加个保护,于是这样写:
python
if user is not None and user['name'] == "花姐":
挺好。但是如果你写反了:
python
if user['name'] == "花姐" and user is not None: # ❌
那还是会爆炸。因为Python会先执行左边的 user['name']
,这时候user就是None,炸了。
💡所以:逻辑判断的顺序非常重要!要让"更不容易报错"的条件写在前头。
十一、搞不懂 range 和 list 的区别,一顿乱用🌀
我以前写循环也懵过,写个:
python
for i in range(1, 5):
print(i)
然后读到数据,发现为啥只有1、2、3、4......5去哪儿了?
这玩意儿是左闭右开区间,range(1, 5)只包含1~4,5不包括。
更坑的是:
python
x = range(5)
print(x[0]) # 0
print(x) # range(0, 5)
你以为它是个列表,其实它不是!是一个惰性对象,打印出来啥也看不到。
所以你要真想拿它当列表用:
python
list(range(5)) # [0, 1, 2, 3, 4]
别忘了加个 list()
。
十二、import 自己的脚本,循环引用卡死!
这个比较隐蔽,有些人会写两个文件:
bash
main.py
utils.py
然后你在 main.py
里:
python
import utils
但在 utils.py
里又写了:
python
from main import something
这时候你就死循环了,import的时候互相依赖,Python会报 ImportError: cannot import name ...
,或者直接卡住。
建议你项目结构里不要写循环依赖,实在要用就抽公共模块出来。
十三、浮点数精度问题,谁用谁尴尬😓
你可能以为:
python
0.1 + 0.2 == 0.3 # True
但其实:
python
>>> 0.1 + 0.2
0.30000000000000004
浮点数的世界里,精度永远不准。这不是Python的问题,是计算机底层的问题。
所以你要比精度,用:
python
abs(0.1 + 0.2 - 0.3) < 1e-8 # True
别用 ==
比浮点,永远是坑。
十四、用了全局变量,以为能改,结果根本没变
你写了个函数,想改全局变量:
python
x = 10
def change():
x = x + 1 # 报错:UnboundLocalError
为啥?因为你在函数里赋值 x = x + 1
,Python以为你定义了一个局部变量,但又没初始化。
你得显式告诉Python:我要改全局变量!
python
x = 10
def change():
global x
x = x + 1
但说句实话,全局变量真心不推荐多用。用类、用参数传递,才是靠谱的代码结构。
十五、逻辑表达式没加括号,结果跑偏
这个坑我小时候写C语言就踩过,没想到Python里又踩了一回。你看这个判断:
python
if a > 0 and b < 10 or c == 1:
你可能以为意思是:"a>0 且 b<10,或者 c==1"
但其实这个表达式是这样解析的:
css
(a > 0 and b < 10) or (c == 1)
还好对。但你再看这个:
python
if a > 0 and b < 10 or c:
你就迷糊了。c 是啥?是 True 吗?是非0就算True?
所以聪明的你一定能想到:这种情况最简单的方法就是------用括号包起来!
python
if (a > 0 and b < 10) or c == 1:
这才是你想表达的意思。逻辑表达式别偷懒,多写括号,代码可读性才是王道。
十六、try-except 滥用,调试地狱😈
很多人觉得 try-except 是"万能胶",啥错都包住:
python
try:
risky_func()
except:
pass
这段代码看着舒服吧?但你连哪里错了、为什么错了、错没错都不知道,就直接忽略了。
而且 except:
会捕获所有异常,包括你根本没意识到的问题,比如拼写错误、逻辑错、甚至 KeyboardInterrupt。
聪明点,要指定捕获的异常类型:
python
try:
risky_func()
except ValueError as e:
print("输入值有问题:", e)
别偷懒写万能 except,哪天出了事你找不到锅在哪。
十七、datetime 转字符串,写乱了🌪️
你写过这种时间格式转换吗?
python
from datetime import datetime
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
不错对吧?
但是你要把字符串再转回来就开始头大了:
python
s = "2025-06-10 18:30:00"
dt = datetime.strptime(s, "%Y/%m/%d %H:%M:%S") # 报错
为啥?你写错格式了!%Y/%m/%d
你原来明明是 -
,结果一改就炸。
还有一个更坑的:你用 pandas 写时间序列,忘记转成 datetime 类型:
python
df["date"] = "2023-01-01"
df["date"].dt.year # 报错:Can only use .dt accessor with datetimelike values
记得加一句:
python
df["date"] = pd.to_datetime(df["date"])
时间格式相关的坑超级多,建议你写个自己的时间处理函数库封装好,少掉坑。
十八、Boolean 判断写复杂了,自己都看不懂🤪
你有没有写过这种鬼代码:
python
if flag == True:
do_something()
这不是多此一举嘛,直接:
python
if flag:
更干净。而更迷惑的是:
python
if not flag == True:
这是啥......你到底是想判断 flag != True
还是 not flag
?
写代码不怕啰嗦,就怕你自己两天后回来看不懂。所以布尔判断就两个建议:
- 要不就写
if flag:
- 要不就明确写
if flag is True:
或if flag is False:
别折腾那些绕来绕去的判等逻辑。
十九、用 lambda 当万能钥匙,写着写着炸了🔑
Lambda 表达式的确好用,但新手很容易过度使用:
python
add = lambda x, y: x + y
print(add(1, 2))
挺好用。但你再来个复杂的:
python
funcs = [lambda x: x + i for i in range(5)]
print([f(10) for f in funcs]) # 全是14?
为啥?因为 lambda
捕获的是 变量 i 的引用 ,不是当前值。等函数执行的时候,i
早就变成4了。
解决方法?用默认参数绑定当前值:
python
funcs = [lambda x, i=i: x + i for i in range(5)]
这才是你想要的 [10,11,12,13,14]
。
lambda 很香,但也是典型的"写着简单,调试地狱"选手。能写 def 就别省这几行。
二十、input()
永远都是字符串,别再直接拿来加法了
你可能写过这样的代码:
python
a = input("请输入一个数字:")
b = input("再输入一个数字:")
print("总和是:", a + b)
你高高兴兴输入了 2 和 3,然后得到:
总和是: 23
你人都傻了😂。Python 是不会自动帮你转类型的,input()
的返回值就是个 str
。
想做数学运算?老老实实加上类型转换:
python
a = int(input("请输入一个数字:"))
b = int(input("再输入一个数字:"))
print("总和是:", a + b)
更保险点,加个异常捕获,不然你输入个"hello"也会直接炸掉。
二十一、路径写死了,换台电脑直接崩😤
你写的爬虫、读 Excel,可能这样写路径:
python
df = pd.read_excel("C:\\Users\\你的用户名\\Desktop\\数据.xlsx")
在你电脑上没问题。
换别人的电脑?路径没这个人名,直接爆炸。
聪明点,写成动态路径:
python
import os
path = os.path.join(os.getcwd(), "数据.xlsx")
df = pd.read_excel(path)
或者用 Pathlib
,更高级写法:
python
from pathlib import Path
file = Path.cwd() / "数据.xlsx"
我自己之前搞分享代码,每次被同事骂"你这路径写得太不通用了",后来我就改了 😂
花姐叨叨两句
说实话,学Python最容易卡的,真的不是那些语法问题,而是你以为你写对了,其实根本跑偏了的逻辑。
错是怎么来的?写多了自然就懂了。 但如果能早点知道一些"坑在哪",你就不会一头撞进去------或者,至少能少撞几次。
这篇文章我已经尽量帮你把我见过最多的新手错误都整理出来了,当然远远不止这些。
我们下篇见~