卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

    • Python有哪些内置数据类型?
    • Python变量保存的是什么?
    • 可变对象和不可变对象有什么区别?
    • is和==有什么区别?
      • 简要回答
      • 详细回答
      • 知识拓展
    • Python的运算符有哪些?
    • Python条件判断和循环的常见写法与易错点
    • Python字符串为什么不可变?
    • Python列表和元组有什么区别?
    • Python字典和集合有什么区别?
    • Python切片和序列解包怎么用?
    • 列表、字典和集合推导式怎么写?
  • 函数、作用域与模块

  • 面向对象

  • 进阶语法与语言机制

# is和==有什么区别?

面试官问:"Python中is和==有什么区别?"

很多录友会答"is比较地址,==比较值",再补一句"小整数用is也相等"。前半句方向没错,后半句就把CPython的实现优化当成了语言规则。这题要讲清楚两件事:is和==走的是两条完全不同的判断路径,以及为什么业务代码不能拿is做值比较。

前置知识是《Python变量保存的是什么?》:变量名只是绑定到对象的引用,多个名字可以绑定同一个对象。

# 简要回答

is比较对象身份,判断两个名字是否绑定到同一个对象,等价于id(a) == id(b)。它由解释器直接判断,不能被类重载。

==比较值是否相等,会调用对象的__eq__()方法,具体规则由类型自己定义。两个内容相同的列表,==为True,is为False。

使用上,判断None这类单例用is,比较值一律用==。小整数、短字符串用is有时也返回True,这是CPython缓存和驻留带来的实现细节,结果会随取值、运行方式和版本变化,不能依赖。

# 详细回答

# 两条不同的判断路径

a is b只问一个问题:这两个名字是不是指向同一个对象?id()返回对象在生命周期内唯一的身份标识,在CPython中就是对象的内存地址。

a == b则会调用a.__eq__(b),让对象自己决定"相等"是什么意思。列表的__eq__()逐个比较元素,自定义类可以按任意字段比较。

scores = [90, 85]
copied = [90, 85]
alias = scores

print(scores == copied)  # True,内容相同
print(scores is copied)  # False,两个独立的列表对象
print(scores is alias)   # True,alias和scores绑定同一个列表
1
2
3
4
5
6
7

copied是另一个内容相同的新列表,所以==成立而is不成立。alias = scores没有复制任何东西,只是让新名字绑定到同一个对象,此时alias.append(100)会让scores也看到变化。这正是is真正有价值的场景:确认两个名字是否共享同一个可变对象,详见《可变对象与不可变对象》。

is与==判断路径对比

图中上半部分是名字与对象的绑定关系,下半部分是两种比较各自读取的信息:is只看两个名字是否指向同一个对象,==进入对象内部按__eq__()规则比较内容。

# 为什么判断None要用is

None在整个解释器中只有一个对象,是单例。判断"是不是None"本质上就是判断"是不是那一个对象",所以用is最贴切。

更实际的原因是:==可以被重载,is不能。

class AlwaysEqual:
    def __eq__(self, other):
        return True

obj = AlwaysEqual()
print(obj == None)  # True,被__eq__欺骗了
print(obj is None)  # False,身份判断不受影响
1
2
3
4
5
6
7

obj明明不是None,obj == None却返回True。用is None可以绕开任何自定义的相等性逻辑,PEP 8也明确建议和None比较时使用is或is not。True、False同样是单例,但条件判断里一般直接写if flag:,不写flag is True。

# 小整数缓存和字符串驻留不是语言规则

在CPython中,-5到256的小整数会被预先创建并复用,部分字符串也会被驻留(interning,同样内容的字符串共享一个对象)。于是会看到这样的结果:

small_a = int("100")
small_b = int("100")
print(small_a is small_b)  # True,在CPython中命中小整数缓存

big_a = int("257")
big_b = int("257")
print(big_a is big_b)      # False,超出缓存范围,是两个对象
print(big_a == big_b)      # True
1
2
3
4
5
6
7
8

这里用int("...")在运行时构造整数,是为了避开编译器的常量合并。如果直接写a = 257、b = 257放在同一个脚本里,CPython编译时可能把两个相同常量合并成一个对象,a is b又会变成True;同样两行代码在交互式环境逐行输入,结果往往是False。

同一段逻辑,结果取决于取值范围、运行方式和解释器版本,这正说明它不能用来判断值是否相等。 面试时把它定性为CPython的内存优化即可,并明确说业务代码比较数字和字符串必须用==。从Python 3.8开始,对字面量使用is(如x is 1)还会触发SyntaxWarning,解释器本身也在提醒这种写法有问题。

# 知识拓展

面试官可能追问:

Q1:a is b为True,a == b一定为True吗?

不一定,最典型的反例是浮点数NaN。nan = float("nan")之后,nan is nan为True,但nan == nan为False,因为IEEE 754规定NaN不等于任何值,包括自己。

Q2:那为什么nan in [nan]返回True?

列表、字典等容器在判断成员时,会先比较身份,再比较相等,相当于x is item or x == item。同一个NaN对象身份相同,所以能被找到。这也说明容器内部的查找规则和单纯的==并不完全一样。

Q3:自定义类不实现__eq__(),==会怎么比较?

继承自object的默认__eq__()按身份比较,效果和is一样,只有同一个对象才相等。想按字段比较就要自己实现__eq__();实现后通常还要处理__hash__(),否则实例会变成不可哈希,不能放进集合或当字典键。

Q4:is not和!=有什么区别?

和正向一一对应:is not判断不是同一个对象,!=调用__ne__()判断值不相等。注意a is not b是一个整体运算符,不是a is (not b)。

Q5:is比==快,能不能为了性能用is?

is只比较身份,通常确实更轻量,但它回答的是另一个问题。为了省一点比较开销换来随时可能出错的结果,不值得。只有语义上就是比较身份(None、哨兵对象、确认是否共享对象)时才用is。

Last Updated: 10/13/2026, 5:57:00 PM

← 可变对象和不可变对象有什么区别? Python的运算符有哪些? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪