Python技术迷

Python 的 pass 语句有什么作用?

代码里看到一个孤零零的 pass,我第一反应一般不是“这语法真优雅”,而是先看它是不是把异常吞了。

因为 pass 本身不干活。它最大的作用,就是在 Python 语法要求“这里必须有一段代码”的地方,先占个坑。

比如你写了一个函数,只把名字定下来,逻辑还没写:

defsync_order_status(order_id: str):
pass

这段代码能跑。

但如果你把 pass 删掉:

defsync_order_status(order_id: str):

解释器直接报错:

IndentationError: expected an indented block

Python 对缩进很认真。def、class、if、for、try 后面都必须有代码块。你可以不写业务逻辑,但不能空着。pass 就是告诉解释器:这里我知道要写代码,但我现在故意什么都不做。

看个稍微像业务点的例子。

我之前写导入脚本时,经常会先把流程骨架搭出来:

classOrderImportJob:
defread_file(self, path: str):
pass

defcheck_header(self, rows: list[dict]):
pass

defsave_orders(self, rows: list[dict]):
pass

defrun(self, path: str):
        rows = self.read_file(path)
        self.check_header(rows)
        self.save_orders(rows)

这时候 pass 的作用很简单:先让代码结构跑起来。

但这玩意不能长期放着。最怕的就是这种代码合并进去了,测试也没覆盖到,线上某个分支走进去,然后什么都没发生。

更坑的是异常里写 pass。

defload_price(item_id: str) -> int:
try:
return int(open(f"/data/price/{item_id}.txt").read().strip())
except FileNotFoundError:
pass

这段代码看着没报错,实际问题更大。

调用方拿到的是 None。然后后面可能炸在一个完全不相关的位置:

TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'

这种问题排起来很烦,因为真正的原因早就被 pass 吃掉了。

我一般不这么写。哪怕暂时兜底,也会把行为写清楚:

defload_price(item_id: str) -> int:
    file_path = f"/data/price/{item_id}.txt"

try:
with open(file_path, "r", encoding="utf-8") as f:
return int(f.read().strip())
except FileNotFoundError:
        print(f"[price_missing] item_id={item_id}, path={file_path}")
return0

别嫌这个 print 土。脚本里跑批量数据时,一条明确日志比沉默强多了。

pass 还有一个常见场景,是先定义接口或者抽象结构。

classFileCleaner:
defclean(self, line: str) -> str:
pass

这代码能表达“以后这里会有清洗逻辑”。但如果真是要做接口约束,我更愿意让它直接报错:

classFileCleaner:
defclean(self, line: str) -> str:
raise NotImplementedError("clean() must be implemented")

区别很大。

pass 是悄悄跳过。

raise NotImplementedError 是明确告诉你:这里没实现,别装作能用。

写工具脚本时,这个判断挺关键。比如你有不同渠道的字段清洗器:

classCsvCleaner:
defclean(self, line: str) -> str:
        cols = [x.strip() for x in line.split(",")]
return",".join(cols)

classExcelCleaner:
defclean(self, line: str) -> str:
raise NotImplementedError("ExcelCleaner is not ready")

这样上线前一跑就能发现问题,不会假装成功。

还有人会把 pass 和 continue 搞混。

这个差别很明显:

for row in rows:
ifnot row.get("order_id"):
pass

    save_row(row)

这里的 pass 只是“不做任何事”,后面的 save_row(row) 还是会执行。

如果你的意思是跳过这一行,应该写:

for row in rows:
ifnot row.get("order_id"):
continue

    save_row(row)

这类 bug 很隐蔽。代码看着像过滤了脏数据,实际一条没过滤。

再比如 if 里占位:

if amount < 0:
pass
else:
    write_bill(amount)

这段代码语法没问题,但业务味道很怪。金额小于 0,既不报错,也不记录,也不返回。后面查账查到这种地方,基本要骂人。

我更倾向这样写:

if amount < 0:
    print(f"[bad_amount] amount={amount}")
return

write_bill(amount)

代码不一定高级,但行为清楚。

所以 pass 的作用可以记成一句话:它是 Python 里的空语句,用来占住一个必须存在的代码块。

它适合临时代码骨架、空类、占位分支。

但它不适合长期放在异常处理里,也不适合假装处理了某个业务分支。

我看到 except: pass 基本会直接改。不是洁癖,是这种代码在线上太像一个黑洞:错误进去,日志没有,返回值变形,最后炸在别的地方。排查时你只能顺着调用链一层层翻,翻到最后发现,原来最早那行代码选择了沉默。