当前位置: 首页 > news >正文

educoder平台课-Python程序设计-2.数值类型与常用运算

课程链接:头歌实践教学平台

自用学习整理,无商业用途!侵权即删TAT

来源:头歌实践教学平台


2.1 数字类型

Python 3 中可参与数学运算的数字类型主要有三种:

  1. 整数(int)
  2. 浮点数(float)
  3. 复数(complex)

2.1.1 整数

整数是不带小数点的数字,包括0、正整数和负整数。整数的4种进制表示如表2.1所示。 例如:123、-45、0b1101(二进制)、0o17(八进制)、0xff(十六进制)。

进制种类引导符号描述与示例
十进制由字符0到9组成,遇10进1,如:99, 156
二进制0b或0B由字符0和1组成,遇2进1,如:0b1010,0B1111
八进制0o或0O由字符0到7组成,遇8进1,如:0o107,0O777
十六进制0x或0X由字符0到9及a、b、c、d、e、f或A、B、C、D、E、F组成,遇16进1,如0xFF,0X10A

一般来说,Python 3 中整数几乎是没有限制大小的,可以存储计算机内存能够容纳的无限大整数,而且整数永远是精确的。

import math  # 导入 math 库
print(math.factorial(100)) # 计算100的阶乘,158位整数

除了通常意义上的整数以外,布尔值(bool)属于整数的子类型。布尔值是两个常量对象FalseTrue。 它们被用来表示逻辑上的真值假值。在数字类的上下文中,例如参与算术运算时,它们分别以整数 0 和 1为值参与运算 。

False + 2
True * 2 + 3

内置函数 bool()可被用来将任意值转换为布尔值,只要该值可被解析为一个逻辑值。

bool(3+4)
bool(10-5*2)

2.1.2 浮点数

浮点数有两种表示方法:

  • 十进制表示

十进制表示的浮点数由整数部分与小数部分组成,其整数和小数部分均可以没有值,但小数点必须要有,此时相当于小数部分为0。

123.45,23.0,0.78,12.,.15

import decimal
b = decimal.Decimal(pow(10.0,23))
print(b)
b = decimal.Decimal(pow(10.0,22))
print(b)
  • 科学计数法

科学计数法表示为<x>e<n>,等价于数学中的x× 10n

0.48e-5,2e3,2.53e3

计算机中数字的表示采用的是二进制的方式,十进制与二进制转换过程中可能会引入误差,所以一般来说,浮点数无法保证百分之百的精确

43.02 * 7  # 预期输出:301.14 
5.02 / 0.1  # 预期输出:50.20

Python 3对于浮点数默认提供大约17位数字的精度。占8个字节(64位)内存空间,其数字范围为1.7×10−308~1.7×10308

pow(809.0, 106)  # 输出809的106次幂的值,结果约为 1.748007496839708e+308
pow(810.0, 106)  # 输出810的106次幂的值,引发OverflowError

系统会将输入的浮点数只保留17位有效数字,其余的截断丢弃,所以在计算机中浮点数经常无法精确表示

a = 3.1415926535897932384626433832795
b = 314159.26535897932384626433832795
c = 31415926535897.932384626433832795
print(a, b, c, sep='\n')

Python默认的是17位有效数字的精度,当计算需要使用更高的精度(超过17位小数)的时候,可以使用以下方法进行处理:

print('{:.30f}'.format(314159.265358979323846264338327950288419716939937510))
print('{:.30f}'.format(3.14159265358979323846264338327950288419716939937510))

输出值:
314159.265358979348093271255493164062
3.141592653589793115997963468544
实际值:
3.14159265358979323846264338327950288419716939937510
结果表明虽然可以显示指定位数的小数部分,但是结果并不准确,超过16位有效数字后面的数字往往没有精确意义了。

2.1.3 复数

复数( (complex))由实数部分和虚数部分构成,可以用a + bj,或者complex(a,b)表示, 复数的实部a和虚部b都是浮点数。 可以用realimag分别获取复数的实部和虚部,用abs(a+bj)获得复数的模。

print((3.0 + 4.0j).real)
print((3.0 + 4.0j).imag)
print(abs(3.0 + 4.0j))
a = 3
b = 2
c = 6
# 3*x**2 + 2*x + 6 = 0
# 计算根
delta = b ** 2 - 4 * a * c
root1 = (-b + delta**0.5) / (2 * a)
root2 = (-b - delta**0.5) / (2 * a)

# 输出结果
print(f"方程的根为:{root1}")  # 方程的根为:(-0.33333333333333326+1.3743685418725535j)
print(f"方程的根为:{root2}")  # 方程的根为:(-0.3333333333333334-1.3743685418725535j)

# 获取实部和虚拟并分别保留2位小数,格式化输出
print("根1 = {:.2f} + {:.2f}j".format(root1.real, root1.imag))
print("根2 = {:.2f} + {:.2f}j".format(root2.real, root2.imag))

2.2 数字类型转换

在程序设计过程中,我们经常需要对数字类型转换。
不同数值类型的转换,可以将数据类型作为函数名,将要转换的数字做为函数的参数即可完成转换。

1. int(x, base=10): 将浮点数或整数类型字符串x转换为一个整数

当x是一个浮点数且没有参数base时,int()函数可以将这个浮点数转换成十进制整数
当x不是数字或给定了参数base时,x必须是一个整型的字符串,此时int()函数将这个整型的字符串转成十进制整数,base为整形字符串的进制,如2、8、10、16分别代表二进制、八进制、十进制和十六进制。

print(int(3.14))               # x为数字,无参数base,浮点数转整数,取整数部分,输出 3
print(int(6.98))               # x为数字,无参数base,浮点数转整数,取整数部分,输出 6

若x为浮点数,则不能有base参数,否则报错。

print(int(3.14, base=10))

若x为整数形式的字符串,缺省情况下base=10,是将一个10进制的整数形式的字符串转成十进制的整数。base可以取有值包括:0、2-36中的整数字。

print(int('10'))                 # x为整数类型的字符串,base缺省时默认其为十进制整数字符串,转为十进制整数10
print(int('11111111',base=2))    # 二进制整数字符串'11111111'转成十进制整数是255
print(int('11111111',2))         # base参数名可省略,二进制整数字符串'11111111'转成十进制整数是255
print(int('1111' + '1111',2))    # 字符串x可由多个字符串拼接而成,输出 255
print(int('107',base = 8))       # '0o'表示这是八进制的整数字符串,输出 71
print(int('107',base = 16))      # '0x'表示这是十六进制的整数字符串,输出 263
print(int('0o107',base = 8))     # '0o'表示这是八进制的整数字符串,输出 71
print(int('0x107',base = 16))    # '0x'表示这是十六进制的整数字符串,输出 263
print(int('0b1001',base = 2))    # '0b'表示这是二进制的整数字符串,输出 9

转换时,字符串开头和结尾的空白字符(空格、\n、\t 等)会被自动去除。

print(int('     10      '))           # 前后的空格将被去除,输出10
print(int('\t11111111\t',2))          # 前后的制表符(\t)将被去除,输出255
print(int('\n107\n\n',base = 8))      # 前后的换行符(\n)将被去除,输出 71
print(int('\t   \n0x107   \n\n\t',base = 16))    # 前后的所有空白字符将被去除,输出 263

当base取值为0时,系统根据字符串前的进制引导符确定该数的进制。2、8、16 进制的数字可以在代码中用__ 0b/0B 、 0o/0O 、 0x/0X __前缀来表示。

print(int('0o107',base = 0))     # '0o'表示这是八进制的整数字符串,输出 71
print(int('0x107',base = 0))     # '0x'表示这是十六进制的整数字符串,输出 263
print(int('0b1001',0))           # '0b'表示这是二进制的整数字符串,输出 9

若x字符串不满足base对应进制的整数构成规则,则抛出异常。

print(int('3.14', base=10))     # 十进制整数不能包含小数点,抛异常
print(int('1a7', base=8))     # 八进制整数不能包含a,抛异常

例: 计算边为整数的矩形面积

矩形的面积等于其长与宽的乘积,用户分两行依次输入两个正整数作为长和宽的值,编程计算矩形的面积并输出整数形式的面积。

Python中任何输入都会被当作字符串进行处理,字符串无法参与数学运算,所以在程序中需要将输入的字符串转为数值类型
当用户的输入确定是整数时,程序中可以用int()函数将输入转为整数类型,计算结果也是整数。 用int()函数不加其他参数将输入转为整数时,输入仅可包括 “0123456789” 中的数字符号。
当输入中包含小数点、字母等其他字符时,会触发ValueError异常。

# 输入整数表示的矩形的长和宽, 计算并输出矩形的面积
width = int(input())      # 用int()函数将输入转成整数,例如输入:3 
length = int(input())     # 用int()函数将输入转成整数,例如输入:4
area = width * length     # 利用面积公式计算面积
print(area)               # 输出:12

2. float(x):将整数x或浮点数类型字符串x转换为一个浮点数

print(float(3))          # 整数转浮点数,增加小数位,小数部分为0,输出:3.0
print(float('3.14'))     # 将字符串'3.14'转为浮点数3.14
print(float('0.456'))    # 将字符串'0.456'转为浮点数0.456

字符串x可以为浮点数的科学计数法表示形式。

print(float('0.48e-5'))   # 将字符串'0.48e-5'转为浮点数4.8e-06
print(float('2.53e3'))    # 将字符串'2.53e3'转为浮点数2530.0

转换时,字符串开头和结尾的range空白字符(空格、\n、\t 等)会被自动去除。

print(float('  0.678\n'))  # 前后的所有空白字符将被去除,输出0.678

例: 计算边为浮点数的矩形面积

矩形的面积等于其长与宽的乘积,用户分两行依次输入两个正浮点数作为长和宽的值,编程计算矩形的面积并输出浮点数形式的面积。

当用户的输入确定是浮点数时,可以用float()函数将输入转为浮点数类型。当输入为整数时,也会被转为浮点数,计算结果也是浮点数。

# 输入浮点数表示的矩形的长和宽, 计算并输出矩形的面积
width = float(input())      # 用float()函数将输入转成浮点数,输入:2.456
length = float(input())     # 用float()函数将输入转成浮点数,输入:3.58
area = width * length
print(area)                 # 输出:8.79248
print(f'{area:.2f}')        # 当小数位数多于2位时,保留2位小数,输出:8.79

3. eval(x):将数值型的字符串对象x转换为其对应的数值

eval函数将根据字符串的构成,自动返回整形或浮点型。

print(eval('3'))         # 转换结果为整数3
print(eval('3.'))        # 转换结果为浮点数3.0
print(eval('2.53e3'))    # 转换结果为浮点数2530.0

例: 计算不确定边长是整数还是浮点数的矩形面积

矩形的面积等于其长与宽的乘积,用户分两行依次输入两个正数作为长和宽的值,编程计算矩形的面积并输出,要求输出的数据类型与输入的数据类型保持一致。

当用户输入不确定是整数还是浮点数时,如果想保证计算结果与输入的数据类型一致,可以使用eval()函数,该函数在将输入转为可计算对象时,会保持数据类型与输入一致。输入整数时,转化后还是整数;输入浮点数时,转化后还是浮点数。

# 输入正数表示的矩形的长和宽, 计算并输出矩形的面积
width = eval(input())      # 用eval()函数将输入转成数值型
length = eval(input())     # 用eval()函数将输入转成数值型
area = width * length      # 利用面积公式计算面积
print(area)               
# 输入3,4时,输出:12;输入3.0,4.0时,输出12.0

eval() 函数还可以把用半角逗号分隔的多个字符型数据转换为一个元素为数值类型的元组。

eval('3.5,3,2.0')

利用这个特性,可以实现在一条语句中将用逗号分隔的多个输入分别命名,实现多变量的同步赋值

m, n = eval(input())              # 可以把输入的用逗号分隔的2个数值型数据分别命名为 m,n
# 例如输入 3,5.0
print(f'm={m}, n={n}')            # 输出 m、n的值
输入一个十进制整数,输出其对应的八进制数和十六进制数。要求输出的八进制数要加前缀0o,输出的十六进制数要加前缀0x。
  • input() 函数
    • 这个函数从标准输入(通常是键盘)读取一行文本,并将其作为一个字符串返回。
  • int() 函数
    • 这个函数尝试将括号内的内容转换为整数。在这里,它尝试将 input() 函数返回的字符串转换为整数。
    • 如果输入的字符串不是一个有效的整数表示,这将引发一个 ValueError 异常。
  • format() 方法
    • format() 是一个字符串方法,用于格式化字符串。它接受一个或多个参数,并返回一个新的字符串,其中花括号 {} 被替换为对应的参数值。
    • 在这个例子中,format() 方法接受一个参数(由 int(input()) 提供),并使用这个参数来替换字符串中的两个花括号占位符 {0}
  • 格式化字符串
    • '{0:#o}\n{0:#x}' 是一个格式化字符串。
    • {0:#o} 表示将第一个参数(在这里是由用户输入的整数)格式化为八进制数,并在前面加上 0o 前缀来表示这是一个八进制数。
    • \n 是一个换行符,用于在输出中插入一个新行。
    • {0:#x} 表示将同一个参数格式化为十六进制数,并在前面加上 0x 前缀来表示这是一个十六进制数。

2.3 数值运算

Python 内置了“+、-、、/、//、% 和 * ”等数值运算操作符,分别被用于加、减、乘、除、整除、取模和幂运算。

优先级运算运算符符号运算实例(a=8, b=5)解释
1+a + bprint(a + b)两个数加和,13
1-a - bprint(a - b)两个数相减,3
2*a * bprint(a * b)两个数相乘,40
2/a / bprint(a / b)两个数相除,1.6
2整除//a // bprint(a // b)两个数整除,返回整数商1
2取模%a % bprint(a % b)模运算,返回整除的余数3
3取正/取负+/-+a、-bprint(+a, -b)符号运算,返回8和-5
4**a ** bprint(a ** b)幂运算,32768

这里的加、减、乘、取正/取负与数学上同类运算意义相同,下面介绍其他几种运算。

  • 精确除法(/)

不论参与运算是数是整数还是浮点数,是正数还是负数,都直接进行除法运算,精确除法运算结果的类型总是浮点数

print(12 / 4)        # 精确除的结果永远为浮点数 3.0
print(-10 / 4)       # -2.5
print(37.58 / -0.5)  # -75.16
print(37.58 / 3)     # 12.526666666666666
  • 整除(//)

采用的是向下取整的算法得到整数结果。所谓向下取整,是在计算过程中,向负无穷大的方向取整。需要注意的是,当参与运算的两个操作数都是整型数字时,结果是整型;当有浮点数参与运算的时候,结果为值为整数的浮点数

print(10 // 4)        # 取负无穷大方向最接近 2.5的那个整数 2
print(10.0 // 4)      # 2.0,结果为浮点类型的整数
print(-10 // 4)       # 取负无穷大方向最接近 -2.5的那个整数 -3
print(37.58 // -3.5)  # 取负无穷大方向最接近 -10.737142857142857的那个整数 -11.0
  • 模运算(% )

模运算数学定义如下:

a % b = a - (a // b) * b

操作数 a 和 b 可以是整数,也可以是浮点数。Python 采取的向下取整算法决定了模运算的一个规律:
模非零时,其符号与操作数 b 相同*

print(-11 % 4)   # 输出:1
print(-11 % -4)  # 输出:-3
print(11 % 4)    # 输出:3
print(11 % -4)   # 输出:-1
print(3.5 % -2)  # 输出:-0.5
print(4 % -2)    # 输出:0

模运算在数论和程序设计中都有着广泛的应用,主要应用于具有周期性规律的场景,从奇偶数的判别到素数的判定都会用到模运算。
例如: 根据x % n结果是否为0可判定x能否被n整除;
根据 x % 2 的结果是 0 还是 1 判断整数 x 的奇偶性。
下面是一些常见的应用方式。

判断整除

a % 4 == 0 and a % 100 != 0  # 结果为True则表示a可被4整除但不能被100整除

应用:输入一个年份,判断是否闰年

year = int(input())    # 输入一个整数表示年份
if year % 400 == 0 or year % 100 != 0 and year % 4 == 0:  # 如果能被400整除或是能被4整除但不能被 100整除
    print(f'{year}年是闰年')
else:
    print(f'{year}年是平年')

判断奇偶

a % 2 == 0   # 结果为True则表示a是偶数;结果为False则表示a是奇数

应用:输出列表中的偶数

score = [56,45,89,78,23,19,22]  # 一个列表
for i in score:      # 遍历列表中的数字
    if i % 2 == 0:   # 如果当前数字是偶数
        print(i)     # 输出当前数字

使结果落在某区间

a % 7   # 结果在 0, 1, 2, 3, 4, 5, 6中
a % 26  # 结果在 0, 1, 2,......24, 25中

应用: 已知2022年9月1日是星期四,输入一个日期,判断是星期几

date = int(input())
day = date % 7 + 3  # 对7取模的值为0,1,2,3,4,5,6
print(f'2022年9月{date}日是星期{day}')
num = '日一二三四五六'  # 字符串,字符序号为0,1,2,3,4,5,6
print(f'2022年9月{date}日是星期{num[day]}')  # num[day]取字符串中第几个字符

应用:输入一个大写字母,输出它后面第4个大写字母

letter = input()
new_letter = chr((ord(letter) - 65 + 4) % 26 + 65)   # 对7取模的值为0,1,2,3,4,5,6
print(f'{letter}后面第四个大写字母是{new_letter}')
A
A后面第四个大写字母是E
Y
Y后面第四个大写字母是C
  • 幂运算(** )

Python 中用双星号“**”表示幂运算,a 的b次幂的表达式是 a ** b。操作数 a 和 b 可以是整数,也可以是浮点数。

print(3 ** 2)      # 输出:9
print(0.5 ** 2)    # 输出:0.25

a ** b 中 b 为浮点数时包含开方运算,负数开偶次方结果为复数。

print(3 ** 0.5)       # 3开平方,输出:1.7320508075688772
print(8 ** (1 / 3))   # 8开立方,输出:2.0
print(2 ** 4.5)       # 等价于2的9次方再开平方,输出:22.627416997969522
print((-4) ** 0.5)    # 输出:(1.2246467991473532e-16+2j)
  • 运算优先级

数学运算是用运算符将对象连接起来构成表达式来实现。

程序设计中,表达式的写法与数学中的表达式稍有不同,需要按照程序设计语言规定的表示方法进行构造,运算符的优先级由高到低排列如下表所示:

序号运算符描述
1()括号表达式
2**幂运算
3+x、 -x正、负
4*、 / 、%乘法、除法与取模
5+ 、-加法与减法

特别需要注意的是,优先级相同时,除了幂运算和正、负运算的结合方向是从右向左,其他运算顺序为从左到右

在复杂表达式中适当加括号是较好的编程习惯,既可以确保运算按自己预定的顺序进行,又提高程序的可读性和可维护性。

print(-2 ** 4)        # 幂运算优先级高于取负运算,相当于 -(2**4),输出-16
print((-2) ** 4)      # 括号改变优先级,3先取负,再进行幂运算,输出16
print(2 ** -4)        # 幂运算和取负运算均为从右向左,4先取负,再进行幂运算,输出0.0625
print(2 ** 3 ** 2)    # 幂运算从右向左,相当于2**(3**2)即2**9,输出512
print((2 ** 3) ** 2)  # 括号改变优先级,即8**2,输出64

实例: 一元二次方程求解

一元二次方程可以用求根公式进行求解。
现有一元二次方程:

ax2+bx+c=0

当a,b,c的值分别为5,8,3时,编程求其实根。

解析:此题中,判别式 Δ=b2−4ac=8×8−4×5×3=4>0

,该方程有两个不相等的实数解,可利用如下一元二次方程的求根公式进行计算。

x1,x2=−b±√b2−4ac2a

a, b, c = 5, 8, 3  # 同步赋值,5,8,3分别赋值给a,b,c
x1 = (-b + (b * b - 4 * a * c) ** (1 / 2)) / (2 * a)
x2 = (-b - (b * b - 4 * a * c) ** (1 / 2)) / (2 * a)
# x2 = (-b - (b ** 2 - 4 * a * c) ** 0.5) / (2 * a) # 用0.5代替1/2
print(x1, x2)                       # 在一行内输出-0.6 -1.0,输出结果用空格分隔
print(f'x1 = {x1}, x2 = {x2}')      # 在一行内输出x1 = -0.6, x2 = -1.0

程序中将求根公式转换为程序中的表达式:

x1 = ( -b + ( b * b – 4 * a * c) ** (1 / 2) ) / ( 2 * a )
x2 = ( -b - ( b * b – 4 * a * c) ** (1 / 2) ) / ( 2 * a )

表达式中的乘号不可以省略,分母中的 (2 * a) 的括号不能省略,否则因乘除的优级相同,会按先后顺序进行运算,那么结果就是除 2 再乘 a。
如果一定要去掉括号的话,可以将2 * a 中的乘号改为除号(/)以保持数学上的运算顺序。
分子里 (1 / 2)的括号不可以省略,因为幂运算优先级高于除法运算,没有括号时会先计算 1 次幂,再除 2,计算顺序错误。为避免这个问题,可以将 (1 / 2) 改写为 0.5。

2.4 常用数学运算函数

Python 内置了一系列与数字运算相关的函数可以直接使用,如下表所示

函数功能描述
abs(x)返回 x的绝对值,x可以是整数或浮点数;当x为复数时返回复数的模
divmod(a, b)(a // b, a % b),以整数商和余数的二元组形式返回
pow(x, y[, z])返回 x的y次幂,当z存在时,返回 x的y次幂对z取余
round(number[, n])返回number舍入到小数点后n位精度的值。当小数点最后几位都是0时,输出其最短表示。当number是整数时,直接返回整数本身
max(iterable)
max(arg1,arg2,…)
从多个参数或一个可迭代对象中返回其最大值,有多个最大值时返回第一个。
min(iterable)
min(arg1,arg2,…)
从多个参数或一个可迭代对象中返回其最小值,有多个最小值时返回第一个。
sum(iterable, start=0)从 start 开始自左向右对 iterable 的项求和并返回总计值。 iterable 的项通常为数字,而 start 值则不允许为字符串。
eval(source)计算指定表达式的值source:必选参数,可以是字符串,也可以是一个任意的代码对象实例。常用于将字符串转为数字
complex([real[, imag]])返回值为 real + imag*1j 的复数,或将字符串或数字转换为复数。
如果第一个形参是字符串,则它被解释为一个复数,并且函数调用时必须没有第二个形参。第二个形参不能是字符串。
int(x, base=10)base缺省为10,当其省略时,当x为浮点数或整数字符串时返回整型数字,当x为浮点数字符串时会返回异常。
当base为其他值时可将字符串x作为base进制字符串,并将其转成十进制数字。
float([x])从一个数字或字符串x返回浮点数字

其中int()与float()函数前面已经讲解过,下面对其他函数进行说明。

  • abs(x)

返回一个数的绝对值。参数可以是整数、浮点数,返回值的类型与参数x的类型一致。

print(abs(3))        # 返回绝对值3
print(abs(-3.0))     # 返回绝对值3.0
print(abs(-3.45))    # 返回绝对值3.45

如果参数x是一个复数,则返回它的

print(abs(3 + 4j))  # 返回复数3+4j的模5.0
  • divmod(a, b)

参数a、b可以是整数、浮点数,返回以整数商和余数构成的二元组,相当于(a // b, a % b)

print(divmod(10, 3))        # 输出(3, 1)
print(divmod(10, -3))       # 输出(-4, -2)
print(divmod(-10, 3))       # 输出(-4, 2)
print(divmod(-10, -3))      # 输出(3, -1)
print(divmod(11.5, 3.5))    # 输出(3.0, 1.0)
print(divmod(11.5, -3.5))   # 输出(-4.0, -2.5)
print(divmod(-11.5, 3.5))   # 输出(-4.0, 2.5)
print(divmod(-11.5, -3.5))  # divmod()输出(3.0, -1.0)
  • pow(x, y[, z])

两参数形式 pow(x, y) 返回x的y次,等价于乘方运算:x ** y

print(pow(2, 3))      # 等价于2**3,返回8
print(pow(2, -3))      # 等价于2**-3,返回0.125
print(pow(2, 1/2))    # 等价于2**0.5,返回1.4142135623730951
print(pow(-9, 1/2))   # 等价于-9**0.5,返回复数1.8369701987210297e-16+3j,实部近似为0

如果z存在,则返回x的y次幂对z取余的结果, 比 pow(x, y) % z 更高效。此时三个参数均必须为整数。

print(pow(1999, 1998, 1997)) 
print(1999 ** 1998 % 1997) 

Python 3.11以后的版本做了一些限制,试着执行下面的代码:

print(1999 ** 1998) 

触发了一个异常,同时告诉可以可以通过设置sys.set_int_max_str_digits()来解决非二进制整型与字符串互转最大数字限制。

ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit
import sys
sys.set_int_max_str_digits(0)

print(1999 ** 1998) 
  • round(number[, n])

返回浮点数number保留n位小的最短表示,若number为整数时,返回整数本身。

Python 中采用的末位取舍算法为:
四舍六入五考虑,
五后非零就进一,
五后为零看奇偶,
五前为偶应舍去,
五前为奇要进一。

四舍六入五考虑,五后非零就进一,

print(round(3.84, 1))      # 舍去,输出3.8
print(round(3.86, 1))      # 进位,输出3.9
print(round(3.125000001, 2))  # 五后非零应进一,输出3.13

五后为零看奇偶,五前为偶应舍去,五前为奇要进一

print(round(3.12500000, 2))     # 五前为偶应舍去,输出3.12
print(round(3.11500000, 2))     # 五前为奇要进一,输出3.12

如果n被省略或为None,则返回最接近输入值的整数,即取整

print(round(3.14))
print(round(-3.84))

小数部分的末位为0 或n 超过小数位数时,返回该数的最短表示,舍弃浮点数末尾无意义的零。

print(round(3.000006, 2))       # 期望输出3.00,实际输出其浮点数的最短表示3.0
print(round(3.140000005, 4))    # 期望输出3.1400,实际输出3.14

需要注意的是,多数浮点数无法精确转为二进制,会导致部分数字取舍与期望不符。

print(round(3.1425, 3))  # 五前为偶应舍去,期望输出3.142,实际输出3.143
print(round(2.675, 2))   # 五前为奇应进一,期望输出2.68,实际输出2.67

使用decimal模块可观察浮点数的精确值,round()函数的舍入实际上是按照其精确值处理的。

import decimal  # 引入decimal模块

print(decimal.Decimal(3.1425))  
print(decimal.Decimal(2.675))

print(decimal.Decimal(14.95))  
print(decimal.Decimal(14.65))
当round(number,n)函数对整数number保留小数点后n位时,得到的数据仍为整数类型。
print(round(5,4))  # 输出 5
  • max(iterable)或max(arg1,arg2,…)

max(arg1,arg2,…) 从多个参数中返回其最大值

print(max(80, 100, 1000))
print(max(32.7, 45.9, -100))

max(iterable) 从一个可迭代对象中返回其最大值

print(max([80, 100, 1000]))     # 返回列表中的最大值
print(max({32.7, 45.9, -100}))  # 返回集合中的最大值
  • min(iterable)或min(arg1,arg2,…)

min(arg1,arg2,…) 从多个参数中返回其最小值

print(min(80, 100, 1000))
print(min(32.7, 45.9, -100))

min(iterable) 从一个可迭代对象中返回其最小值

print(min([80, 100, 1000]))     # 返回列表中的最小值
print(min({32.7, 45.9, -100}))  # 返回集合中的最小值
  • sum(iterable, start=0)

省略start时,将元素为数值的可迭代对象iterable中的元素累加

print(sum(range(11)))      # 1+2+3+......+10,返回55
print(sum([1, 2, 3, 4]))   # 1+2+3+4,返回10

若给strat参数传值,将元素为数值的可迭代对象iterable中的元素累加到start上

print(sum(range(11), start=10))  # 10+1+2+3+......+10,返回65
print(sum([1, 2, 3, 4], 20))     # 20+1+2+3+4,返回30

start参数值非整数时的应用:列表降维

若一个二维列表包含 n 个一维列表元素,可使用sum()函数将这些子列表拼成一个新的一维列表,起到降维的效果。

ls=[[95, 96, 85, 63, 91], [75, 93, 66, 85, 88], [86, 76, 96, 93, 67], [88, 98, 76, 90, 89], [99, 96, 91, 88, 86]]
print(sum(ls,[]))

在某些使用场景时,不要用sum()。例如当以扩展精度对浮点数求和时,使用sum()可能无法得到精确结果,此时推荐使用 math.fsum()。

print(sum([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]))   # 期望输出1.0,实际结果为0.9999999999999999
import math  # 引入math模块
print(math.fsum([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]))  # 可获得精确的结果,输出1.0
  • eval(source)

前面我们讲解过使用eval()进行数值类型转换
实际上,source参数是一个字符串,python会将source当做一个python表达式(从技术上讲,是一个条件列表)进行解析和计算。

x = 1
y = 3
print(eval('x+1'))        # 计算表达式x+1,输出2
print(eval('pow(y, 2)'))  # 计算表达式pow(y, 2),输出9

当eval()解析到表达式是不可以计算后,就会查找它是不是一个变量的名字,如果是一个变量的名字,那么它会输出这个变量的内容,否则就会抛异常。

s = 'abc'
print(eval('s'))  # 解析到s,将其当作变量名,输出其值'abc'
s = '"abc"'
print(eval(s))    # eval去除单引号后得到了字符串“abc”,直接输出。
s = 'abc'
print(eval(s))   # 解析到abcd,将其当作变量名,此时该变量名未定义,抛NameError异常
  • complex([real[, imag]])

如果第一个形参real是字符串,则它被解释为一个复数。此时函数调用时必须没有第二个形参,否则将抛异常。

print(complex('1'))     # 解释为复数,此时虚部为0,输出(1+0j)
print(complex('1+2j'))  # 解释为复数,输出(1+2j)
print(complex('1',2))        # 第一个参数为字符串,此时若设置第二个参数,将抛异常

如果第一个形参real是数值型(整数、浮点数或复数),则函数调用时可以有第二个形参imag,返回值为 real + imag*1j 的复数。如果省略了imag,则默认值为零。

print(complex(1))           # 实部为1,此时虚部为默认值0,输出(1+0j)
print(complex(1, 2))        # 输出(1+2j)
print(complex(3.4, 5.6))    # 输出(3.4+5.6j)
print(complex(1+2j, 3+4j))  # 计算(1+2j)+(3+4j)*j,输出(-3+5j)

第二个形参imag不能是字符串,否则将抛异常。

print(complex(1, '2'))        # 第二个参数为字符串,抛异常

2.5 math模块及其应用

模块(module)是 Python 中非常重要的东西,可以把它理解为 Python 的扩展工具,可提供面向特定领域或方向的程序功能。Python 安装好之后,默认安装了一些模块,称为“标准库”,“标准库”中的模块不需要安装,就可以直接使用。
没有纳入标准库的模块,需要使用 “pip install模块名”安装之后才能使用。

在数学运算之中,除了加、减、乘、除运算之外,还有其它更多的运算,比如乘方、开方、对数运算等等,要实现这些运算,可以使用Python 中的math模块。该模块是内置模块,不需要安装,可直接使用。

Python中导入模块的方法有两种:

  • 导入模块名

此时,程序可以调用模块名中的所有函数,语法表示如下:

import 库名

调用模块中函数时,需明确指出函数所在的模块的名称,格式如下:

模块名.函数名(函数参数)

import math   # 导入math模块,引用其中的函数时需要用在函数名前面加“math.”

print(math.pi)             # 输出math模块中的pi值3.141592653589793
print(math.sqrt(5))        # 调用math模块中的sqrt函数求解5的平方根并输出

若调用模块中函数时,未指出函数所在的模块的名称,将抛异常。

import math   

print(pi)   # 未指明模块名,抛未定义异常
  • 直接导入库中的函数

可以同时引用多个函数,各函数间用逗号分隔,也可以用通配符“*”代替,表示引入该为中的所有函数,语法表示如下:

from <库名 > import <函数名,函数名,...,函数名 > from <库名 > import *

此时,调用该库的函数时不需要指明函数所在库的名称,格式如下:

<函数名 >(<函数参数 >)

from math import pi,sqrt  # 导入math中的常数pi和sqrt()函数,直接引用函数名

print(pi)             # 输出math模块中的pi值3.141592653589793
print(sqrt(5))        # 调用math模块中的sqrt函数求解5的平方根并输出
from math import *    # 导入math中的所有函数,直接引用函数名
print(pi)             # 输出math模块中的pi值3.141592653589793
print(sqrt(5))        # 调用math模块中的sqrt函数求解5的平方根并输出

一般程序较简单时,只引入一个库或所引用的函数仅在一个库中存在时,两种方法都可以使用。
当编写的程序较复杂、引用多个库时,可能在多个库中存在同名函数,而这些同名函数功能可能不同。这时建议使用第一种方法,明确指出所引用的函数来自于哪个库,以免出现错误。

math库中包括:
24个数论与表示函数
10个幂和对数函数
9个三角函数
6个双曲函数
2个角度转换函数
4个特殊函数
5个数学常量

24个数论与表示函数

序号函数或常量描述与示例
1ceil(x)返回 x 的向上取整,即大于或等于 x 的最小的整数
2comb(n, k)返回不重复且无顺序地从 n 项中选择 k 项的方式总数
3copysign(x, y)返回一个基于 x 的绝对值和 y 的符号的浮点数。在支持带符号零的平台上,copysign(1.0, -0.0) 返回 -1.0.
4fabs(x)返回 x 的绝对值
5factorial(x)以一个整数返回 x 的阶乘。
6floor(x)返回 x 的向下取整,小于或等于 x 的最大整数。
7fmod(x, y)返回 fmod(x, y)
8frexp(x)以 (m, e) 对的形式返回 x 的尾数和指数
9fsum(iterable)返回迭代中的精确浮点值
10gcd(*integers)返回给定的整数参数的最大公约数
10isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0)若 a 和 b 的值比较接近则返回 True,否则返回 False
12isfinite(x)如果 x 既不是无穷大也不是NaN,则返回 True ,否则返回 False
13isinf(x)如果 x 是正或负无穷大,则返回 True ,否则返回 False
14isnan(x)如果 x 是 NaN(不是数字),则返回 True ,否则返回 False
15isqrt(n)返回非负整数 n 的整数平方根。 这就是对 n 的实际平方根向下取整,或者相当于使得 a² ≤ n 的最大整数 a。
16lcm(*integers)返回给定的整数参数的最小公倍数
17ldexp(x, i)返回 x * (2**i) 。 这基本上是函数 frexp() 的反函数。
18modf(x)返回 x 的小数和整数部分。两个结果都带有 x 的符号并且是浮点数
19nextafter(x, y)返回 x 趋向于 y 的最接近的浮点数值
20perm(n, k=None)返回不重复且有顺序地从 n 项中选择 k 项的方式总数
21prod(iterable, *, start=1)计算输入的 iterable 中所有元素的积。 积的默认 start 值为 1。
22remainder(x, y)返回 IEEE 754 风格的 x 相对于 y 的余数。对于有限 x 和有限非零 y ,这是差异 x - n*y ,其中 n 是与商 x / y 的精确值最接近的整数。如果 x / y 恰好位于两个连续整数之间,则将最接近的 偶数 用作 n 。
23trunc(x)返回去除小数部分的 x ,只留下整数部分。
24ulp(x)返回浮点数 x 的最小有效比特位的值

10个幂和对数函数

序号函数或常量描述与示例
1cbrt(x)返回 x的立方根(3.11新增)
2exp(x)返回 e 次 x 幂,其中 e = 2.718281... 是自然对数的基数。这通常比 math.e ** x 或 pow(math.e, x) 更精确。
3exp2(x)返回 2 的x次幂(3.11新增)
4expm1(x)返回 e 的 x 次幂,减1。这里 e 是自然对数的基数。
5log(x[, base])使用一个参数,返回 x 的自然对数(底为 e )
6log1p(x)返回 1+x 的自然对数(以 e 为底)。 以对于接近零的 x 精确的方式计算结果。
7log2(x)返回 x 以2为底的对数。这通常比 log(x, 2) 更准确。
8log10(x)返回 x 底为10的对数。这通常比 log(x, 10) 更准确
9pow(x, y)将返回 x 的 y 次幂, pow(1.0, x) 和 pow(x, 0.0) 总是返回 1.0
10sqrt(x)返回 x 的平方根

9个三角函数

序号函数或常量描述与示例
1acos(x)返回以弧度为单位的 x 的反余弦值。 结果范围在 0 到 pi 之间
2asin(x)返回以弧度为单位的 x 的反正弦值。 结果范围在 -pi/2 到 pi/2 之间
3atan(x)返回以弧度为单位的 x 的反正切值。 结果范围在 -pi/2 到 pi/2 之间
4atan2(y, x)以弧度为单位返回 atan(y / x) 。结果是在 -pi 和 pi 之间
5cos(x)返回 x 弧度的余弦值
6dist(p, q)返回 p 与 q 两点之间的欧几里得距离,以一个坐标序列(或可迭代对象)的形式给出。 两个点必须具有相同的维度。
7hypot(*coordinates)返回欧几里得范数,sqrt(sum(x**2 for x in coordinates))。 这是从原点到坐标给定点的向量长度
8sin(x)返回 x 弧度的正弦值
9tan(x)返回 x 弧度的正切值

6个双曲函数

序号函数或常量描述与示例
1acosh(x)返回 x 的反双曲余弦值
2asinh(x)返回 x 的反双曲正弦值
3atanh(x)返回 x 的反双曲正切值
4cosh(x)返回 x 的双曲余弦值
5sinh(x)返回 x 的双曲正弦值
6tanh(x)返回 x 的双曲正切值

2个角度转换函数

序号函数或常量描述与示例
1degrees(x)将角度 x 从弧度转换为度数
2radians(x)将角度 x 从度数转换为弧度

4个特殊函数

序号函数或常量描述与示例
1erf(x)返回 x 处的 error function
2erfc(x)返回 x 处的互补误差函数
3gamma(x)返回 x 处的 伽马函数 值
4lgamma(x)返回Gamma函数在 x 绝对值的自然对数

5个数学常量

序号函数或常量描述与示例
1pi数学常数 π = 3.141592653589793,精确到可用精度
2e数学常数 e = 2.718281828459045,精确到可用精度
3tau数学常数 τ = 6.283185307179586,精确到可用精度。Tau 是一个圆周常数,等于 2π,圆的周长与半径之比。
4inf浮点正无穷大
5nan一个浮点的 "非数字"(NaN)值。使用 isnan() 函数来测试 NaN,而不是 is 或 ==
import math

print(math.pi)    # 3.141592653589793
print(math.e)     # 2.718281828459045
print(math.tau)   # 6.283185307179586

这些函数一般都是对C语言库中同名函数进行简单封装,仅支持整数和浮点数,不支持复数运算
如果需要复数支持,可以使用cmath模块。

下面对其中较常用的函数做简单说明,其他情况可参考官方文档math --- 数学函数 — Python 3.13.2 文档 。

  • 数学常量

math.pi:返回圆周率常数π值
math.e:返回自然常数e值
import math
print(math.pi)
print(math.e)
  • 数论与表示函数

math.fabs(x):以浮点数形式返回x的绝对值。
import math
print(math.fabs(-6))  # 返回浮点型的绝对值,输出6.0
math.factorial(x):返回x的阶乘,要求x为非负整数,x为负数或浮点数时返回错误。
print(math.factorial(6))  # 返回6!,输出720
print(math.factorial(-6))  # 参数为负数,抛异常
print(math.factorial(6.5))  # 参数为浮点,抛异常
math.fsum(iterable)):返回浮点数迭代求和的精确值,避免多次求和导致精度损失。
print(sum([.1, .1,.1, .1, .1, .1, .1, .1, .1, .1]))     
print(math.fsum([.1, .1, .1, .1, .1, .1, .1, .1, .1, .1])) 
math.gcd(*integers):返回给定的整数参数的最大公约数。

如果参数之一非零,则返回值将是能同时整除所有参数的最大正整数。
如果所有参数为零或无参数,则返回值为 0。
在 3.9 版后添加了对任意数量的参数的支持,3.8版本或之前只支持两个参数。

print(math.gcd(88, 44))      # 返回2个值的最大公约数,输出44
print(math.gcd(88, 44, 22))  # 返回3个值的最大公约数(3.9新增),输出22
print(math.gcd(88, 44, 0))   # 返回值是能同时整除所有参数的最大正整数,输出44
print(math.gcd(0, 0))        # 如果所有参数为零或无参数,则返回值为0
print(math.gcd())  
math.lcm(*integers):返回给定的整数参数的最小公倍数。(3.9版新增函数)

如果所有参数均非零,则返回值将是为所有参数的整数倍的最小正整数。
如果参数之一为零,则返回值为 0。
不带参数的 lcm() 返回 1。

print(math.lcm(5, 44, 22))  # 返回3个值的最小公倍数,输出220
print(math.lcm(22, 44, 0))  # 如果参数之一为0,返回值为0
print(math.lcm(0, 0))
print(math.lcm())           # 无参数时,返回值为1
math.prod(iterable, *, start=1):计算输入的可迭代对象 iterable 中所有元素的积。

积的默认起始值start为 1。
当可迭代对象为空时,返回起始值。
此函数特别针对数字值使用,并会拒绝非数字类型。

print(math.prod([1,2,3,4,5]))            # 1*2*3*4*5,输出120
print(math.prod([1,2,3,4,5], start=2))   # 2*1*2*3*4*5,输出240
print(math.prod([]))
print(math.prod([], start=2))            #当可迭代对象为空时,返回起始值start(默认为1)。
math.floor(x):返回不大于x的最大整数。
print(math.floor(9.99))    # 输出9
print(math.floor(-9.01))   # 输出-10
print(math.floor(9))       # 输出9
math.ceil(x):返回不小于x的最小整数。
print(math.ceil(9.01))    # 输出10
print(math.ceil(-9.99))   # 输出-9
print(math.ceil(9))       # 输出9
math.isqrt(n):返回非负整数 n 的整数平方根。

即对 n 的实际平方根向下取整,或者相当于使得 a² ≤ n 的最大整数 a。

print(math.isqrt(99))            # 输出 99的整数平方根 9

某些情况下,对于正数 n,需求使得 n ≤ a² 的最小整数 a ,或者换句话说就是 n 的实际平方根向上取整,可使用下面的方法实现。

print(1 + math.isqrt(99 - 1))    # 输出不小于99的平方根的最小整数10
  • 幂和对数函数

math.log(x[, base]):返回x以base为底的对数。

使用一个参数,返回 x 的自然对数(底base为 e )。

使用两个参数,返回给定的 base 为底的对数 x ,计算为 log(x)/log(base) 。

print(math.log(10))      # 返回10的自然对数
print(math.log(10, 2))  # 返回10以2为底对数
math.log2(x):返回以2为底的x的对数。

其值通常比log(x, 2)值更精确

print(math.log2(50))    # 返回50以2为底对数
math.log10(x):返回以10为底的x的对数。

其值通常比log(x, 10)值更精确

print(math.log10(50))    # 返回50以10为底对数
math.exp(x):返回 e的x次方
print(math.exp(2))               # 输出 e 的平方,7.38905609893065
math.pow(x, y):返回x的y次幂

结果为浮点数,pow(1.0, x) 和 pow(x, 0.0) 总返回1.0。

print(math.pow(2, 3))             # 输出 2 的 3 次方 8.0
print(math.pow(1.0, 5))           # 输出 1.0
print(math.pow(5, 0))             # 输出 1.0
math.sqrt(x):返回x的平方根

结果为浮点数

print(math.sqrt(100))   # 输出 100的正数平方根 10.0
print(math.sqrt(2))     # 输出 2的正数平方根 1.4142135623730951
  • 三角函数

math.sin(x):返回弧度x的正弦值。
print(math.sin(math.pi / 6))   
print(math.sin(math.pi / 3))  
math.cos(x):返回弧度x的余弦值。
print(math.cos(math.pi / 6))   
print(math.cos(math.pi / 3))  
math.tan(x):返回弧度x的正切值。
print(math.tan(math.pi / 4))   
print(math.tan(0))  
math.asin(x):返回以弧度为单位的 x 的反正弦值。

结果范围在 −π2到 π2之间。

print(math.asin(0.5))   
print(math.asin(1)) 
math.acos(x):返回以弧度为单位的 x 的反余弦值。

结果范围在0到π之间。

print(math.acos(0.5))   
print(math.acos(0)) 
math.atan(x):返回以弧度为单位的 x 的反正切值。

结果范围在 −π2到 π2之间。

print(math.atan(1))   
print(math.atan(0)) 
math.hypot(*coordinates): 返回欧几里得范数。

即从原点到坐标给定点的向量长度

print(math.hypot(3, 4))          # 计算点(3, 4)到原点(0.0)的距离,输出 5.0
  • 角度转换函数

math.degrees(x):弧度值转角度值。
print(math.degrees(math.pi / 4))  # 输出 45.0
print(math.degrees(math.pi))      # 输出 180.0
math.radians(x):角度值转弧度值。
print(math.radians(45))    # 输出约为pi/4
print(math.radians(180))   # 输出约为pi

实例:梅钦法计算圆周率

梅钦公式是格里高利/莱布尼茨计算的公式的变体,它的收敛速度很快,至今仍然是计算圆周率的主要公式,请根据此公式计算圆周率值。

提示:math库中反正切函数为atan(x)
import math
quarter_of_pi = 4 * math.atan(1 / 5) - math.atan(1 / 239)
pi = 4 * quarter_of_pi
print(pi)  # 3.1415926535897936

2.6 逻辑值检测

Python中代表真值的布尔对象称为布尔(bool)类型,布尔类型只有两个常量实例:True和False。内置函数 bool() 可将任意值转换为布尔值。bool 是 int 的子类, 在许多数学运算场景下,True 和 Fasle 分别对应于整数“1”和“0”, 但是,一般建议使用 int() 显式地执行转换再进行运算。

print(bool(100))             # bool() 可将任意值转换为布尔值,True
print(True == 1)             # True 对应于整数“1”,比较运算结果为True
print(False == 0)            # Fasle 对应于整数“0”,比较运算结果为True
print(5 + True)              # 5 + 1,输出值为6
print(True * 5 + False * 2)  # 1 * 5 + 0 * 2,输出值为5
print(int(True) * 5 + int(False) * 2)  # 规范用法,等价于1 * 5 + 0 * 2,输出值为5

python中任何对象都能直接进行逻辑(真假)值的测试,而不需要额外的类型转换,用于if或者while语句的条件判断,也可以做为布尔(逻辑)运算符的操作数。

对象的逻辑(真假)值测试的结果为布尔类型(bool),所以对象是不管什么类型,其逻辑值检测的结果必然是TrueFalse中的一个。

在python中逻辑值的测试结果可以通过调用内置函数bool()来确认。

print(bool(3.14))   # True
print(bool(100))    # True
print(bool(3+4j))   # True
print(bool('123'))  # True
print(bool(0))      # False

利用if或while条件表达式或通过逻辑运算,任何对象都能被用于逻辑值检测。逻辑值检测的结果只有True和False。一个对象会被视为具有真值,除非其所属的类定义了在对象上调用时返回 False 的 bool() 方法或者返回零的 len() 方法。 以下基本完整地列出了具有假值的内置对象: Python中,除以下几种情形以外,其他对象的真值测试结果均为True:

  • 常量 None 或者 False

其中,None是一个特殊的常量,起到空的占位作用,它有自己的数据类型NoneType。None不是False、不是0、不是空字符串。None是一个特殊的常量,起到空的占位作用,他有自己的数据类型NoneType。

  • 任何数值类型的0

Python中一切值为0的数值类型的真值测试结果均为False。包括: 0、0.0、0j、Decimal(0)、 Fraction(0, 1))。等。

  • 空对象

Python中一切都是对象,一切空对象如:''、 ()、[]、 range(0)、{}、set()的真值测试结果均为False。
空对象包括:空字符串 空序列 空字典 空集合 空对象等。

from decimal import Decimal     # decimal模块中的小数函数Decimal
from fractions import Fraction  # fractions模块中的分数函数Fraction

print(bool(0))                  # 整数0,False
print(bool(0.0))                # 浮点数0.0与数字0等值,False
print(bool(0j))                 # 复数0j,False
print(bool(Fraction(0, 1)))     # Fraction(0,1)表示分子为0分数,0/1,False
print(bool(Decimal(0)))         # Decimal(0) 表示0,False

print(bool('hello'))            # 非空字符串,True
print(bool(100))                # 非0数字,True
print(bool(' '))                # 空格是非空字符串,True

print(bool(''))                 # 空字符串,False
print(bool(()))                 # 空元组,False
print(bool([]))                 # 空列表,False
print(bool({}))                 # 空字典,False
print(bool(set()))              # 空集合,False
print(bool(range(0)))           # 空 range,False

print(bool(None))               # None类型,False
print(bool(False))              # False

内置函数

内置函数all()用于判断给定的可迭代参数iterable中的所有元素是否都为 True。如果所有元素是否都为 True,函数返回 True,否则返回 False。

print(all([1, 2, 3]))  # 输出: True
print(all([0, 1, 2]))  # 输出: False,因为0是False

内置函数any() 用于判断给定的可迭代参数 iterable 中的任一元素是否为真。如果有一个元素为 True,则返回 True,否则返回 False。

print(any([0, 0, 1]))     # 输出: True
print(any([0, 0, None]))  # 输出: False

2.7 成员运算

运算符 in 和 not in 用于成员检测,返回布尔值True 或 False。如果 x 是 s 的成员则 x in s 值为 True,否则为 False。 x not in s 返回 x in s 取反后的值。所有内置序列和集合类型以及字典都支持此运算,对于字典来说 in 检测其是否在字典的键中存在。 对于字符串和字节串类型来说,当且仅当 x 是 y 的子串时 x in y 为 True。空字符串总是被视为任何其他字符串的子串,因此 "" in "abc" 将返回 True。成员运算符的描述如表 2.2 所示。

运算符描述实例
in如果对象在某一个序列中存在,返回 True,否则返回 Falseprint('u' in ['U','u','USD']) # 输出 True
not in如果对象在某一个序列中不存在,返回 True,否则返回 Falseprint('r' not in ['U','u','USD']) # 输出True

Python 中的成员运算使用语法是:

obj [not] in sequence
  • x in s :如果 x s 的成员则返回值值为True,否则为False。

  • x not in s :如果 x 不是 s 的成员则返回值为True,否则为False。即返回x in s 取反后的值。

s 可以是字符串,列表、元组、range、字典的键、集合等。

  • 对于字符串和字节串类型来说,当且仅当x 是y 的子串时,x in y 为True
print('admin' in 'administrator')    # True
print('ada' in 'administrator')      # False
print(' ' in 'admin')                # False,空格字符串

空字符串总是被视为任何其他字符串的子串

print('' in 'admin')    # True,空字符串
print(' ' in 'admin')   # False,空格字符串
  • 所有内置序列、集合类型、字典都支持成员运算,测试元素 x 是否为对象 s 的一个成员(元素)。

print(10 in range(1, 20))               # True,range(1,20)生成的数据中包含10
print('me' in {'you', 'me', 'he'})      # True,字符串'me'为集合中的一个元素
print('she' in {'you', 'me', 'he'})     # False,集合元素中不包含字符串'she'
print(2 in [1, 2, 3, 4, 5])             # True,整数2为列表中的一个元素
print(6 in (1, 2, 3, 4, 5))             # False,元组元素中不包含整数6

注意下面两种情况的区别。

print([1, 2] in [[1, 1], [1, 2], [1, 3]])  # True,列表[1, 2]为后面列表中的一个元素
print([1, 2] in [1, 2, 3, 4, 5])           # False,后面的列表中所有元素均为整数,并不包含列表1, 2]

对于字典来说成员运算检测对象是否在字典的键中存在。

print('me' in {'you': 100, 'me': 1000, 'he': 50})  # True
print(100 in {'you': 100, 'me': 1000, 'he': 50})   # False

实例 判断字符类型

输入一个字符,判断这个字符是小写字母还是数字。 判定一个字符的类型,可以用成员运算,用in检测目标是否在对应的字符集中存在,若目标在所有字母构成的字符串中存在则该字符是字母,若目标在所有数字构成的字符串中存在,则该字符是数字字符。

letter = input()             # 输入一个字符
ascii_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'  # 所有字母
if letter in ascii_letters:  # 成员检测,输入字符在字母字符串中存在时
    print('这是字母')         # 输出'这是字母'
if letter in '0123456789':   # 成员检测,输入字符在数字字符串中存在时
    print('这是数字')         # 输出'这是数字'

2.8 比较运算

比较运算符用于比较两个值,并确定他们之间的关系,结果是逻辑值TrueFalse

共8种比较运算:

  • 2 种一致性比较(==、!=)
  • 4 种次序比较(<、>、<=、>=)
  • 2 种标识号比较(is 和is not)

这些比较运算符的优先级相同,比布尔运算优先级高。比较运算符可以连续使用,例如: x < y <= z 相当于同时满足条件 x < y and y <= z,x is y == z 相当于x is y and y == z。比较运算与成员运算的优先级也相同,成员运算符 in 也可以与比较运算符一起连续使用。

# 输入Python考试成绩并为浮点数
score = float(input())
# 若成绩在90分以上且不大于100分,输出“优秀”
if score >= 90 and score <= 100:  # 逻辑与
    print("优秀")
if 100 >= score >= 90:            # 连续比较,推荐使用
    print("优秀")

Python 所有的内建类型都支持比较运算,不同的类型的比较方式不一样。一致性比较 (== 和 !=)基于对象的值,具有相同值的实例一致性比较结果为相等。 内置数值类型 (int, float, complex) 以及标准库类型 fractions.Fraction(分数运算) 和 decimal.Decimal(精确运算)可进行类型内部和跨类型的比较,复数不支持次序比较。在类型相关的限制以内,他们会按数学规则进行比较且不会有精度损失。

from fractions import *

print(2 == 2.0)           # 整数与浮点数比较 True
print(2 < Fraction(5, 3)) # 与分子为5,分母为3的分数比较,False

字符串使用内置函数 ord()获取其字符的 Unicode 码数字值按字典顺序进行比较。元组、列表和range等序列只可进行类型内部的比较,range不支持次序比较。以上对象的跨类型一致性比较结果将是不相等,跨类型次序比较将引发 TypeError异常。

print(ord('a'), ord('h'))     # 97 104
print('abc' < 'hello')        # 第一个字符就比较出结果,True
print([1, 2, 3] < [1, 2, 4])  # True

数值类型会根据数字大小和正负进行比较,而字符串会根据字符串序列值进行比较。int、float等同属于数值类型,可以相互比较,其他如数值、字符串等不同类型的对象不能直接进行比较运算。None和任何其他的数据类型比较永远返回False。None 是单例对象,PEP 8 建议单例对象的比较应当总是通过 is 或 is not 而不是等于运算符来进行。 运算符 is 和 is not 用于检测对象的标识号是否相同,也就是比较两个对象的存储单元是否相同,对象的标识号可使用 id() 函数来确定。当且仅当 x 和 y 是同一对象,即仅当id(x) == id(y)时, x is y 结果为True。x is not y 会产生相反的逻辑值,即当id(a) != id(b)时,引用的不是同一个对象,返回结果 True,否则返回 False。比较运算符的描述如表 3.1 所示。默认的一致性比较 (== 和 !=) 是基于对象的标识号。

具体见下表。

运算符含义描述实例(设a = 5, b = 10, c = 10)
==等于比较a、b两个对象是否相等(a == b) 返回值 False
!=不等于比较a、b两个对象是否不相等(a != b) 返回值 True
>严格大于返回a是否大于b(a > b) 返回值 False
<严格小于返回a是否小于b。返回1表示真,返回0表示假,分别True和False等价(a < b) 返回值 True
>=大于等于返回a是否大于等于b(a >= b) 返回值 False
<=小于等于返回a是否小于等于b(a <= b) 返回值 True
is同一对象当且仅当 x 和 y 是同一对象时 x is y 为真,即要求x和y的类型、值和id均相同时为True。(c is b) 返回值 True
is not不同对象当且仅当 x 和 y 不是同一对象时 x is not y 为真,即要求x和y的类型、值和id其中任一项不相同时即为True。(a is not b) 返回值 True
  • 数值类型会根据数字大小和正负进行比较,整数和浮点数可以跨类型比较。

浮点数比较时精度会影响结果

print(100 > 99)           # True
print(3.14 > 3.141)       # False
print(1.0 == 1)           # True,值相等的不同对象
  • 浮点数比较时精度会影响结果,一般不做相等比较

print(1e18 == 1e18 + 10)  # True,大浮点数比较精度影响结果
  • 字符串比较根据其字符的Unicode 码数字值大小,按字典顺序进行

print('ji lin' > 'ji an')   # True
# ord()获取字符Unicode码数字值
print([ord(y) for y in 'ji lin'])
print([ord(x) for x in 'ji an'])
  • 元组、列表和range等序列只可进行类型内部的比较,比较方法是依次比较元素的值。

print([1, 2, 3, 4] > [1, 2, 3])   # True
print ([1, 2, 3, 4] > [3, 2, 3])  # False
print((1, 2, 3, 4) > (1, 2, 3))   # True
print ((3, 2, 3) > (1, 2, 3))     # True
print(range(5) == range(0,5))     # True

特别的,range不支持次序比较。print(range(5) > range(8))

  • 不同序列类型不支持型次序比较,跨类型次序比较将引发TypeError异常。

print([1, 2, 3, 4] > (1, 2, 3))
print(0 > None)
  • 跨类型一致性比较结果将是不相等

print([1, 2, 3, 4] == (1, 2, 3, 4)) # False
print([1, 2, 3, 4] == range(1, 5))  # False

None和任何其他的数据类型的一致性比较永远返回False

print(range(0) == None)   # Range, False
print([] == None)         # 空列表, False
print('' == None)         # 空字串符, False
print(0 == None)          # 整数0 , False
  • 运算符 isis not 用于检测是否同一个对象,要求类型、值与标识号均相同

当且仅当 x 和 y 是同一对象时 x is y 为真,即要求x和y的类型、值和id均相同时为True,否则为False。

当且仅当 x 和 y 不是同一对象时 x is not y 为真,即要求x和y的类型、值和id其中任一项不相同时即为True。

a = 5
b = 5.0

print(a == b)           # True,值相等,但数据类型不同
print(id(a), id(b))     # 两个不同的id值
print(id(a) == id(b))   # False

print(a is b)           # False
print(a is not b)       # True
print([1, 2] == [1, 2, 3, 4])          # 两个列表值不同 False
print(id([1, 2]) == id([1, 2, 3, 4]))  # True
print(id([1, 2]), id([1, 2, 3, 4]))    # 1387046221120 1387046221120
print([1, 2] is [1, 2, 3, 4])          # 两个对象值不同 False
ls = [1, 2]
lst = [1, 2, 3, 4]

print(ls == lst)          # 两个列表值不同 False
print(id(ls) == id(lst))  # 不同对象,False
print(id(ls), id(lst))    # 地址不同1750617592064 1750618649920
print(ls is lst)           # 两个对象值不同 False
c = int('10')
d = 20 // 2
print(c == d)           # True,值相等
print(id(c), id(d))     # 两个相同同的id值
print(id(c) == id(d))   # True
print(c is d)           # True
print(c is not d)       # False
  • 比较运算符可以连续使用

x < y <= z 
90 <= score <= 100

等价于

x < y and y <= z
score >= 90 and score <= 100
score = int(input())
if 0 <= score <= 100:  # if 0 <= score and score <= 100:
    print('这是合法成绩')
else:
    print('这是非法成绩')

2.9 布尔运算

Python 语言支持布尔运算符,布尔运算也称为逻辑运算,包括:
and (与)
or (或)
not (非)
在执行布尔运算或当表达式被用于流程控制语句时,以下值会被解析为False:
False、None、所有类型的数字零、以及空字符串和空容器(包括字符串、元组、列表、字典、集合与冻结集合)。
所有其他值都会被解析为True。
三种运算的表达式与功能描述见表 3.3,按优先级升序排序。

运算符表达式功能描述
orx or y首先对表达式 x 求值,如果值为True则返回 x的值,否则对表达式y求值并返回其结果值。
andx and y首先对表达式 x 求值,如果值为False则返回 x的值,否则对表达式y求值并返回其结果值。
notnot x表达式 x 值为 False时返回 True,否则返回 False。

注释:

or与and都是短路运算符,因此只有在第一个参数为假值时才会对第二个参数求值。
not 的优先级比非布尔运算符低,因此 not a == b 会被解读为 not (a == b) 而 a == not b 会引发语法错误。

and 和 or 两边的 x 和 y 可以是数字、变量或表达式,布尔运算返回的并不是 False 和 True,而是返回参与布尔运算的操作数或表达式的值。

运算符为or时,解释器首先对 or 左边的表达式进行运算,当其值为 True 时直接返回左侧表达式的运算结果。此时不会对右侧表达式进行运算,这种特性也称为短路特性。

print(10 or 'hello')  # 先对左侧求值,值为True则返回左侧的值,返回整数 10
print('hello' or 10)  # 先对左侧求值,值为True则返回左侧的值,返回'hello'
print(10 or '')       # 先对左侧求值,值为True则返回左侧的值,返回整数 10

运算符为or时,解释器仅当 or 左边的表达式运算结果为False时才对右侧的表达式进行运算,此时右侧表达式的值为逻辑运算表达式的值。

print(0 or 'hello')   # 先对左侧求值,值为False则返回右侧的值,返回'hello'
print(0 or False)     # 先对左侧求值,值为False则返回右侧的值,返回False
print('' or [])       # 先对左侧求值,值为False则返回右侧的值,返回[]

运算符为and时,解释器首先对 and 左边的表达式进行运算,当左侧表达式的值为 False 时,直接返回左侧表达式的值,而右侧的表达式不会被运算。

print(0 and 'hello')   # 先对左侧求值,值为False则返回左侧的值,返回0
print(0 and False)     # 先对左侧求值,值为False则返回左侧的值,返回0
print('' and [])       # 先对左侧求值,值为False则返回左侧的值,返回''

运算符为and时,当左侧的表达式值为 True 时才对右侧的表达式进行运算并返回右侧表达式的值。

print(10 and 'hello')  # 先对左侧求值,值为True则返回右侧的值,返回'hello'
print('hello' and 10)  # 先对左侧求值,值为True则返回右侧的值,返回整数 10
print(10 and '')       # 先对左侧求值,值为True则返回右侧的值,返回''

逻辑运算 or 和 and 的值是参与运算的表达式的值之一 not 运算结果一定是布尔值,值为True或False

print(not 10)       # 10是非0整数,布尔值为True,取非结果为False
print(not '')       # '',空字符串,布尔值为False,取非结果为True

布尔运算可放到 ifwhile 后面的条件表达式中,先得到布尔运算的值,再对返回值做逻辑值检测,根据真值测试的结果决定是否执行分支下的语句。

if 10 or ' ':     # 10 and ' '布尔运算的值是10,条件运算结果为True
    print(True)   # 当10 and ''布尔运算值是True时,输出True
else:             # 否则
    print(False)  # 输出 False,执行此分支

上述分支语句的程序可以用下面一行代码实现:

print(bool(10 or ''))  # 先得到10 or ' '的结果为10,再用bool(10)返回True输出

布尔运算可放到赋值语句中,将布尔运算的结果赋值给前面的变量。
下述语句中,若input()函数接收到非空输入时,值为True,将接收到的字符串赋值给变量;若直接回车,接收到空字符串,此时布尔运算的值为or右侧的“保密”,将“保密”赋值给左侧的变量。

birthdate = input('请输入出生日期: ') or '保密'
print(birthdate)                    # 无输入,直接回车时,输出'保密'

解释器先对布尔运算符左侧的操作数进行运算这种特性称为短路特性
布尔运算符左侧的表达式已经可以决定布尔运算的结果时,该语句之后的所有代码都不会被执行
短路特性可以有效的提高效率
把容易判断、计算量较小的表达式放在布尔运算符的左侧,可以减少不必要的运算,提高算法效率。

实例 输入用户信息

编程接收用户输入的出生日期,当用户不想公开自己的出生日期时,可以直接输入回车,此时记录“保密”。 实现这个功能,可以先接收用户的输入,再用一个分支语句根据用户不同的输入给出不同的输出信息。

birthdate = input('请输入出生日期: ')  # 接收用户输入的出生日期或回车符
if birthdate:          # 当birthdate不为空字符串时,逻辑值检测结果为True
    print('你的生日是:', birthdate)   # 有输入时,输出用户输入的出生日期
else:                  # 无输入时(直接回车时),输出'保密'
    print('你的生日是:', '保密')

利用布尔运算,这个代码可以用以下方法实现,5行代码精简为2行,且代码的逻辑变得更加简洁:

birthdate = input('请输入出生日期: ') or '保密'  # 布尔表达式的短路求值结果赋值给birthdate
print('你的生日是:', birthdate)  

input()函数的返回值为接收到用户输入的字符串,在上面的代码段中,当用户输入非空时,input() 函数返回值的布尔值为“True”,那么他的值就会赋给 birthdate,且不再对or右侧进行处理;当用户不输入任何字符直接回车时,input()函数获得的是空字符串,其布尔值为False,此时布尔运算的值为or 右侧的字符串'保密',即将字符串“保密”赋值给变量 birthdate。这种表达方法与使用 if 语句效果相同,但更简洁。

解释器先对逻辑运算符左侧的操作数进行运算这种特性称为短路特性。当发生短路之后,该语句短路处之后的所有代码都不会被执行。短路特性可以有效的提高效率。把容易判断、计算量较小的表达式放在逻辑运算符的左侧,可以减少不必要的运算,提高算法效率。 例如判断一个数是否是回文素数时,将判断回文表达式str(i) == str(i)[::-1]放在运算符左侧,当判断不是回文时,不再执行右侧判定素数函数prime(i)。因判定素数的计算量较大,这样设计可以极大的降低运算量,提高效率。

实例:输出20000以内的所有回文素数

判断一个数是否是回文素数时,将判断回文表达式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}毫秒')
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}毫秒')

若将回文判定改写为通过取余和整除操作来反转整数再比较的方法,性能还可以进一步提升,表明纯粹的数值运算的优能是优于转字符串再处理的。

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

def reverse_number(num):
    """接收正整数n,判断是否为回文数,返回布尔值"""
    temp = num
    reverse_n = 0
    while temp > 0:
        reverse_n = reverse_n * 10 + temp % 10
        temp //= 10
    return num == reverse_n

start = datetime.datetime.now()  # 记录程序开始时间

for i in range(20000):
    # 利用短路提高效率
    if reverse_number(i) and prime(i): # 先判断回文,再判断素数,效率高
        print(i,end=' ')
        
end = datetime.datetime.now()  # 记录程序结束时间
during = (end - start).seconds * 1000 + (end - start).microseconds / 1000   # 计算耗时,精确到毫秒 
print(f'\n耗时:{during}毫秒')

练一练

补充完成下面的程序,判断输入的年份是否为闰年。

用户输入一个年份,若该年份为闰年,则输出'XXXX年是闰年';否则输出'XXXX年不是闰年'。

公历闰年判定遵循的规律为:“四年一闰,百年不闰,四百年再闰。”,符合以下条件之一的年份即为闰年

  1. 能被4整除而不能被100整除。
  2. 能被400整除。

输入一个正整数表示的年份,判定是否是闰年。 闰年的判定标准是能被4整除但不能被100整除的年份,或者能被400整除的年份。这个判定可以用布尔运算来实现:

输入输出样例:

输入:2000
输出:2000年是闰年

输入:2022
输出:2022年不是闰年
year = int(input())  # 输入整数年份
# 参考前例,根据条件使用if...else...判断是否为闰年,并输出对应信息
# 参考答案:
year = input()  # 输入要检查的年份

is_leap_year = year % 4 == 0 and year % 100 != 0 or year % 400 == 0

if is_leap_year:
    print(year, '年是闰年')
else:
    print(year, '年不是闰年')

and优先级高,先计算year % 4 == 0 and year % 100 != 0的值,and两侧的比较运算的结果都是布尔值,再参与到and运算得到的结果也只能是布尔值True或False,再参与优先级较低的 or 运算得到最终结果仍是布尔值 True或False,表示是闰年或非闰年。程序中的布尔运算相当于(year % 4 == 0 and year % 100 != 0) or year % 400 == 0。为了避免引起误读,在同一个表达式中同时出现 and 和 or 时,建议用加小括号的方法明确顺序,这样可以更准确的表达逻辑顺序,同时提高程序的可读性和易维护性。

练一练

改变逻辑运算的顺序,参考回文素数的例子,计算运算时间,考察不同运算顺序是否影响效率

逻辑运算符 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

实例: 登录验证

用户名为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('登录失败')

上面的代码中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('登录失败')

上面修改后的代码,表达式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('登录失败')
user_name = input()
password = input()
if (user_name == 'admin' and password == '123456') or (user_name == 'root' and password == '123456'): 
    print('登录成功')
else:
    print('登录失败')
user_name = input()
password = input()
if user_name == 'root' or user_name == 'admin':
    if password == '123456': 
        print('登录成功')
else:
    print('登录失败')

2.10 运算优先级

Python支持多种运算符的混合运算,所有运算符的优先级(由高到低排列)的描述如下表所示。

序号运算符描述
1()、[]、{}括号表达式,元组、列表、字典、集合
2x[i],x[m:n]索引、切片
3**幂运算
4+x、 -x、~x正、负,按位非 NOT
5*、 / 、//、%乘法、除法与取模
6+ 、-加法与减
7<< 、>>移位
8&按位与
9^按位异或
10|按位或
11<、<=、>、>=、!=、== 、is、is not、in、not in比较运算、成员检测和标识号检测
12not x逻辑非
13and逻辑与运算符
14or逻辑或运算符
15if…else条件表达式
16lambdalambda表达式
17:=赋值表达式、海象运算符
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)

括号的优先级最高,可以强制表达式按照需要的顺序求值。可以通过加入小括号“()”的方法来提供弱优先级的优先执行。加了括号,无需比较哪个优先级更高,使程序和表达式更加易于阅读和维护。

实例:判断是否直角三角形

输入三个数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')

上面的代码中条件都比较长,可通过改进算法简化,代码如下。

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')

浮点数计算误差的处理

计算机中浮点数在十进制和二进制转换过程中经常会出现误差,例如:
5.02 ÷ 0.1 = 50.199999999999996 5.06 * 2.09 = 10.575399999999998

print(5.02/0.1)  # 期望输出50.2,实际输出50.199999999999996
print(5.06*2.09)# 期望输出10.5754,实际输出10.575399999999998

想避免这种结果的出现,可以在程序中处理一下。

对于乘法运算,可以采用下述处理方法:

a = input()
b = input()
print(float(a) * float(b))

c = 0
if '.' in a:
    c = c + len(a[a.index('.'):]) - 1
if '.' in b:
    c = c + len(b[b.index('.'):]) - 1
    
result = float(a) * float(b)
print(round(result, c))

输入
79.03
51.0006
输出
4035.2718000000004
4035.2718
先计算两个浮点数中小数的位数,乘法运算结果中小数位数最多不超过两个参与运算的数中小数位数的和,用c做round()函数的第二个参数,可以限制结果的小数位数最多不超过参与计算的两个数中小数位的和,且round()函数处理结果会保留浮点数的最短表示,也就是会丢弃小数点后末尾的0,这样就可以得到和数学上的运算一致的结果了。

对于除法,可以采用下述处理方法:

a = input()
b = input()
print(float(a) / float(b))

n = 0
if '.' in a + b:
    n = max(len(a[a.index('.'):]), len(b[b.index('.'):]))    # 获得最大小数位数
    
result = int((float(a) * 10 ** n)) / int((float(b) * 10 ** n))  # 小数变整数
print(result)

先获得参与计算的两个浮点数中小数位数最多的数的小数位数,然后把两个数同时放大转为整数,再进行除法运算
输入
5.02
0.1
输出
50.199999999999996
50.2

数字的判定

isdigit()、isnumeric()和isdecimal()的区别

数字类型函数能否判别
unicode(半角)isdigit()
isnumeric()
isdecimal()
True
True
True
全角数字isdigit()
isnumeric()
isdecimal()
True
True
True
bytes数字isdigit()
isnumeric()
isdecimal()
True
False
False
阿拉伯数字isdigit()
isnumeric()
isdecimal()
False
True
False
汉字数字isdigit()
isnumeric()
isdecimal()
False
True
False

1. unicode(=半角数字)

num = '123'
print(num.isdigit())    # True
print(num.isnumeric())  # True
print(num.isdecimal())  # True

2. 半角与全角数字:

0-9对应Unicode编码范围:
半角——’\u0030’ 到 ‘\u0039’
全角——’\uff10’到’\uff19’
全角数字(双字节)

num = '\uff10'
print(num.isdigit())    # True
print(num.isnumeric())  # True
print(num.isdecimal())  # True

3. bytes数字

num = b'6'
print(num.isdigit())  # True
num = b'6'
print(num.isnumeric())
# Traceback (most recent call last):
#   File "<pyshell#51>", line 1, in <module>
#     num.isnumeric()
# AttributeError: 'bytes' object has no attribute 'isnumeric'
num = b'6'
print(num.isdecimal())
# Traceback (most recent call last):
#   File "<pyshell#53>", line 1, in <module>
#     num.isdecimal()
# AttributeError: 'bytes' object has no attribute 'isdecimal'

4. 罗马数字

num = 'Ⅱ'
print(num.isdigit())    # False
print(num.isnumeric())  # True
print(num.isdecimal())  # False

5. 汉字数字

num = '四'
print(num.isdigit())    # False
print(num.isnumeric())  # True
print(num.isdecimal())  # False

6.判定一个字符串是否是数字

def is_number(value):
    """判断当前数据是否是数值,返回布尔型。 """
    return value.strip().lstrip('-+').replace('.', '', 1).isnumeric()


if __name__ == '__main__':
    while s := input():
        if is_number(s):
            print(f'{s}是数字类型数据')
        else:
            print(f'{s}不是数字类型数据')

计算几何体的表面积与体积

下面代码可以用于计算几何体的表面积与体积,找到calculate(self)函数,并为其中的语句增加注释,了解其中各语句的功能

import tkinter as tk
import math

class GeometryCalculator:
    def __init__(self, master):
        self.master = master
        self.master.title("几何体计算器")
        self.master.geometry("400x300")

        self.geometry_type = tk.StringVar()
        self.geometry_type.set("球体")

        self.radius = tk.StringVar()
        self.radius.set("1")

        self.length = tk.StringVar()
        self.length.set("1")

        self.width = tk.StringVar()
        self.width.set("1")

        self.height = tk.StringVar()
        self.height.set("1")

        self.result = tk.StringVar()

        self.label1 = tk.Label(self.master, text="请选择几何体类型:")
        self.label1.pack()

        self.geometry_type_menu = tk.OptionMenu(self.master, self.geometry_type, "球体", "立方体", "长方体", "圆柱体", "圆锥体", command=self.show_input_fields)
        self.geometry_type_menu.pack()

        self.label2 = tk.Label(self.master, text="请输入半径:")
        self.label2.pack()

        self.radius_entry = tk.Entry(self.master, textvariable=self.radius)
        self.radius_entry.pack()

        self.label3 = tk.Label(self.master, text="请输入长度:")
        self.label3.pack()

        self.length_entry = tk.Entry(self.master, textvariable=self.length)

        self.label4 = tk.Label(self.master, text="请输入宽度:")
        self.label4.pack()

        self.width_entry = tk.Entry(self.master, textvariable=self.width)

        self.label5 = tk.Label(self.master, text="请输入高度:")
        self.label5.pack()

        self.height_entry = tk.Entry(self.master, textvariable=self.height)

        self.button1 = tk.Button(self.master, text="计算表面积和体积", command=self.calculate)
        self.button1.pack()

        self.label6 = tk.Label(self.master, text="结果:")
        self.label6.pack()

        self.result_label = tk.Label(self.master, textvariable=self.result)
        self.result_label.pack()

        self.show_input_fields()

    def show_input_fields(self, *args):
        geometry_type = self.geometry_type.get()
        if geometry_type == "球体":
            self.label2.config(text="请输入半径:")
            self.radius_entry.pack()
            self.label3.pack_forget()
            self.length_entry.pack_forget()
            self.label4.pack_forget()
            self.width_entry.pack_forget()
            self.label5.pack_forget()
            self.height_entry.pack_forget()
        elif geometry_type == "立方体":
            self.label2.config(text="请输入边长:")
            self.length_entry.pack()
            self.label3.pack_forget()
            self.width_entry.pack_forget()
            self.label4.pack_forget()
            self.height_entry.pack_forget()
            self.label5.pack_forget()
            self.height_entry.pack_forget()
        elif geometry_type == "长方体":
            self.label2.config(text="请输入长度:")
            self.length_entry.pack()
            self.label3.config(text="请输入宽度:")
            self.width_entry.pack()
            self.label4.config(text="请输入高度:")
            self.height_entry.pack()
            self.label3.pack()
            self.label4.pack()
            self.label5.pack_forget()
            self.height_entry.pack_forget()
        elif geometry_type == "圆柱体":
            self.label2.config(text="请输入底面半径:")
            self.radius_entry.pack()
            self.label3.config(text="请输入高度:")
            self.height_entry.pack()
            self.label3.pack()
            self.label4.pack_forget()
            self.width_entry.pack_forget()
            self.label5.pack_forget()
            self.height_entry.pack_forget()
        elif geometry_type == "圆锥体":
            self.label2.config(text="请输入底面半径:")
            self.radius_entry.pack()
            self.label3.config(text="请输入高度:")
            self.height_entry.pack()
            self.label3.pack()
            self.label4.pack_forget()
            self.width_entry.pack_forget()
            self.label5.pack_forget()
            self.height_entry.pack_forget()

    def calculate(self):
        geometry_type = self.geometry_type.get()
        if geometry_type == "球体":
            radius = float(self.radius.get())
            surface_area = 4 * math.pi * radius ** 2
            volume = 4 / 3 * math.pi * radius ** 3
        elif geometry_type == "立方体":
            length = float(self.length.get())
            surface_area = 6 * length ** 2
            volume = length ** 3
        elif geometry_type == "长方体":
            length = float(self.length.get())
            width = float(self.width.get())
            height = float(self.height.get())
            surface_area = 2 * (length * width + length * height + width * height)
            volume = length * width * height
        elif geometry_type == "圆柱体":
            radius = float(self.radius.get())
            height = float(self.height.get())
            surface_area = 2 * math.pi * radius * height + 2 * math.pi * radius ** 2
            volume = math.pi * radius ** 2 * height
        elif geometry_type == "圆锥体":
            radius = float(self.radius.get())
            height = float(self.height.get())
            surface_area = math.pi * radius * math.sqrt(radius ** 2 + height ** 2) + math.pi * radius ** 2
            volume = 1 / 3 * math.pi * radius ** 2 * height
        else:
            surface_area = 0
            volume = 0

        self.result.set(f"表面积:{surface_area:.2f},体积:{volume:.2f}")

if __name__ == "__main__":
    root = tk.Tk()
    app = GeometryCalculator(root)
    root.mainloop()

个人理财计算器项目

设计一个个人理财计算器项目可以帮助学生学习编程的基础知识。以下是一个项目指南:

项目目标

学习基础编程概念:输入/输出、基本数据类型、运算符。
培养问题分析能力:如何将现实问题转化为计算问题。
掌握工具使用:开发环境配置,代码调试。

项目设计

  1. 开发环境配置

选择编程语言:Python 是个不错的选择,简单易学。
安装工具:安装 Python 和一个代码编辑器,如 VSCode 或 PyCharm。
2. 功能需求 收入与支出输入:用户输入每月的收入和支出。
预算计算:计算每月结余或赤字。
储蓄目标:输入储蓄目标,计算达到目标所需时间。
输出结果:打印详细的每月财务报告。
3. 程序模块 输入模块
函数:get_user_input()
功能:获取用户的收入、支出和储蓄目标。

def get_user_input():
    income = float(input("请输入每月收入: "))
    expenses = float(input("请输入每月支出: "))
    savings_goal = float(input("请输入储蓄目标: "))
    return income, expenses, savings_goal

计算模块
函数:calculate_finances(income, expenses, savings_goal)
功能:计算每月结余和达到储蓄目标所需时间。

def calculate_finances(income, expenses, savings_goal):
    monthly_balance = income - expenses
    if monthly_balance > 0:
        months_to_goal = savings_goal / monthly_balance
    else:
        months_to_goal = float('inf')  # 无法实现储蓄目标
    return monthly_balance, months_to_goal

输出模块
函数:print_report(monthly_balance, months_to_goal)
功能:输出计算结果。

def print_report(monthly_balance, months_to_goal):
    print(f"每月结余: {monthly_balance:.2f} 元")
    if months_to_goal == float('inf'):
        print("当前支出超过收入,无法实现储蓄目标。")
    else:
        print(f"达到储蓄目标需要的月份: {months_to_goal:.1f}")
  1. 主程序

将以上模块组合成一个完整的程序:

def main():
    income, expenses, savings_goal = get_user_input()
    monthly_balance, months_to_goal = calculate_finances(income, expenses, savings_goal)
    print_report(monthly_balance, months_to_goal)

if __name__ == "__main__":
    main()

扩展功能

添加数据验证:确保输入为有效数字。
增加支出分类:详细记录不同类型的支出。
图形化界面:使用 Tkinter 或其他库创建简单的 GUI。

学习成果

通过这个项目,学生将学会如何分析和解决问题,掌握基本的编程技能,并能独立开发简易的应用程序。

下面是使用 Tkinter 创建的个人理财计算器的简单界面,并包含一些有意义的功能扩展:

功能扩展

支出分类:用户可以输入不同类型的支出。
保存和加载数据:可以将输入的数据保存到文件并重新加载。

import tkinter as tk
from tkinter import messagebox
from tkinter import filedialog
import json

def calculate_finances(income, expenses, savings_goal):
    monthly_balance = income - expenses
    if monthly_balance > 0:
        months_to_goal = savings_goal / monthly_balance
    else:
        months_to_goal = float('inf')  # 无法实现储蓄目标
    return monthly_balance, months_to_goal

def on_calculate():
    try:
        income = float(income_entry.get())
        rent = float(rent_entry.get())
        food = float(food_entry.get())
        entertainment = float(entertainment_entry.get())
        other = float(other_entry.get())
        savings_goal = float(goal_entry.get())
        
        total_expenses = rent + food + entertainment + other
        balance, months = calculate_finances(income, total_expenses, savings_goal)
        
        result_text = f"每月结余: {balance:.2f} 元\n"
        if months == float('inf'):
            result_text += "当前支出超过收入,无法实现储蓄目标。"
        else:
            result_text += f"达到储蓄目标需要的月份: {months:.1f}"
        
        result_label.config(text=result_text)
    except ValueError:
        messagebox.showerror("输入错误", "请输入有效的数字")

def save_data():
    data = {
        "income": income_entry.get(),
        "rent": rent_entry.get(),
        "food": food_entry.get(),
        "entertainment": entertainment_entry.get(),
        "other": other_entry.get(),
        "savings_goal": goal_entry.get()
    }
    file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
    if file_path:
        with open(file_path, 'w') as f:
            json.dump(data, f)
        messagebox.showinfo("保存成功", "数据已保存")

def load_data():
    file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
    if file_path:
        with open(file_path, 'r') as f:
            data = json.load(f)
            income_entry.delete(0, tk.END)
            income_entry.insert(0, data.get("income", ""))
            rent_entry.delete(0, tk.END)
            rent_entry.insert(0, data.get("rent", ""))
            food_entry.delete(0, tk.END)
            food_entry.insert(0, data.get("food", ""))
            entertainment_entry.delete(0, tk.END)
            entertainment_entry.insert(0, data.get("entertainment", ""))
            other_entry.delete(0, tk.END)
            other_entry.insert(0, data.get("other", ""))
            goal_entry.delete(0, tk.END)
            goal_entry.insert(0, data.get("savings_goal", ""))
        messagebox.showinfo("加载成功", "数据已加载")

app = tk.Tk()
app.title("个人理财计算器")

# 输入框和标签
tk.Label(app, text="每月收入:").grid(row=0, column=0)
income_entry = tk.Entry(app)
income_entry.grid(row=0, column=1)

tk.Label(app, text="房租:").grid(row=1, column=0)
rent_entry = tk.Entry(app)
rent_entry.grid(row=1, column=1)

tk.Label(app, text="食品支出:").grid(row=2, column=0)
food_entry = tk.Entry(app)
food_entry.grid(row=2, column=1)

tk.Label(app, text="娱乐支出:").grid(row=3, column=0)
entertainment_entry = tk.Entry(app)
entertainment_entry.grid(row=3, column=1)

tk.Label(app, text="其他支出:").grid(row=4, column=0)
other_entry = tk.Entry(app)
other_entry.grid(row=4, column=1)

tk.Label(app, text="储蓄目标:").grid(row=5, column=0)
goal_entry = tk.Entry(app)
goal_entry.grid(row=5, column=1)

# 计算按钮
calculate_button = tk.Button(app, text="计算", command=on_calculate)
calculate_button.grid(row=6, column=0, columnspan=2)

# 保存和加载按钮
save_button = tk.Button(app, text="保存数据", command=save_data)
save_button.grid(row=7, column=0)

load_button = tk.Button(app, text="加载数据", command=load_data)
load_button.grid(row=7, column=1)

# 结果显示
result_label = tk.Label(app, text="")
result_label.grid(row=8, column=0, columnspan=2)

app.mainloop()

功能说明

收入与支出输入:用户可以输入收入和多种支出类型。
储蓄目标:输入储蓄目标。
计算与显示:点击“计算”按钮后,显示每月结余和达到储蓄目标所需的时间。
数据保存与加载:可以将数据保存为 JSON 文件,并重新加载。

学习内容

Tkinter 界面设计:学习如何使用 Tkinter 创建基本 GUI。
文件操作:学习如何保存和读取 JSON 格式的数据。
数据处理:输入验证和错误处理。

 
     

相关文章:

  • 联合索引关于In和范围查询影响索引使用的情况分析
  • 解决开启Fiddle后,网页显示你的连接不是专用/私密连接
  • 使用服务器搭建无门槛ChatGPT WEB应用LobeChat
  • ​网络变压器的主要电性参数与测试方法(3)
  • 智慧消防新篇章:4G液位/压力传感器,筑牢安全防线!
  • 康托展开,逆康托展开,原理分析,题目练习
  • 那年周五放学
  • [Origin]使用origin画分组箱型图
  • 【GoTeams】-3:构建api、重构错误码
  • 【CPP】STL容器模拟实现篇之string
  • python:pymunk + pygame 模拟六边形内小球弹跳运动
  • Matlab中lfilter函数
  • 【MySQL_03】数据库基本--核心概念
  • Apache Httpd 多后缀解析
  • 防火墙带宽管理实验
  • 宠物医院台账怎么做,兽医电子处方单模板打印样式,佳易王兽医兽药开方宠物病历填写打印操作教程
  • 蓝桥云客 卡牌
  • unity3d 背景是桌面3d数字人,前面是web的表单
  • 服务器数据恢复—raid5阵列中硬盘出现坏道的数据恢复流程
  • 从CL1看生物计算机的创新突破与发展前景:技术、应用与挑战的多维度剖析
  • 网站开发用什么语言好/优化网站标题名词解释
  • 做网站店铺怎样打理/做网络推广的团队
  • 做淘客网站去哪里购买空间/做互联网项目怎么推广
  • 做企业网站和邮箱/app001推广平台官网
  • 广州网站备案/金戈枸橼酸西地那非片
  • 编程课程培训机构排名/广州seo实战培训