Python 的 is 把我坑惨了,原来 == 和 is 在小整数池外完全是两码事

简介: 本文以一次深夜调试经历切入,揭示Python中`==`与`is`的本质区别:`==`比较值是否相等,`is`判断是否为同一对象。通过numpy.int64类型陷阱、小整数池(-5~256)、字符串驻留等实例,说明滥用`is`的隐患,并强调——除判空(`x is None`)等极少数场景外,一律应使用`==`。

一个让我熬夜到凌晨三点的 Bug

先说说我是怎么栽在这个坑里的。

去年有个项目,我要写一个缓存系统。逻辑很简单:从数据库里查出用户 ID,然后判断这个 ID 是不是 0——0 代表"游客",需要特殊处理。

代码大概是这样的:

def get_user_role(user_id):
   # user_id 是从数据库查出来的
   if user_id is 0:
       return "游客"
   return "注册用户"

本地测试的时候一切正常,user_id 为 0 时能正确返回"游客"。我就信心满满地部署上线了。

结果上线之后,监控系统疯狂报警——"游客"身份的判断时不时失效,有时候明明是 0,却被当成注册用户处理了。

我盯着代码看了两个小时,确认 user_id 的值确实是从数据库查出来的 0。Python 里 0 is 0 不是应该返回 True 吗?本地明明跑得好好的啊。

后来我打印了 user_id 的类型才发现——数据库驱动返回的 user_idnumpy.int64(0),不是 Python 内置的 int 类型。

numpy.int64(0) is 0 返回 False,而 numpy.int64(0) == 0 返回 True

那一刻我意识到:我一直在用 is 做值比较,但这个操作符根本不是干这个用的。

代理 IP 使用小技巧 让你的数据抓取效率翻倍 (64).png

改了一行代码——把 is 换成 ==——问题解决了。但那天晚上的三个小时再也回不来了。

== 和 is 到底有什么不一样?

先给出最简洁的答案:

  • == 比较的是值——两个东西"长得是不是一样"
  • is 比较的是身份——两个东西"是不是同一个东西"

用大白话说:**== 问你俩是不是双胞胎,is 问你俩是不是同一个人**。

看几个最简单的例子:

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True,两个列表的值一模一样
print(a is b)  # False,两个列表是不同的对象,只是内容碰巧相同
print(a is c)  # True,a 和 c 指向的是同一个对象

这个好理解对不对?一个变量赋值,一个新建列表,虽然内容一样但确实是两个独立的东西。

问题在于——对于数字和字符串这种基础类型,Python 搞了一些"小动作",让你产生了"is 也能比较值"的错觉。

小整数池:为什么 256 以内 is 没问题,257 就不行了?

先看这段代码:

a = 100
b = 100
print(a is b)  # True

再看这段:

a = 1000
b = 1000
print(a is b)  # False

同样的写法,只是数字从 100 换成了 1000,结果完全不一样。

你是不是觉得 Python 疯了?

其实 Python 没疯,它只是在背后做了一件优化的事。

Python 在启动的时候,会预先创建好一批整数对象,范围是 -5 到 256。这些整数是"常驻内存"的,不管你写多少遍,用的都是同一个对象。

这就是所谓的 **"小整数池"**。

所以当你写 a = 100b = 100 时,Python 不会新建两个整数对象,而是直接把两个变量指向小整数池里的那个 100。所以 a is bTrue——它们确实是同一个对象。

但当你写 a = 1000 时,1000 不在小整数池里,Python 会新建一个整数对象。再写 b = 1000,又新建一个。虽然值一样,但两个对象在内存里是不同的,所以 a is bFalse

这就是为什么你平时用 is 比较小整数一直没问题,一到生产环境数据量大了、数字超过 256 就莫名失灵的根本原因。

同样的道理也适用于字符串。Python 有一种叫 "字符串驻留" 的机制——代码里写死的字符串字面量,比如 "hello",会被复用同一个对象。但动态生成的字符串,比如 "".join(["h", "e", "l", "l", "o"]),就是新建的对象。

a = "hello"
b = "hello"
print(a is b)  # True,字面量复用

a = "".join(["h", "e", "l", "l", "o"])
b = "".join(["h", "e", "l", "l", "o"])
print(a is b)  # False,动态生成的两个不同对象

你看,你用 is 比字符串有时候是对的,有时候是错的,完全取决于字符串是怎么来的。这谁能受得了?

None、True、False:这几个是例外

有一类对象是可以用 is 的,而且应该用 is——就是那些全局唯一的单例

  • None
  • True
  • False

这几个对象在 Python 整个运行过程中有且只有一个实例。不管你怎么引用,它们都是同一个对象。

所以判断一个变量是不是 None,标准写法是:

if x is None:   # 正确
if x == None:   # 不推荐,虽然能工作但语义不对

判断布尔值也是同理:

if flag is True:    # 可以,但通常直接 if flag: 更简洁
if flag == True:    # 不推荐

为什么官方推荐用 is 来比较 None?因为 None 是单例,用 is 比较的是身份,速度更快,而且语义更准确——你确实想知道"这个变量是不是那个唯一的 None",而不是"这个变量的值是否等于 None"。

一个更隐蔽的坑:自定义类的实例

这个问题在自定义类里尤其容易踩。

class Person:
   def __init__(self, name):
       self.name = name

p1 = Person("张三")
p2 = Person("张三")

print(p1 == p2)  # False,默认比较的是内存地址
print(p1 is p2)  # False,本来就是两个对象

如果想让 p1 == p2 返回 True,你需要给类实现 __eq__ 方法:

class Person:
   def __init__(self, name):
       self.name = name

   def __eq__(self, other):
       if not isinstance(other, Person):
           return False
       return self.name == other.name

这时候 p1 == p2 就是 True 了,但 p1 is p2 仍然是 False——因为它们是两个不同的对象,只是名字一样而已。

这就是 ==is 的本质区别:== 的行为是可以自定义的,而 is 是内存地址的直接比较,永远无法改变。

什么时候用 ==,什么时候用 is?

经过那次三个小时的教训,我给自己定了一个铁律:

比较值的时候,永远用 ==,除非你有 100% 的把握要用 is

什么时候可以有那 100% 的把握?只有这三种情况:

  • 判断 Nonex is None
  • 判断 True / Falsex is True(但通常直接用 if x:if not x:
  • 判断是否是同一个对象(比如在缓存里判断两个引用是否指向同一个实例)

除此之外,**一律用 ==**。

不管是你觉得"这个数字肯定很小不会超 256",还是"这个字符串肯定是字面量",都不要赌。生产环境的数据你永远猜不到它会变成什么样。

# 永远这样做
if user_id == 0:
   return "游客"

# 不要这样做
if user_id is 0:   # 万一 user_id 不是 int 呢?万一超过 256 呢?
   return "游客"

写在最后

那天凌晨三点,我把 is 改成 == 之后,缓存系统恢复了正常。关掉 IDE 的时候我在想——如果当初写代码的时候能多想一想"我到底要比较什么",那三个小时完全不用浪费。

is== 的区别,写出来就是一句话,但真正理解它需要踩一次坑。

== 问的是"你俩值一样吗",is 问的是"你俩是同一个东西吗"。大多数时候你想问的都是前者,而不是后者。

小整数池和字符串驻留这些优化机制,本来是 Python 为了提升性能做的好事,但它们也让 is 的行为变得"时灵时不灵",反而成了陷阱的源头。

记住一句话:除非比较 None,否则默认用 == 这个习惯能帮你省下无数个深夜调试的时间。

希望你不用像我一样,等到凌晨三点才明白这个道理。

目录
相关文章
|
20天前
|
人工智能 IDE 搜索推荐
阿里云Qoder CN原名通义灵码指南及收费价格,个人社区版免费使用哦
阿里云Qoder CN(原通义灵码)是面向开发者的AI智能编码助手,支持VS Code、JetBrains等IDE插件及独立IDE,覆盖代码补全、问答、Agent、RepoWiki等场景,提供多模型(Qwen/GLM/Kimi)支持与灵活计费版本。阿里云Qoder CN官网:https://t.aliyun.com/U/fEiOLV
|
22天前
|
缓存 人工智能 算法
DeepSeek-V4-Pro 正式版深夜突袭:Agent 评测暴涨 50 分,一套代码通吃 OpenAI 与 Anthropic 双生态
JeecgBoot AI专题研究 DeepSeekV4Pro 正式版全面拆解——Agent 能力跃迁、双 API 兼容与缓存降本实战![DeepSeekV4Pro 正式版发布概念图](https://oscimg.oschina.net/oscnet/up45eab3ac316682157a43
1003 1
DeepSeek-V4-Pro 正式版深夜突袭:Agent 评测暴涨 50 分,一套代码通吃 OpenAI 与 Anthropic 双生态
|
19天前
|
人工智能 开发框架 Java
如何入门学习 Agent 开发?
本文分享Agent开发实战经验:强调甄别一手资讯、聚焦Context本质而非框架、坚持实操落地、重视效果评测与自我迭代,助新手避开玄学误区,从真实场景出发高效入门。(238字)
81 5
|
20天前
|
人工智能 测试技术 Shell
Opencode最被低估的6个测试指令:每天帮你省下3小时重复劳动
本文详解Opencode六大自定义指令(如/test、/coverage),助测试工程师30分钟配置、每日省3小时,告别重复Prompt输入,实现测试流程自动化提效。
|
18天前
|
人工智能 测试技术 开发工具
新版Qoder CN AI编程智能体详解:RepoWiki、Quest2.0与专家团实战教程
在AI辅助开发持续迭代的当下,AI编程工具已经跳出简单代码片段生成的范畴,逐步进化为具备任务规划、多文件修改、自测修复、知识沉淀的编程智能体体系。新版Qoder CN作为面向完整软件研发链路的AI编程智能体平台,完成底层架构与核心能力的大规模升级,不再局限单文件代码补全,面向真实工程级项目打造完整Agent工作流,覆盖需求梳理、方案设计、编码实现、单元测试、缺陷修复、项目文档沉淀全流程。产品形态十分丰富,包含独立Qoder CN IDE、JetBrains系列插件、VSCode扩展组件、Qoder‑CLI命令行工具,同时兼容对接百炼平台Coding Plan、Token Plan订阅计费方案,
712 1
|
19天前
|
人工智能 算法 API
【第二部分:大模型应用开发基础】9. RAG 是什么,它与 Agent 有什么关系?——从知识库问答到 Agentic RAG
RAG 通过文档解析、切分、Embedding、混合检索、Rerank 与引用机制,让大模型在回答问题时能够按需获取企业知识,而不是依赖训练数据“记住一切”。文章进一步介绍 RAG 如何从固定的检索增强生成流程演进到 Agentic RAG:由 Agent 判断是否需要检索、如何规划 Query、证据是否充分,并在必要时继续改写和多轮检索。同时梳理 RAG、Memory、Tool 与 Agent 的边界,强调知识库问答系统并不等同于 Agent,RAG 只是 Agent 获取外部知识的一种能力。
183 2
|
20天前
|
弹性计算 人工智能 运维
最新版阿里云CLI完整功能详解:插件化架构、多账号管理、自动化运维实操教程
在云原生运维大规模普及的当下,传统网页控制台的图形化操作已经很难满足批量运维、持续集成、多环境管理、自动化脚本编排的业务诉求。大量运维工程师、开发人员需要一套可以脱离浏览器,直接在终端、服务器、CI流水线、AI智能体内部调用云平台能力的工具。阿里云CLI就是这样一款开源跨平台命令行管理工具,底层基于平台OpenAPI接口封装,支持Linux、macOS、Windows多操作系统,新版采用轻量化插件架构,覆盖三百余款云产品,几乎网页控制台可以完成的操作,都可以通过命令行实现。很多初次接触该工具的使用者,只把它当作简单查询工具,却不了解它完整的凭证体系、插件自动加载、结果过滤、预演校验、多账号隔离
169 1
|
22天前
|
缓存 人工智能 JSON
DeepSeek V4 Pro转正,性能逼近Fable 5,价格仅六十分之一
DeepSeek V4 Pro正式版发布:1M上下文、384K输出,价格只有Fable 5的六十分之一。本文从价格、能力、跑分可信度拆解,说清该不该切。
274 0
|
18天前
|
人工智能 IDE 开发工具
阿里云Qoder CN产品详解:模型能力、版本、价格及应用场景解析
在AI重塑软件研发的浪潮中,阿里云推出了面向全品类开发者的AI智能编码助手——Qoder CN。作为原“通义灵码”的全面升级版,Qoder CN不仅继承了强大的代码生成能力,更向目标驱动的全栈Agent式智能编程平台转型。本文将深入解析Qoder CN的模型底座、版本差异、价格体系及适用场景,助您选择最合适的AI编程伙伴。
|
19天前
|
存储 人工智能 Linux
【全网最详细】Ollama下载+安装+运行大模型超详细图文教程
Ollama 是一款免费开源的本地大模型运行工具,支持 Windows/macOS/Linux,一键安装、命令行操作极简。可离线运行 Llama、Qwen、DeepSeek、Gemma 等主流模型,数据不出本机,隐私安全,还能接入编程工具,是个人与开发者的本地 AI 助手。(239字)

热门文章

最新文章