布尔运算也称为逻辑运算,包括“and(与)”、“or(或)”、“not(非)”运算。
运算符 | 表达式 | 功能描述 |
---|---|---|
or | x or y | 首先对表达式 x 求值,如果值为True则返回 x的值,否则对表达式y求值并返回其结果值。 |
and | x and y | 首先对表达式 x 求值,如果值为False则返回 x的值,否则对表达式y求值并返回其结果值。 |
not | not x | 表达式 x 值为 False时返回 True,否则返回 False。 |
注释:
or与and都是短路运算符,因此只有在第一个参数为假值时才会对第二个参数求值。not 的优先级比非布尔运算符低,因此 not a == b 会被解读为 not (a == b) 而 a == not b 会引发语法错误。
请注意 and 和 or 都不限制其返回的值和类型必须为 False 和 True,而是返回最后被求值的操作数, not 则不论其参数为何种类型它都会返回一个布尔值。
print(10 and 'hello') # 输出 hello,非空字符串,布尔值为True
print(bool(10 and 'hello')) # hello,非空字符串,布尔值为True
hello
True
print(10 or 'hello') # 输出整数 10,非0,布尔值为True
print(bool(10 or 'hello')) # 10,非0,布尔值为True
10
True
print(10 or '') # 输出整数 10,非0,布尔值为True
print(bool(10 or '')) # 整数 10,非0,布尔值为True
10
True
print(10 and '') # 输出'',空字符串,布尔值为False
print(bool(10 and '')) # '',空字符串,布尔值为False
False
逻辑运算 or 和 and 的值是参与运算的表达式的值之一,not 运算结果一定是布尔值,值为True或False
print(not 10) # 10是非0整数,布尔值为True,取非结果为False
print(not '') # '',空字符串,布尔值为False,取非结果为True
False
True
布尔运算可放到 if或while 后面的条件表达式中,根据真值测试的结果决定是否执行分支下的语句。
if 10 and '': # 10 and ''布尔运算的值是空字符串,条件运算结果为False
print(True) # 当10 and ''布尔运算值是True时,输出True
else: # 否则
print(False) # 输出 False,执行此分支
False
上述分支语句的程序可以用下面一行代码实现:
print(bool(10 and ''))
False
布尔运算可放到赋值语句中,将布尔运算的结果赋值给前面的变量。下述语句中,若input()函数接收到非空输入时,值为True,将接收到的字符串赋值给变量;若直接回车,接收到空字符串,此时布尔运算的值为or右侧的“保密”,将“保密”赋值给左侧的变量。
birthdate = input('请输入出生日期: ') or '保密'
print(birthdate) # 无输入,直接回车时,输出'保密'
请输入出生日期:
保密
解释器先对逻辑运算符左侧的操作数进行运算这种特性称为短路特性。当布尔运算符左侧的表达式已经可以决定布尔运算的结果时,该语句之后的所有代码都不会被执行。短路特性可以有效的提高效率。把容易判断、计算量较小的表达式放在逻辑运算符的左侧,可以减少不必要的运算,提高算法效率。
判断一个数是否是回文素数时,将判断回文表达式str(i) == str(i)[::-1]
放在运算符左侧,当判断不是回文时,不再执行右侧判定素数函数prime(i)
。因为判定素数的计算量较大,这样设计可以极大的降低运算量,提高效率。断回文素数的完整程序如下(后续章节将详细讲解,这里不讲函数,只让大家了解计算量大的放到逻辑运算符右侧会极大提高效率)
import datetime # 导入datetime,用于计算程序运行时间
def prime(n):
"""接收正整数n,判断是否为素数,返回布尔值"""
if n < 2:
return False # 0和1不是素数
for i in range(2, n): # 遍历(2, n-1)中的数
if n % i == 0: # 若在(2, n-1)中存在因子则不是素数
return False # 不是素数时返回False
else: # for语句遍历(2, n-1)中所有数,未发现因子存在时,才是素数
return True # 素数时返回True
start = datetime.datetime.now() # 记录程序开始时间
for i in range(20000):
# 利用短路提高效率
if prime(i) and str(i) == str(i)[::-1]: # 先判断素数,再判断回文,效率低
print(i,end=' ')
end = datetime.datetime.now() # 记录程序结束时间
during = (end - start).seconds * 1000 + (end - start).microseconds / 1000 # 计算耗时,精确到毫秒
print(f'\n耗时:{during}毫秒')
2 3 5 7 11 101 131 151 181 191 313 353 373 383 727 757 787 797 919 929 10301 10501 10601 11311 11411 12421 12721 12821 13331 13831 13931 14341 14741 15451 15551 16061 16361 16561 16661 17471 17971 18181 18481 19391 19891 19991
耗时:1456.962毫秒
import datetime # 导入datetime,用于计算程序运行时间
def prime(n):
"""接收正整数n,判断是否为素数,返回布尔值"""
if n < 2:
return False # 0和1不是素数
for i in range(2, n): # 遍历(2, n-1)中的数
if n % i == 0: # 若在(2, n-1)中存在因子则不是素数
return False # 不是素数时返回False
else: # for语句遍历(2, n-1)中所有数,未发现因子存在时,才是素数
return True # 素数时返回True
start = datetime.datetime.now() # 记录程序开始时间
for i in range(20000):
# 利用短路提高效率
if str(i) == str(i)[::-1] and prime(i): # 先判断回文,再判断素数,效率高
print(i,end=' ')
end = datetime.datetime.now() # 记录程序结束时间
during = (end - start).seconds * 1000 + (end - start).microseconds / 1000 # 计算耗时,精确到毫秒
print(f'\n耗时:{during}毫秒')
2 3 5 7 11 101 131 151 181 191 313 353 373 383 727 757 787 797 919 929 10301 10501 10601 11311 11411 12421 12721 12821 13331 13831 13931 14341 14741 15451 15551 16061 16361 16561 16661 17471 17971 18181 18481 19391 19891 19991
耗时:42.089毫秒
用户输入一个年份,若该年份为闰年,则输出’XXXX年是闰年’;否则输出’XXXX年不是闰年’。
公历闰年判定遵循的规律为:“四年一闰,百年不闰,四百年再闰。”,符合以下条件之一的年份即为闰年
输入输出样例:
输入:2000
输出:2000年是闰年
输入:2022
输出:2022年不是闰年
year = int(input()) # 输入整数年份
# 参考前例,根据条件使用if...else...判断是否为闰年,并输出对应信息
if (year%4==0) and (year%100 !=0) or (year%400)==0:
print("{}年是闰年".format(year))
else:
print("{}年不是闰年".format(year))
2022
2022年不是闰年
改变逻辑运算的顺序,参考回文素数的例子,计算运算时间,考察不同运算顺序是否影响效率?
# 补充你的代码
import datetime # 导入datetime,用于计算程序运行时间
start = datetime.datetime.now() # 记录程序开始时间
if (year%4==0) and (year%100 !=0) or (year%400)==0:
print("{}年是闰年".format(year))
else:
print("{}年不是闰年".format(year))
end = datetime.datetime.now() # 记录程序结束时间
during = (end - start).seconds * 1000 + (end - start).microseconds / 1000 # 计算耗时,精确到毫秒
print(f'\n耗时:{during}毫秒')
2022年不是闰年
耗时:0.166毫秒
import datetime # 导入datetime,用于计算程序运行时间
start = datetime.datetime.now() # 记录程序开始时间
if (year%100 !=0) and (year%4==0) or (year%400)==0:
print("{}年是闰年".format(year))
else:
print("{}年不是闰年".format(year))
end = datetime.datetime.now() # 记录程序结束时间
during = (end - start).seconds * 1000 + (end - start).microseconds / 1000 # 计算耗时,精确到毫秒
print(f'\n耗时:{during}毫秒')
2022年不是闰年
耗时:0.237毫秒
逻辑运算符 or、and 和 not 中,not 优先级最高,or 最低,按优先级升序排序为 or < and < not。在同一个表达式中同时出现 and 和 or 时,建议用加小括号的方法明确顺序,这样可以更准确的表达逻辑顺序,同时提高程序的可读性和易维护性。
print(1 or 0 and 2 ) # 输出 1
# 由于 or 优先级最低,最后参与运算,先计算0 and 2的值,整个表达式等价于在0 and 2上加括号
print(1 or (0 and 2)) # 输出 1
1
1
用户名为admin和root的用户的密码均为“123456”。用户输入用户名与密码,当输入的用户名为admin或root,且密码为“123456”时,输出“登录成功”;否则输出“登录失败”。
user_name = input()
password = input()
if user_name == 'root' or 'admin' and password == '123456': # 等价于(user_name == 'root') or ('admin' and password == '123456')
print('登录成功')
else:
print('登录失败')
a
123456
登录成功
上面的代码中if后面的条件表达式等价于:
(user_name == 'root') or ('admin' and password == '123456')
or两边有一边为真时,就可以通过验证。当用户输入的用户名为root时,左边为True,or运算发生短路,略过右边表达式,所以不论密码输入什么也可登陆成功。当用户输入任意用户名时,or左边为False,这时表达式结果由右边and运算结果确定。因为and左边是a非空字符串“admin”,为True,那么只要右侧的比较运算结果为True,即只要密码输入正确,无论用户名是什么,都可以登陆成功,显然与题目要求不符。
user_name = input()
password = input()
if (user_name == 'root' or 'admin') and password == '123456':
print('登录成功')
else:
print('登录失败')
abc
123456
登录成功
上面修改后的代码,表达式user_name == 'root' or 'admin'
中字符串’admin’的真值测试结果为True,因此该表达式值恒为True,这时or左边的用户名验证会失效。无论用户输入任何用户名,只要密码输入正确即可登陆成功,显然与题目要求仍然不符。
正确的代码如下:
user_name = input()
password = input()
if (user_name == 'root' or user_name == 'admin') and password == '123456':
print('登录成功')
else:
print('登录失败')
admin
123456
登录成功
user_name = input()
password = input()
if (user_name == 'admin' and password == '123456') or (user_name == 'root' and password == '123456'):
print('登录成功')
else:
print('登录失败')
ro
123456
登录失败
user_name = input()
password = input()
if user_name == 'root' or user_name == 'admin':
if password == '123456':
print('登录成功')
else:
print('登录失败')
Python支持多种运算符的混合运算,所有运算符的优先级(由高到低排列)的描述如下表所示。
序号 | 运算符 | 描述 |
---|---|---|
1 | ()、[]、{} | 括号表达式,元组、列表、字典、集合显示 |
2 | x[i],x[m:n] | 索引、切片 |
3 | ** | 幂运算 |
4 | ~ | 按位翻转 |
5 | +x、 -x | 正、负 |
6 | *、 / 、% | 乘法、除法与取模 |
7 | + 、- | 加法与减法 |
8 | << 、>> | 移位 |
9 | & | 按位与 |
10 | ^ | 按位异或 |
11 | | | 按位或 |
12 | <、<=、 >、 >=、 !=、 == | 比较运算符 |
13 | is、 is not | 同一性测试 |
14 | in、not in | 成员测试 |
15 | not x | 逻辑非 |
16 | and | 逻辑与运算符 |
17 | or | 逻辑或运算符 |
18 | if…else | 条件表达式 |
19 | lambda | lambda表达式 |
20 | := | 赋值表达式、海象运算符 |
print(4 * 2 ** 3) # 先幂运算,再计算乘法,输出:32
print(3 + 4 * -2) # 2先取反、与4相乘,再做加法,输出:-5
print(3 + 4 * 2 / 2) # 先计算乘除法,除法运算结果为浮点数,输出:7.0
print(3 << 2 + 1) # 加法优先级高,先2+1,3(11)左移3位变成24 (11000)
32
-5
7.0
24
括号的优先级最高,可以强制表达式按照需要的顺序求值。可以通过加入小括号“()”的方法来提供弱优先级的优先执行。加了括号,无需比较哪个优先级更高,使程序和表达式更加易于阅读和维护。
输入三个数a,b,c, 判断能否以它们为三个边长构成直角三角形。若能,输出YES,否则输出NO。
a = float(input())
b = float(input())
c = float(input())
# 三角形判定条件:边都为正数,任意两边之和大于第三边
if a <= 0 or b <= 0 or c <= 0 or (a + b) <= c or (a + c) <= b or (b + c) <= a:
print('No')
# 直角三角形条件,两边平方和等于第三边
elif a * a + b * b == c * c or a * a + c * c == b * b or c * c + b * b == a * a:
print('Yes')
else:
print('No')
3
4
5
Yes
上面的代码中条件都比较长,可通过改进算法简化,代码如下:
a = float(input())
b = float(input())
c = float(input())
shortest = min(a, b, c) # 获得最短边长度
longest = max(a, b, c) # 获得最长边长度
middle = sum([a, b, c]) - shortest - longest
# 若最小边为负值或长边小于等于其他两边之和时,构不成三角形
if shortest <= 0 or shortest + middle <= longest:
print('NO')
# 两边短边长度平方和等于第三边长度平方为直角三角形
elif shortest ** 2 + middle ** 2 == longest ** 2:
print('YES')
else:
print('NO')
3
4
5
YES