Python数字运算(-)数值、哈希、按位、布尔等运算

所有的类型都可以被比较、检测逻辑值、转换字符串,所有的数据类型都可进行数学运算。

逻辑值检测

任何数据类型或对象皆可进行逻辑值检测,默认情况下均被视为真值,除非对象或所属类重定义了bool()方法且返回False 或者是对象定义了len()方法且返回零。

以下是在逻辑检测时被视为假值的对象:

.被定义为假值的常量:None 和 False

.任何数值类型的零:0,0.0,0j,Decimal(0),Fraction(0,1)

.空的序列和多项集:'',(),{},set(),range(0)

Bool运算

Bool运算包括 and、or、not,按照优先级排列not > and > or

运算 结果 注释
x or y 如果x为false,那么返回y,否则返回x or为短路运算符,只有在第一个参数为假值时才会对第二个参数求值
x and y 如果x为false,那么返回x,否则返回y and同样是短路运算符,只有在第一个参数为真值时才会对第二个参数求值
not x 如果x为false,那么返回True,否则返回False 优先级比非布尔运算符低,因此 not a == b会被解读为not (a==b),而a == not b会造成语法错误

比较运算

Python中有八种比较运算符,优先级相同,但都比布尔运算优先级高,并且比较运算符可以任意串联。例如,x<y<=z等价于 x< y and y <=z,不同之处是前者y只被求值一次,后者会被求值两次,相同点是在x<y结果为假时z都不会被求值。

八种比较运算符如下:

运算符 含义
< 小于
<= 小于或等于
> 大于
>= 大于等于
== 等于
!= 不等于
is 对象标识
is not 否定的对象标识

比较运算注意点

. 不同数字、不同类型对象比较时绝不会相等

. 函数类型仅支持简化形式比较

. <,<=,>,>= 运算符在比较复数和其它数字类型时,或在两个对象具有无法被比较的不同类型时,或在未定义次序的其他情况时,以上皆会产生TypeErroe异常

. 不同标识类的实例比较,除非定义了eq()方法,否则不相等

. 类实例不能与相同类或其他实例或其它类型对象进行排序,除非定义了 it(),le(),gt(),ge()这些函数

. isis not无法自定义,可以被应用于任意两个对象不引发异常。

.innot in ,与上面八种比较运算符具有相同优先级,但只能支持iterable(可迭代对象)或实现了contains()方法的类型

数字运算

所有的数字类型,除了复数类型,都支持下面的运算操作,所有数字运算的优先级都高于比较运算,以下按照优先级升序排列

运算 含义
+ 两数之和
- 两数之差
$ \times $ 两数乘积
/ 两数相除,结果返回商
// 两数整除,结果返回商数
% 两数相除结果取余数
-x x取反
+x x取正
abs(x) x的绝对值
int(x) 将x转换为整数
float(x) 将x转换为浮点数
complex(re,im) 一个带有实部re和虚部im的复数,im默认为0
c.conjugate() 复数c的共轭
divmod(x,y) 执行x//y x%y ,得到x被y除之后的商和余数
pow(x,y) x的y次幂
x **y 同样表示x的y次幂
invmod(x,y) 对x模y取反

数字运算注意点:

. / 除法运算返回的永远是一个浮点数

.// 整除结果值是一个整数,但结果的类型不一定是int。 运算结果永远向负无穷方向舍入, 1//2 为0, (-1)//2 为-1,1//(-2)为-1,(-1)//(-2)为0。

. % 不能用于复数

. int() 若从浮点数转换为整数会被舍入或被截断,原因在前面文章数字类型中提到过,因为二进制浮点数问题

数字类型还能进行数学函数运算,具体会在后面math和cmath模块讲到。

整数类型按位运算

按位运算只对整数有意义,按位运算优先级低于数字运算,高于比较运算,但一元运算~与其它一元算术运算符优先级相同。

运算 含义 说明
x\ y x和y按位或
x^y x和y按位异或
x&y x和y按位与
x<<n x左移n位 负的移位会引发ValueError,左移n位等价于不带溢出检测的乘以pow(2,n)
x>>n x右移n位 负的移位同样会引发ValueErroe,右移n位等价于不带溢出检测的除以pow(2,n)
~x x按位取反

数字类型的哈希运算

不同数字类型的两个数,要做== 比较运算时,必须转成哈希值,即hash(x)==hash(y)。为便于在各种数字类型上实现并保证效率,Python对数字类型的哈希运算是基于任意有理数定义统一的数学函数,本质上hash()是通过以一个固定质数P进行P降模。P的值在 Python 中可以 sys.hash_infomodulus 属性的形式被访问。

CPython implementation detail: 所用质数设定,在 C long 为 32 位的机器上 P = 2**31 - 1 而在 C long 为 64 位的机器上 P = 2**61 - 1

运算规则如下:

  • 如果 x = m / n 是一个非负比例数,且 n 不能被 P 整除,则定义 hash(x)m * invmod(n, P) % P
  • 如果 x = m / n 是一个非负比例数,且 n 能被 P 整除(但 m 不能)则 n 不能对 P 降模,以上规则不适用;在此情况下则定义 hash(x) 为常数值 sys.hash_info.inf
  • 如果 x = m / n 是一个负比例数,则定义 hash(x)-hash(-x)。 如果结果哈希值为 -1 则将其替换为 -2
  • 特定值 sys.hash_info.inf, -sys.hash_info.infsys.hash_info.nan 被用作正无穷、负无穷和空值(所分别对应的)哈希值。 (所有可哈希的空值都具有相同的哈希值)
  • 对于一个 complexz,会通过计算 hash(z.real) + sys.hash_info.imag * hash(z.imag) 将实部和虚部的哈希值结合起来,并进行降模 2**sys.hash_info.width 以使其处于 range(-2**(sys.hash_info.width - 1), 2**(sys.hash_info.width - 1)) 范围之内。 同样地,如果结果为 -1 则将其替换为 -2

为了更好的理解运算规则,用代码实现如下:

对分数求哈希值:

>>> import sys,math
>>> def hash_fraction(m,n):
    '''计算比例数m/n的哈希值,m和n为整数,n为正数'''
    P = sys.hash_info.modulus
    #去掉P的公因数,除非m和n互质
    while m%P == n%P ==0:
        m,n = m//P, n//P
    #如果n能被P整除,hash值为固定值
    if n % P == 0:
        hash_value = sys.hash_info.inf
    else:
        #如果n不能被P整除,则对P进行降模处理
        hash_value = (abs(m)%P)*pow(n,P-2,P)%P
    #判断m是否是负数,对负数求hash
    if m < 0:
        hash_value = -hash_value
    if hash_value == -1:
        hash_value = -2
    return hash_value

对float浮点数类型求哈希值:

>>> def hash_float(x):
    #计算浮点数x的哈希值
    if math.isnan(x):
        return sys.hash_info.nan
    elif math.isinf(x):
        return sys.hash_info.inf if x > 0 else -sys.hash_info.inf
    else:
        return hash_fraction(*x.as_integer_ratio())

对复数类型求哈希值:

#计算复数z的哈希值
    hash_value = hash_float(z.real) + sys.hash_info.imag * hash_float(z.imag)
    M = 2 **(sys.hash_info.width - 1)
    hash_value = (hash_value & (M-1)) - (hash_value&M)
    if hash_value == -1:
        hash_value = -2
    return hash_value

Decimal运算实例

Decimal 进行 $+ 、- 、\times 、/$ 运算

>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> sum (data)
Decimal('19.29')
>>> min(data)
Decimal('0.03')
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> a,b,c = data[:3]
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')
>>> a + b + c
Decimal('6.66')
>>> a - b
Decimal('-0.53')
>>> 

当余数运算%应用于Decimal对象时,结果的符号是被除数的符号,而不是除数的符号

>>> -5 % 8
3
>>> Decimal(-5) % Decimal(8)
Decimal('-5')
>>> Decimal(8) % Decimal(-5)
Decimal('3')

同样Decimal也可以进行一些数学函数运算

>>> Decimal(2).sqrt()
Decimal('1.41421')
>>> Decimal(1).exp()
Decimal('2.71828')
>>> Decimal(10).ln
<built-in method ln of decimal.Decimal object at 0x1073b04a8>
>>> Decimal(10).ln()
Decimal('2.30259')
>>> Decimal('10').log10()
Decimal('1')

关于四舍五入,Decimal的quantize()方法可以将数字四舍五入为固定函数

>>> Decimal('7.325').quantize(Decimal('0.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')

Fraction运算实例

Fraction同样可以进行$+、-、\times /$ 四则运算和%运算等

>>> from fractions import Fraction
>>> import math
>>> Fraction(2,3) + Fraction(3,5)
Fraction(19, 15)
>>> Fraction(2,3) - Fraction(3,5)
Fraction(1, 15)
>>> Fraction(2,3) * Fraction(3,5)
Fraction(2, 5)
>>> Fraction(2,3) / Fraction(3,5)
Fraction(10, 9)
>>> Fraction(2,3) % Fraction(-3, 5)
Fraction(-8, 15)
>>> Fraction(2,3) % Fraction(3,5)
Fraction(1, 15)

Fraction没有sqrt()、exp()等函数方法。

参考文献:

1.https://docs.python.org/zh-cn/3.6/contents.html