GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • 卡码笔记-最强八股文
  • 计算机基础
  • Python
  • C++
  • Java
  • Go
  • ChatGPT充值
  • Claude充值
  • 国内使用Claude
  • 中转站推荐
  • 🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经

This view

  • Original page
  • New address

GeezerVison is showing notes.kamacoder.com — Python运算符详解:and、or为什么返回对象、短路求值、真值测试、地板除与运算符优先级

# Python的运算符有哪些?and、or为什么会返回对象?

面试官问:"Python有哪些运算符?and和or为什么不总是返回True或False?"

很多人会列举加减乘除、大于小于,但对逻辑运算符的返回值感到困惑。看到3 and 5返回5、0 or "hello"返回"hello",以为是特殊情况。真正要讲清楚的是:Python的and、or采用短路求值,返回的是决定真假的那个对象本身,而不是转换后的布尔值。

# 简要回答

Python运算符按功能分为:算术运算符(+、-、*、/、//、%、**)、比较运算符(==、!=、>、<、>=、<=、is、is not、in、not in)、逻辑运算符(and、or、not)、位运算符(&、|、^、~、<<、>>)、赋值运算符(=、+=、-=等)和其他运算符(如海象运算符:=)。

and和or采用短路求值,返回决定结果的对象,而不是True或False。x and y在x为假时返回x,否则返回y;x or y在x为真时返回x,否则返回y。这种设计让表达式更简洁,比如result = x or default可以在x为空时使用默认值。

not是唯一总是返回布尔值的逻辑运算符,它对操作数取反后返回True或False。

# 详细回答

# 算术运算符

常见的数学运算:

print(10 / 2)    # 5.0,真除法总是返回浮点数
print(-7 // 2)   # -4,地板除向负无穷取整
print(-7 % 2)    # 1,余数符号跟随除数
print(2 ** 10)   # 1024
1
2
3
4

/总是返回浮点数,即使能整除。//是向下取整,对负数来说是朝负无穷方向,所以-7 // 2是-4而不是-3。%和//配套,满足a == (a // b) * b + a % b,因此余数的符号跟随除数。这两点是C/Java转过来的录友最容易踩的坑。

# 比较运算符

==、<这类比较对内置类型返回True或False。Python的比较运算符支持链式比较,1 < x < 10等价于1 < x and x < 10,中间的x只计算一次:

age = 25
print(18 <= age < 60)  # True
print(1 < 3 > 2)       # True,等价于 1 < 3 and 3 > 2
1
2
3

is和is not比较对象身份,已在《is和==有什么区别?》中详细讲解。

in和not in判断成员关系,比如3 in [1, 2, 3]、"py" in "python"。它的本质是调用容器的__contains__()方法,没有该方法时退回到迭代逐个比较。

# 逻辑运算符:and、or、not

这是面试重点。先明确真值测试:在if、while、and、or、not这类需要判断真假的位置,Python会检查对象的真假。None、False、数值零(0、0.0)、空容器(""、[]、{}、set()等)为假,其余对象默认为真。自定义类可以通过__bool__()或__len__()改变规则。

and和or只用真值测试做判断,返回的却是原来的操作数对象,不是布尔值。

and:遇假即停

x and y的求值规则:

  1. 先计算x,如果x为假,直接返回x,不再计算y。
  2. 如果x为真,返回y(不管y真假)。

or:遇真即停

x or y的求值规则:

  1. 先计算x,如果x为真,直接返回x,不再计算y。
  2. 如果x为假,返回y(不管y真假)。

用一段代码同时验证两条规则:

print(0 and 5)          # 0,左边为假,直接返回左边
print(3 and 5)          # 5,左边为真,返回右边
print([] or "default")  # default,左边为假,返回右边
print("Carl" or "anon") # Carl,左边为真,直接返回左边

nickname = ""
print(nickname or "匿名用户")  # 匿名用户
1
2
3
4
5
6
7

nickname or "匿名用户"是常见的默认值写法:空字符串为假,or返回右边的对象。它的坑在于0、""、[]都会被当成"没有值"。如果0是合法输入,就要写成x if x is not None else default。

not总是返回布尔值

not 0是True,not [1, 2]是False。它只有一个操作数,不存在短路问题,也不会返回原对象。

and与or的短路求值流程

图中展示了and和or的短路求值机制:and在左侧为假时直接返回左侧对象,or在左侧为真时直接返回左侧对象,都不会继续计算右侧表达式。

# 短路求值的实际意义

短路求值不仅是性能优化,还能避免错误:

data = []
if data and data[0] == 1:
    print("第一个元素是1")
print("没有报错")  # 没有报错
1
2
3
4

data是空列表,and在左边就停下了,data[0]根本不会执行,所以不会抛IndexError。同理,user and user.name可以在user为None时避免AttributeError。把"保护条件"写在and左边,是短路求值最常见的用法。

# 位运算符

对整数的二进制位进行操作:

运算符 含义 示例
& 按位与 5 & 3 → 1
\| 按位或 5 \| 3 → 7
^ 按位异或 5 ^ 3 → 6
~ 按位取反 ~5 → -6
<< 左移 5 << 1 → 10
>> 右移 5 >> 1 → 2

&、|不是逻辑运算符。它们不短路,两边都会求值;对bool使用时虽然能得到True或False,但和比较运算混用时优先级会坑人:x > 1 & x < 5实际是x > (1 & x) < 5。条件判断一律用and、or。

# 赋值运算符

=把名字绑定到对象,+=、-=、*=等是复合赋值。x += 1看起来等价于x = x + 1,但对可变对象并不完全一样:列表的+=会调用__iadd__()原地修改,x = x + [1]则创建新列表再重新绑定。如果还有其他名字指向原列表,两种写法的结果就不一样。这一点和《可变对象与不可变对象》直接相关。

Python 3.8引入了海象运算符:=(赋值表达式),可以在表达式中同时赋值和使用值:

words = ["python", "go", "javascript"]
if (n := len(words)) > 2:
    print(f"共{n}个单词")  # 共3个单词
1
2
3

海象运算符可以减少重复计算或重复调用,但不要滥用,影响可读性时还是拆开写更清楚。

# 运算符优先级

常见优先级从高到低:

  1. **(幂运算)
  2. ~、+、-(一元运算符)
  3. *、/、//、%
  4. +、-(二元加减)
  5. <<、>>(位移)
  6. &(按位与)
  7. ^(按位异或)
  8. |(按位或)
  9. 比较运算符(==、!=、>、<等)
  10. not
  11. and
  12. or

两个容易错的点:**右结合且高于一元负号,所以-2 ** 2是-4;not低于比较运算,所以not x == y是not (x == y)。拿不准就加括号。

# 知识拓展

面试官可能追问:

Q1:老代码里的cond and a or b有什么问题?

这是三元表达式出现前的模拟写法,问题在于a为假时会错误地返回b。比如True and 0 or 10结果是10而不是0。Python 2.5起就有条件表达式,直接写a if cond else b。

Q2:1 or 2 and 0返回什么?

返回1。and优先级高于or,表达式等价于1 or (2 and 0);左边1为真,or直接短路,右边根本不计算。

Q3:in运算符的时间复杂度是多少?

取决于容器。列表、元组、字符串需要逐个比较,是O(n);集合和字典的键基于哈希查找,平均是O(1)。频繁做成员判断时,先把数据转成集合。

Q4:为什么~5等于-6?

Python的整数语义规定~x等于-(x+1),可以理解为在无限位补码下按位取反。Python整数没有固定位宽,所以不会像C语言那样出现~5等于某个大正数的情况。

Q5:a < b < c和a < b and b < c完全等价吗?

结果一样,但链式比较中的b只计算一次。如果b是一个有副作用或很耗时的函数调用,两种写法的行为就会不同。

← is和==有什么区别? Python条件判断和循环的常见写法与易错点 →

评论

验证登录状态...

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.