第5章 数值类型

第五章 数据类型

本章中出现的数据结构,大部分上一张我贴过图,贴过图的不再贴了,将来会出一篇常见类型的结构图的文章

在Python中,数据采用了对象的形式,无论是Python所提供的内置对象,或是我们使用Python工具以及像C这样的其他语言所创建的对象。

Python内置的对象,即融合于Python语法中的类型。Python的原生类型,这些类型都是用高效的C语言写的。

Python工具创建的对象,即纯Python语言编写的对象,运行需要虚拟机的支持,运行效率低。

C语言创建的对象,与内置对象一样用C语言创建,执行效率高,但请设计好,毕竟内置对象是大佬们精心设计的。很通用,而自建C语言创建对象工具,要精心设计,才更加的高效。

对象是一切Python程序的基础,是Python编程中的最基本的概念,是本书第一个关注的焦点。

数值类型基础知识

Python中大部分的属质类型都是相当典型的,他们可以记录绝大多数的数值量。

在Python中,数字并不真的是一种对象类型,而是一组相似类型的分类:

  • 整数 int、浮点数 float、复数 complex、

  • 小数(decimal,十进制数):固定精度对象

  • 分数 fraction:有理数对象

  • 集合 set:带有数值运算的集合体,可以进行数学中的集合操作:空集、子集、交集、并集、补集、并交补(^)。

  • 布尔 bool:真True和假False

  • 内置函数和模块:四舍五入round、数学模块math、随机模块random等

  • 表达式;无限制整数精度;位运算;十六进制、八进制、二进制格式

  • 第三方那个扩展:向量、库、可视化、作图等

有理数:整数和分数的统称,其他数为无理数。

有理数,包括整数、有限小数和无限循环小数。

无理数,也称为无限不循环小数,不能写作两整数之比。

「有理」「无理」的用法当追溯到欧几里得《几何原本》。

《原本》卷10定义3中区分了ῥητός线和ἄλογος线,即有理线段和无理线段。ῥητός本意为「确定的、可言说的、可表达的」,ἄλογος是λόγος加上了否定前缀α-形成的形容词。λόγος在《原本》作名词「比例」用,但该名词还有「言语;原因;计算」等含义。因此「有理」当最终产生于λόγος,其含义按照现代的概念可以表达为:可以表达为两整数之比的数。(这一阐释既体现了λόγος又体现了ῥητός的含义)

作者:羟基氧
链接:https://www.zhihu.com/question/366206160/answer/977009690

数值字面量

Python提供了整数和浮点数作为其基本类型(正整数、负整数、零;带有小数部分的数字,有时简称“浮点”)

Python还允许我们使用十六进制、八进制和二进制字面量来表示整数

提供了一个复数类型;并且允许整数具有无限精度——只要内存空间允许,整数可以增长为任何位数的数字

字面量 解释
1234 , -24 , 0 , 99999999999 整数(无大小限制)
1.23 , 1. , .1 , 3.14e-10 4E210 , 4.0e+210 浮点数
0o017 , 0x01aF , 0b011001100110 八进制、十六进制、二进制字面量
(3+4j) , (3+4.1j),3j 3+4j , 3.0+4.0j , 3J 复数字面量
{‘a’, ‘m’, ‘p’, ‘s’} set(‘spam’) , {1,2,3,4} 集合
Decimal(‘1’) , Decimal(‘1.0’) , Fraction(1, 3) 小数、分数扩展类型
bool(X) , True , False 布尔类型和字面量

上述表格中,有的我使用了四个空格隔开,前面是标准字面量,后面的是非标准字面量或者表达式

标准字面量我指的是,他的输入和输出都一样,而非标准字面量,例如4E210。

另外set()、Decimal()、Fraction()都是函数,虽然后两者的输入和输出看似一样。

整数

整数:在Python中,是无限精度的

>>> a=2**(2**15*8)
>>> import sys
>>> sys.getsizeof(a)
34980
>>> s=str(a) # 当a再大些,这个转换明显耗时长了
>>> sys.getsizeof(s)
78963
>>> 78963/34980 # 字符串占用内存是整数的2.25倍
2.2573756432247

2**1000
Out[15]: 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376

mem.data('10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376')
2e 01 00 00 00 00 00 00 c2 c8 d3 7d 7f 0c 43 df
e5 31 30 37 31 35 30 38 00 00 00 00 00 00 00 00
31 30 37 31 35 30 38 36 30 37 31 38 36 32 36 37
33 32 30 39 34 38 34 32 35 30 34 39 30 36 30 30
30 31 38 31 30 35 36 31 34 30 34 38 31 31 37 30
35 35 33 33 36 30 37 34 34 33 37 35 30 33 38 38
33 37 30 33 35 31 30 35 31 31 32 34 39 33 36 31
32 32 34 39 33 31 39 38 33 37 38 38 31 35 36 39
35 38 35 38 31 32 37 35 39 34 36 37 32 39 31 37
35 35 33 31 34 36 38 32 35 31 38 37 31 34 35 32
38 35 36 39 32 33 31 34 30 34 33 35 39 38 34 35
37 37 35 37 34 36 39 38 35 37 34 38 30 33 39 33
34 35 36 37 37 37 34 38 32 34 32 33 30 39 38 35
34 32 31 30 37 34 36 30 35 30 36 32 33 37 31 31
34 31 38 37 37 39 35 34 31 38 32 31 35 33 30 34
36 34 37 34 39 38 33 35 38 31 39 34 31 32 36 37
33 39 38 37 36 37 35 35 39 31 36 35 35 34 33 39
34 36 30 37 37 30 36 32 39 31 34 35 37 31 31 39
36 34 37 37 36 38 36 35 34 32 31 36 37 36 36 30
34 32 39 38 33 31 36 35 32 36 32 34 33 38 36 38
33 37 32 30 35 36 36 38 30 36 39 33 37 36 00

mem.data(10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376)
22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00

在python中,用字符串和整数表示长符号,在内容长度不长的情况下,整数相对字符耗时不是问题,长度也略小。但如果例如电话包含()-、身份证包含X,而且有可能需要获取部分信息时,例如139、010、1999,整数需要再转换。所以看数据类型需要和后期处理选择使用什么类型存储。当然常规想大部分都是字符串,但是如果钻一下,效率、空间、可视性,来确定使用什么类型

>>> mem.data(2**10)  # 0100 0000 0000
01 00 00 00 00 00 00 00 00 04 00 00
>>> mem.data(2**30)
02 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
>>> mem.data(2**33)
02 00 00 00 00 00 00 00 00 00 00 00 08 00 00 00
>>> 33//30
1
>>> 33%30
3  # 1000
>>> 0b1000
8

简单的2的n次幂,可以推导出需要多少个块,最后一个块的数值是多少

浮点数

浮点数:带一个小数点,也可以加上一个科学计数标志e或E,在标准CPython中,采用C语言中的“双精度”来实现
这里浮点数之所以选择这个结构,是因为现在CPU的浮点运算单元都是双精度结构,FPU、FP FMA,是受硬件限制的。

314e-10
Out[7]: 3.14e-08
4E210
Out[8]: 4e+210
4.0e+210
Out[10]: 4e+210

三十六进制、十六进制、十进制、八进制、二进制

binary 二进制 01 bin(int)

decimal 十进制 0-9 int(int|[str,[2-36])

36进制:0-9A-Z 数学家的游戏?

hexadecimal 十六进制 0-9A-F hex(int)

octal 八进制 0-7 oct(int)

integers 整数;literal 字面量

这里我写的几个紧致写法并不统一,例如字面量不需要区分大小写,不需要前面加0,之所以这样写,是因为这里单纯的是字面量而非结果。

结果只存在于bin()、oct()、hex()转换的字符串中,但如果是为了代码的可读性,还是建议符合规则的去写

>>> hex(100)
'0x64'
>>> 0x64
100
>>> oct(100)
'0o144'
>>> 0o144
100
>>> bin(100)
'0b1100100'
>>> 0b1100100
100

int(100)
Out[1]: 100

int('100')
Out[2]: 100

int('100',1)
ValueError: int() base must be >= 2 and <= 36, or 0

int('100',2)
Out[4]: 4

int('100',3)
Out[5]: 9

int('100',4)
Out[6]: 16

int('100',8)
Out[7]: 64

int('100',16)
Out[8]: 256

int('100',36)
Out[9]: 1296

int('Z',36)
Out[25]: 35

int('f',35)
Out[26]: 15

0123
SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers
0X1F;0x1f;4E210;4e+210 15.1 ns

复数

Python的复数字面量写成实部+虚部,虚部以j结尾,不分大小写,虚部可以独立于实部存在。

从结构看,复数通过一对浮点数来实现。对复数的运算会按照复数的运算法则进行。

也可以通过内置函数complex(real, imag)来创建

1+1j
Out[11]: (1+1j)

1.0+1.0j
Out[12]: (1+1j)

1+1.1j
Out[13]: (1+1.1j)

1.1J
Out[14]: 1.1j

complex(1.0,1.1)
Out[15]: (1+1.1j)

mem.data(1.1j)
00 00 00 00 00 00 00 00 9a 99 99 99 99 99 f1 3f

mem.data(1+1.1j)
00 00 00 00 00 00 f0 3f 9a 99 99 99 99 99 f1 3f

mem.data(1.1+1.1j)
9a 99 99 99 99 99 f1 3f 9a 99 99 99 99 99 f1 3f

mem.data(1.1)
9a 99 99 99 99 99 f1 3f

mem.data(1)
01 00 00 00 00 00 00 00 01 00 00 00

mem.data(1.0)
00 00 00 00 00 00 f0 3f

嗯,很单纯的两个float,没有别的任何内容,虽然1.0的字面量结果是1,但还是使用float存储

编写其他的数值类型

在本章后面将会详细介绍小数和分数

import decimal,fractions
Decimal('1.0') # NameError: name 'Decimal' is not defined
Fraction(1,3) # NameError: name 'Fraction' is not defined
from decimal import Decimal
from fractions import *

>>> mem.data(Decimal('1'))
ff ff ff ff ff ff ff ff 30 c4 de 02 03 02 00 00
00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
08 7e d5 02 03 02 00 00 01 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
>>> mem.data(Decimal('1.0'))
ff ff ff ff ff ff ff ff 30 66 d2 02 03 02 00 00
ff ff ff ff ff ff ff ff 02 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00
08 7e d5 02 03 02 00 00 0a 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00

bool()
Out[54]: False
bool(X)
NameError: name 'X' is not defined
bool('')
Out[57]: False
bool('0')
Out[58]: True

mem.data(True)
01 00 00 00 00 00 00 00 01 00 00 00

mem.data(False)
00 00 00 00 00 00 00 00

Decimal的括号内容是字符串,而非整数和浮点数

内置数值工具

表达式运算符

? + - * / >> ** &等

内置数学函数

? pow、abs、round、int、hex、bin等

类型内置方法

? float.hex、float.as_integer_ratio、float.is_integer…

float.hex(1.75)
Out[48]: '0x1.c000000000000p+0'

mem.data(1.75)
00 00 00 00 00 00 fc 3f

struct.pack('>d',1.75).hex(' ')
Out[22]: '3f fc 00 00 00 00 00 00'

struct.pack('>d',1).hex(' ')
Out[25]: '3f f0 00 00 00 00 00 00'

struct.pack('>d',0.5).hex(' ')
Out[26]: '3f e0 00 00 00 00 00 00'

struct.pack('>d',2).hex(' ')
Out[27]: '40 00 00 00 00 00 00 00'

struct.pack('>d',4).hex(' ')
Out[28]: '40 10 00 00 00 00 00 00'

mem.data(-1.75)
00 00 00 00 00 00 fc bf

float.hex(-1.75)
Out[2]: '-0x1.c000000000000p+0'

float.hex(-1.75e-100)
Out[3]: '-0x1.87f4907fc448ap-332'

float.hex(-0.00000175)
Out[4]: '-0x1.d5c31593e5fb7p-20'

float.hex(-0.125)
Out[5]: '-0x1.0000000000000p-3'

float.hex(-1.25e-1)
Out[6]: '-0x1.0000000000000p-3'

这里float.hex分为三个部分、符号位、数值区、指数区

int.to_bytes(100,length=4,byteorder='big')
Out[17]: b'\x00\x00\x00d'

int.to_bytes(-100,length=4,byteorder='big')
OverflowError: can't convert negative int to unsigned

int.to_bytes(-100,length=4,byteorder='big',signed=True)
Out[19]: b'\xff\xff\xff\x9c'

hex(0xffffffff-99) # 补码的算法,-1即ffffffff
Out[40]: '0xffffff9c'

mem.data(i)
02 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00

mem.data(-i)
fe ff ff ff ff ff ff ff 00 00 00 00 04 00 00 00

struct.pack('>i',0).hex(' ')
Out[31]: '00 00 00 00'

struct.pack('>i',-1).hex(' ')
Out[32]: 'ff ff ff ff'

struct.pack('>i',-2).hex(' ')
Out[33]: 'ff ff ff fe'

mem.data(-1)
ff ff ff ff ff ff ff ff 01 00 00 00

mem.data(-2)
ff ff ff ff ff ff ff ff 02 00 00 00

(i:=2**32).bit_length()
Out[21]: 33

int.to_bytes(32,length=4,byteorder='big')
Out[26]: b'\x00\x00\x00 '

int.to_bytes(32,length=4,byteorder='big').hex(' ')
Out[27]: '00 00 00 20'

int.to_bytes(i,length=4,byteorder='big').hex(' ')
OverflowError: int too big to convert

int.to_bytes(i,length=8,byteorder='big').hex(' ')
Out[29]: '00 00 00 01 00 00 00 00'

int.to_bytes(i,length=5,byteorder='big').hex(' ')
Out[30]: '01 00 00 00 00'

工具模块

random、math等

Python表达式运算符及程序

运算符 描述
yield x 生成器函数send协议
:= (海象)赋值表达式
lambda args: expression 创建匿名函数
x if y else z 三元表达式(仅当y为真时,x才会被计算)
x or y 逻辑或(仅当x为假时,y才会被计算)
x and y 逻辑与(仅当x为真时,y才会被计算)
not x 逻辑非
x in y , x not in y 成员关系(容器/多项集:可迭代对象、集合,字典仅key)
x is y , x is not y 对象同一性测试
xy , x>=y 大小比较、集合的子集和超集
x==y , x!=y 值等价性运算符
x | y 按位或、集合并集
x ^ y 按位异或、集合对称差集
x & y 按位与、集合交集
x<>y 将x左移或右移y位
x + y 加法、拼接
x - y 减法、集合差集
x * y 乘法、重复
@ 矩阵乘,操作对象numpy.array
x % y 求余数取模数、格式化
x // y , x y 真除法、向下取整除法
-x , +x 取负、取正
~x 按位非(取反码)
x ** y 幂运算(指数)
await x await表达式,类似yeild,接future对象/协程对象
x[i] 索引(序列、映射等)
x[i:j:k] 分片
x(…) 调用(函数、方法、类,其他可调用对象)
x.attr 属性引用
(…) 元组、表达式、生成器表达式
[…] 列表、列表推导
{…} 字典、集合、字典与集合推导

表达式:数字(或其他对象)与运算符相组合
优先级:从上之下,优先级从低到高

取模

好吧,我又错了,本书在表格后面的注解中提到向下取整,并且参阅后面的“除法:经典除法、向下取整除法和真除法”一节。下面的部分可以忽略了!

取模小故事

%符号,在C和Java中是求余,在Python中是取模

-4%-3
Out[1]: -1

4%-3
Out[2]: -2

divmod(-4,-3)
Out[30]: (1, -1)

divmod(4,-3)
Out[31]: (-2, -2)

因为Python选择了向下取整,所以取模就对应的变成了如此形式

第5章 数值类型_第1张图片

Python整除为什么是向下取整:

数学的整除一直有两种方案:向零取整,向下取整。Python之父选择了后者。

以 POSIX 时间戳(1970年以来的秒数)为例,如果我们要获取当前的具体时间,由于每天为 24 * 3600 = 86400 秒,只需直接取模 t % 86400 就可以了。假如时间戳为负数,即在 1970 年之前,那么,为获得正确结果,就只能用向下取整,而不是向零取整。

我能想到的另一个例子是图像点位的计算。而且我很确定,还有许多其它例子。

研究模的意义是什么?

换代数那一套是服务于代数几何的(以及数论,不过数论本来就和抽象代数几何密切相关,比如数域可类比函数域,后者又对应于代数曲线)

所以什么环啊模啊张量积啊都是学一点代数几何才能明白其背后的动机的

最具体的例子,An上的代数函数不就是n元多项式环嘛。但Pn上就只有常数(类比解析情形的刘维尔定理),没什么意思。如果考虑只在局部有定义在其他地方可能有pole的有理函数,就比较有意思了。这就引出空间上的line bundle和divisor的概念。可以用来研究空间的几何性质。而这些对象是定义为sheaf of modules而不是sheaf of rings,比如最简单的Serre’s twisting sheaf是通过把多项式环的分层(grading)给shift一下来构造的,这使得它成为一个分层模而不是环。

然后就是数学家最喜欢做的一般化了。比如多项式环推广到一般环就对应于affine scheme。而一般的scheme就是局部体现为affine scheme的几何对象,类似于流形局部体现为欧式空间。而scheme上的sheaf of finitely generated modules (coherent sheaf)可以看成vector bundle的推广等等。一般的情形下要理解这些对象就需要交换代数。当然你要问这种一般化有用吗?听说数论里是有用的。但再深的我就不懂了

学反码、补码、原码,取模,取余

“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的
余数。任何有模的计量器,均可化减法为加法运算。
例如: 假设当前时针指向10点,而准确时间是6点,调整时间可有以下两种拨法:
一种是倒拨4小时,即:10-4=6
另一种是顺拨8小时:10+8=12+6=6
在以12模的系统中,加8和减4效果是一样的,因此凡是减4运算,都可以用加8来代替。
对“模”而言,8和4互为补数。实际上以12模的系统中,11和1,10和2,9和3,7和5,6和6都有这个特
性。共同的特点是两者相加等于模。

推导式

  • [...] 语法同时用于列表字面量、列表推到表达式。
    列表推导表达式会执行隐式的循环,并把表达式的结果收集到一个新列表中。(an implied loop)

隐式,隐含的、心照不宣的,这指的是解释器有自己的一系列方法去执行表达式
隐式编程
隐式(tacit)编程,也叫做无点(point-free)样式,是其中函数定义不标示所要操作的参数(或称“点”)一种编程范型。转而函数定义只是其他函数的复合,比如那些操纵参数的组合子。隐式编程有着理论价值,因为严格的使用复合导致程序非常适配于方程式推理[1]。它也是特定编程语言的自然样式,包括APL及其派生者[2],和串接语言比如Forth。将参数缺席称为“point-free”导致了不必要的晦涩,故而有了别名为“pointless”[1]。
Python基础教程第三版290p:
NumPy的主要优点是速度快。对数字数组中的每个元素执行很多常见操作时,速度都比使用列表和for循环执行同样的操作快得多,这是因为隐式循环是直接使用C语言实现的。

  • (...) 语法用于元组和表达式分组,以及生成器表达式。
    生成器是一种能够按需产生结果的列表推导,而不是一次性创建完整的结果列表。
    以上用法中,圆括号有时候可以省略

  • {...} 语法用于字典字面量、集合字面量,以及字典和集合的推到。

yield

PEP 255 – Simple Generators
yield会在生成器中返回send(…)参数。
如果yield不是单独的位于一条赋值语句的右边,需要用圆括号。

In [5]: def y():
   ...:     for i in range(10):
   ...:         yield i
   ...:

In [6]: next(y)
TypeError: 'function' object is not an iterator

In [7]: next(y())
Out[7]: 0

In [8]: next(y())
Out[8]: 0

In [9]: next(y())
Out[9]: 0

In [10]: y=y()

In [11]: next(y)
Out[11]: 0

In [12]: next(y)
Out[12]: 1

In [13]: next(y)
Out[13]: 2

三元 if/else 选择表达式

三元表达式是一个多行if语句的简化形式

In [14]: %%timeit
    ...: 0 if 0 else 1
    ...:
    ...:
39.7 ns ± 0.129 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [15]: %%timeit
    ...: 1 if 1 else 0
    ...:
    ...:
26.9 ns ± 0.105 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [16]: %%timeit
    ...: if 0:
    ...:     0
    ...: else:
    ...:     1
    ...:
14.9 ns ± 0.0456 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

In [17]: %%timeit
    ...: if 1:
    ...:     1
    ...: else:
    ...:     0
    ...:
14.9 ns ± 0.0736 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

并未提升效率,但是适用一些只需要一条语句的场合

In [19]: [i for i in range(10) if i%2==0]
Out[19]: [0, 2, 4, 6, 8]

In [20]: %%timeit
    ...: [i for i in range(10) if i%2==0]
    ...:
    ...:
1.29 μs ± 15.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [25]: %%timeit
    ...: l=[]
    ...: for i in range(10):
    ...:     if i%2==0:l.append(i)
    ...:
1.34 μs ± 6.18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [21]: def f(i):
    ...:     if i%2==0:return i
    ...:

In [22]: [f(i) for i in range(10)]
Out[22]: [0, None, 2, None, 4, None, 6, None, 8, None]

这里因为推导式的效率高,是的语句块没能超过推导式和三元表达式的复合体

比较运算符

笔交运算符可以链式实用:X

In [31]: 1<2>0
Out[31]: True

In [33]: 1<2<3<4
Out[33]: True

1>2 and 2>3 # 54.8 ns
1>2>3 # 66.7 ns
1<2 and 2<3 # 77.7 ns
1<2<3 # 84.2 ns

Python 2.X中,允许比较混合类型的相对大小。
Python 3.X中,不允许,排序也会引发异常。
Python 3.X中,字典的相对大小比较也不再支持,尽管支持等价性测试

In [72]: (1,2,'7')<(2,3,4)
Out[72]: True

In [73]: ('1',2,'7')<(2,3,4)
TypeError: '<' not supported between instances of 'str' and 'int'

In [74]: (1,'s')<(1,2)
TypeError: '<' not supported between instances of 'str' and 'int'

In [75]: (1,'s')<(1,'2')
Out[75]: False

TypeError: '>' not supported between instances of 'dict' and 'dict'

分片

新版的Python中,分片表达式X[I:J:K]等同于用一个分片对象进行索引:X[slice(I,J,K)]

In [11]: range(10)[3]
Out[11]: 3

In [12]: range(10)[slice(1,8,3)]
Out[12]: range(1, 8, 3)

In [13]: range(10)[1:8:3]
Out[13]: range(1, 8, 3)

In [14]: list(range(10))[1:8:3]
Out[14]: [1, 4, 7]

In [15]: import mem

In [16]: mem.data(slice(1,8,3))
30 69 d8 26 ff 01 00 00 10 6a d8 26 ff 01 00 00
70 69 d8 26 ff 01 00 00 70 ef a4 c7 fd 7f 00 00
00 00 00 00 00 00 00 00

In [17]: mem.data(range(1,8,3))
30 69 d8 26 ff 01 00 00 10 6a d8 26 ff 01 00 00
70 69 d8 26 ff 01 00 00 70 69 d8 26 ff 01 00 00

slice的第四组字节,指向的内容:
40 53 55 56 57 41 54 41 55 41 56 41 57 48 81 EC @SUVWATAUAVAWH?ì
这个东西很常见,但我仍未知道到底是什么意思!可能就是一个终止符号
既,slice的实际内存内容只有三个,而相对的range有四个,最后一个是迭代元素的数量

lambda args: expression : λ参数:表达式

a=1

args=(a,'b')

print(*args)
1 b

d={
     'a':ord('a'),'b':ord('b')}

d
Out[52]: {
     'a': 97, 'b': 98}

list(filter(lambda x:d[x],d))
Out[61]: ['a', 'b']

list(map(lambda x:d[x],d))
Out[64]: [97, 98]

args
Out[87]: (1, 'b')

def f(a,b):
    b=b+' '

f(*args)

字典中,args的意思是数值,而在这里,…,仍是数值,而非变量,虽然写的时候,确实可以输入变量名,但是实际传输的是数值内容的地址。上面从args创建,到f函数内,b被表达式赋值之前,相关的数值内容,在内存中仅有一处,全局变量、局部变量都在指向他,做的极端点的,args的其中一个数值是’ '*2**20*100,即 100MB,观察内存占用的变化用以确定内存、数值、地址、变量的关系。

or和and的截断效应

0 or 1 # 41.4 ns
1 and 1 # 41.2 ns
1 or 1 # 36.1 ns
0 and 1 # 36.3 ns
if 1:1 # 15.3 ns
if 0:
    pass
else:
    1
15.7 ns

10 if 0 else 11
Out[79]: 11

10 if 0 else 11 # 40.4 ns
10 if 1 else 11 # 28.1 ns

虽然or和and有截断功能,但是实际上效率并不高,所以不建议使用此功能截断。

而推导式的效率高,不是因为缩写的效率高,而是因为有另外一套实现方法。

混合运算遵循运算符优先级

与大多数编程语言一样,在Python中,你可以串联使用以上的运算符表达式来编写更复杂的表达式:
AB+CD
当编写含有一个运算符以上的表达式时,Python将按照优先级法则对表达式进行分组,这个分组决定表达式中各各部分的计算顺序。
表中同一行、同一级优先级在分组的时候,通常按照从左到右组合(除了幂运算,它是从右向左组合;比较运算,从左到右链接)。

A*B+ => (A*B)+

+C*D => +(C*D)

运算顺序:1、AB;2、CD;3、+

已优先级对上表进行重编辑

运算符 描述
yield 函数语句
:= 赋值表达式
lambda 表达式内匿名函数
x if y else z 三元表达式
x or y 逻辑或
x and y 逻辑与
not x 逻辑非
x in y , x not in y
x is y , x is not y
xy , x>=y , x==y , x!=y
成员关系,y是容器,x不能是表达式
对象同一性测试,x、y不能是表达式
大小比较、集合的子集和超集,值等价性运算符
x|y 按位或、集合并集
x^y 按位异或、集合对称差集
x&y 按位与、集合交集
x<>y 将x左移或右移y位
x+y , x-y 加法、拼接,减法、集合差集
x*y , x%y , x/y , x//y 乘法、重复;求余、取模、格式化;真除法;向下取整除法
-x , +x , ~x 取负、取正、取反(按位非,取反码)(一元表达式)
x**y 幂运算(指数)
await x await表达式
x[i] , x[i:j:k] , x(…) , x.attr 索引;分片;调用;属性引用
(…) , […] , {…} 括号、推导式、字面量

总的来说,一般情况下,幂运算有最高的优先级,其次是一元表达式,乘除,加减,位,比较,逻辑,三元表达式,函数
而容器是特殊情况,容器也不能直接拿来运算,除了拼接和重复,容器类似于一元表达式。而拼接和重复类似于二元表达式
一元表达式:一般情况下,若一元表达式非优先级,整个式子都无法解析
例如 1 or not 1,或是not 1 or 1,其实可以理解为or和not是同级优先级的,但是就是必须给not一个安居之所
所以not、+、-、~等一元表达式下,必须看作和后面的数值是一体的
is和in与比较运算符是同一级别的,但注意使用场合的意义
其实最不习惯的就是很少用到的位运算,在Python中,每一个位运算不与其他位运算同一优先级:位移、与、异或、或

复合表达式的操作流程:
1、遍历操作符,确定优先级(操作单元)
2、从左到右依次运算

In [22]: 3 if 1 and 0 else 6 or 5
Out[22]: 6

1 or 0 and 0
Out[1]: 1

(1 or 0) and 0
Out[2]: 0
# 由此确定,and优先级大于or

In [1]: l=[]

In [2]: def f1():
   ...:     l.append(1)
   ...:     return 1
   ...:

In [3]: def f0():
   ...:     l.append(0)
   ...:     return 0
   ...:

In [4]: f1() or f0() and f0()
Out[4]: 1

In [5]: l
Out[5]: [1]
# 细品,为什么and优先级大于or的时候,只运算了f1()

In [6]: l=[]
   ...: f1() and f0() or f1() and f1()
Out[6]: 1

In [7]: l
Out[7]: [1, 0, 1, 1]

In [8]: l=[]
   ...: f1() and f0() or f0() and f1()
Out[8]: 0

In [9]: l
Out[9]: [1, 0, 0]
# 细品一下,or和and的优先级是怎样的,解释器是如何运算的

In [82]: %%timeit
    ...: 1 or 0 and 2 or 3
    ...:
    ...:
35.8 ns ± 0.208 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [83]: %%timeit
    ...: 0 and 1 and 2 or 1
    ...:
    ...:
40.5 ns ± 0.345 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [59]: 1 in [1] or 0
Out[59]: True

In [60]: 0 or 1 in [1]
Out[60]: True

In [27]: 1 and 0 or 0 or 1
Out[27]: 1

In [28]: 1 and 0 or 0 or 0
Out[28]: 0

In [29]: 1 and 0 or 0 or 0<1
Out[29]: True

In [71]: not 1+1
Out[71]: False

In [72]: not 1-1
Out[72]: True

+以下括号的问题,后面小节的链式比较+
In [33]: 1 in (1,) == True
Out[33]: False

In [14]: 1 in (1,) == (1,)
Out[14]: True

In [15]: 1 in (1,) == [1]
Out[15]: False

In [16]: 1 in (1,) == tuple([1])
Out[16]: True

In [34]: (1 in (1,)) == True
Out[34]: True

In [35]: (1 in (1,)) is True
Out[35]: True

In [36]: 1 in (1,) is True
SyntaxWarning: "is" with a literal. Did you mean "=="?
Out[36]: False

In [42]: True is 1<2
SyntaxWarning: "is" with a literal. Did you mean "=="?
Out[42]: False

In [43]: True is 1>2
SyntaxWarning: "is" with a literal. Did you mean "=="?
Out[43]: False

In [17]: 1 in (1,) is (1,)
SyntaxWarning: "is" with a literal. Did you mean "=="?
Out[17]: True

In [44]: True is (1<2)
Out[44]: True

In [18]: a=1000

In [19]: b=999+1

In [20]: a==b
Out[20]: True

In [21]: a is b
Out[21]: False

In [22]: 1000 in (a,) == (b,)
Out[22]: True

In [23]: 1000 in (a,) is (b,)
Out[23]: False

In [47]: 1<2 in (False,)
Out[47]: False

In [48]: 1>2 in (False,)
Out[48]: False

In [49]: (1>2) in (False,)
Out[49]: True

In [50]: (1>2) in (1>2,)
Out[50]: True

In [51]: (1>2) in (1>3,)
Out[51]: True

In [52]: (1>2) in (1<3,)
Out[52]: False

In [56]: (1>2) is 1>2
1: SyntaxWarning: "is" with a literal. Did you mean "=="?
  (1>2) is 1>2
Out[56]: False

In [57]: (1>2) is (1>2)
Out[57]: True

In [73]: 1>1==0
Out[73]: False

In [74]: (1>1)==0
Out[74]: True

In [75]: 1>(1==0)
Out[75]: True

In [76]: 1>1!=0
Out[76]: False

++++++++++++++++++++++++++++

In [38]: not 0 > 0
Out[38]: True

In [39]: not 0 > -1
Out[39]: False

In [40]: (not 0)>0
Out[40]: True

In [20]: 0 or not -1
Out[20]: False

In [80]: 0 or not 0
Out[80]: True

In [82]: 1|1==1
Out[82]: True

In [85]: 1==1|1
Out[85]: True

In [88]: 1|2^3
Out[88]: 1

In [90]: (1|2)^3
Out[90]: 0

In [100]: 4^1&3
Out[100]: 5

In [101]: (4^1)&3
Out[101]: 1

In [105]: 2&1<<1
Out[105]: 2

In [55]: 5&3
Out[55]: 1

In [56]: 5&3<<2
Out[56]: 4

In [106]: (2&1)<<1
Out[106]: 0

In [41]: 1<<1
Out[41]: 2

In [42]: 5+1<<1
Out[42]: 12

In [43]: 5+1<<1+1
Out[43]: 24

In [112]: 0.3+0.4-0.6
Out[112]: 0.09999999999999998

In [113]: 0.3+(0.4-0.6)
Out[113]: 0.10000000000000003
(1.5).as_integer_ratio()
Out[69]: (3, 2)

1.5.as_integer_ratio()
Out[70]: (3, 2)

float.hex(1.5)
Out[71]: '0x1.8000000000000p+0'

1.5.hex()
Out[72]: '0x1.8000000000000p+0'

int.as_integer_ratio(1) # 144 ns
Out[76]: (1, 1)

1.as_integer_ratio()
SyntaxError: invalid syntax

(1).as_integer_ratio() # 92.7 ns
Out[78]: (1, 1)

0b1.as_integer_ratio()
Out[81]: (1, 1)

'香'.encode()
Out[83]: b'\xe9\xa6\x99'

为什么1.5可以而1不可以,其实1.5在这里算是表达式而非字面量,是生成浮点数的表达式,很明显浮点数的真实值与输入的原始值是不一样的,而字符串也可以直接使用,偏偏int不行,需要加括号。而以二进制等输入的,也算是一种转换成整数的表达式

括号分组子表达式

如果用圆括号将表达式各部分进行分组,就可以不必记忆优先级的事情了。
当你将子表达式括在圆括号中时,就会超越Python的优先级规则。
Python总会先行计算圆括号中的表达式,然后再将结果用于整个表达式中。

(1+1)*1 # 14.8 ns
1+(1*1) # 15 ns
1+1*1 # 15.1 ns

在大性表达式中,添加括号是个很好的做法,它不仅能强指按照你想要的顺序进行计算,也增加了程序的可读性。

混合类型向上转换

出了在表达式中混合运算符以外,你也可以混合数值类型。
例如你可以把一个整数与一个浮点数相加:40+3.14
在混合类型的表达式中,Python首先将备操作的对象转换成其中最复杂的操作数的类型,然后再对相同类型的操作数进行数学运算。这点和C类似!
整数比浮点数简单,浮点数比复数简单。
这里推荐一个高效的整数转浮点数,直观高效的方法:int/1

你也可以手动调用内置函数强制转换类型:

In [20]: int(3.1425)
Out[20]: 3

In [21]: float(3)
Out[21]: 3.0

然而你通常不需要这样做,因为Python的自动升级成更复杂的类型,其结果往往就是你所想要的。

这些混合类型转换仅适用于数值类型的混合,这包括那些使用数字和比较运算符的表达式。

In [6]: 100000000000000000000000000000000000000000+0.1
Out[6]: 1e+41

In [8]: 100000000000000000000000000000000000000000+0.1==100000000000000000000000000000000000000000
Out[8]: False

In [9]: 100000000000000000000000000000000000000000+0.1==100000000000000000000000000000000000000000.0
Out[9]: True

In [11]: 100000000000000000000000000000000000000000+0.1==float(100000000000000000000000000000000000000000)
Out[11]: True

In [12]: 100000000000000000000000000000000000000000.0==100000000000000000000000000000000000000000
Out[12]: False

In [10]: 1==1.0
Out[10]: True

In [13]: 1000==1000.0
Out[13]: True

In [14]: '3.14'==str(3.14)
Out[14]: True

In [15]:  3.14
Out[15]: 3.14

In [16]: repr(3.14)
Out[16]: '3.14'

In [17]: '%.10f'%3.14
Out[17]: '3.1400000000'

In [18]: '%.30f'%3.14
Out[18]: '3.140000000000000124344978758018'

In [19]: '%.30f'%1e+41
Out[19]: '100000000000000000620008645040778319495168.000000000000000000000000000000'

这里整数、浮点数、和字符串的比较结果不一样

预习:运算符重载和多态

所有的Python运算符都可以被Python的类或C扩展类型所重载(即实现),从而能用于你自己创造的对象。
重载:可以理解为重新载入,调用等。
例如x+y表达式,可以使加法,也可以是拼接,会根据处理的对象类型而进行不同的操作。
实际上,“+”用在你自己定义的类的对象上时,可以进行你想要的任意操作。

这种特性称为多态:操作的意义由操作对象来决定。第16章会深入探索。

数字的实际应用

变量与基础表达式

  • 变量在第一次赋值时被创建
  • 变量在表达式中使用时,会被替换成它们的值
  • 变量在表达式中使用之前,必须已被赋值(创建)
  • 变量引用对象,而且从不需要事先声明???
a=3 #变量创建,不需要提前声明
b=4

在Python中,#是注释符号,自他开始到行末,会被Python忽略掉,注释是为人类便携的可读的文档,不是运行语句的组成部分。
之后还会介绍更有用的特性——文档字符串。
文档字符串可以加载的时候附加到对象上,而不是被Python完全忽略,之后可以使用help等方式直接显示文档字符串而不需要去翻源代码。

In [23]: def f():
    ...:     '这是一个自定义的函数,但这里只用来演示这个文档字符串'
    ...:

In [24]: f?
Signature: f()
Docstring: 这是一个自定义的函数,但这里只用来演示这个文档字符串
File:      c:\users\jone\appdata\local\programs\python\python39\scripts\<ipython-input-23-45658de7362a>
Type:      function

In [25]: help(f)
Help on function f in module __main__:

f()
    这是一个自定义的函数,但这里只用来演示这个文档字符串

现在让我们在一些表大市中使用新的整数对象,当变量被用在表达式中,他们就会被替换成它们的值:

In [30]: a+1,a-1
Out[30]: (4, 2)

In [31]: b*3,b/2
Out[31]: (12, 2.0)

In [33]: a%2,b**2
Out[33]: (1, 16)

In [34]: 2+4.0,2.0**b #混合类型转换
Out[34]: (6.0, 16.0)

In [1]: n,m=1,2

In [2]: n,m=(1,2)

In [3]: n
Out[3]: 1

In [4]: m
Out[4]: 2

In [6]: c*2
NameError: name 'c' is not defined

这里显示的结果是包含两个值的元组,因为在输入行上包含了两个被逗号分开的表达式

你不需要再Python中提前声明变量,但变量在使用之前必须至少被赋值一次。
实际上,你在累加计数器之前必须先将他们赋值为0。
你在循环比较中需要将存储变量初始化为一个与规则和第一个数有关的数,或者float(‘inf’)之类
将一系列数据整理为列标之前,初始化一个空列表。

In [10]: f=float('inf')

In [11]: f
Out[11]: inf

In [12]: 1-inf
NameError: name 'inf' is not defined

In [13]: 1>f
Out[13]: False

In [14]: 1<f
Out[14]: True

In [15]: f=float('nan')

In [16]: 1>f
Out[16]: False

In [17]: 1<f
Out[17]: False

In [20]: 1-f
Out[20]: nan

In [21]: 1-float('inf')
Out[21]: -inf

In [22]: 1>None
TypeError: '>' not supported between instances of 'int' and 'NoneType'
In [24]: b/2+a
Out[24]: 5.0

In [25]: b/(2+a)
Out[25]: 0.8

In [33]: (100000000000000000000000000000000000000000000000+2.0)//2
Out[33]: 5e+46

In [34]: int((100000000000000000000000000000000000000000000000+2.0)//2)
Out[34]: 50000000000000002192292152253809867731702382592

In [35]: (100000000000000000000000000000000000000000000000+2)//2
Out[35]: 50000000000000000000000000000000000000000000001

In [46]: 100000000000000000//2+1.0
Out[46]: 5e+16

In [47]: 10000000000000000//2+1.0
Out[47]: 5000000000000001.0

In [49]: decimal.Decimal(10000000000000000//2+1.0)
Out[49]: Decimal('5000000000000001')

In [50]: decimal.Decimal(100000000000000000//2+1.0)
Out[50]: Decimal('50000000000000000')

In [51]: decimal.Decimal(5e+16)
Out[51]: Decimal('50000000000000000')

In [52]: '%.20f'%5e+16
Out[52]: '50000000000000000.00000000000000000000'

In [53]: decimal.Decimal(5e+46)
Out[53]: Decimal('50000000000000002192292152253809867731702382592')

这里展现了括号强制优先级、混合运算的转换等操作。

In [87]: f
Out[87]: 1.7976931348623157e+308

In [88]: mem.data(f)
ff ff ff ff ff ff ef 7f

In [89]: decimal.Decimal(f)
Out[89]: Decimal('179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368')

In [90]: int(f)
Out[90]: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

In [91]: i=int(f)

In [92]: (i+2)//2==i//2+1
Out[92]: True

In [93]: (i+2.0)//2==i//2+1
Out[93]: False

In [94]: (i+2.0)//2==i//2+1.0
Out[94]: True

In [95]: (i+2)//2==i//2+1.0
Out[95]: False

In [96]: (i+2)//2
Out[96]: 89884656743115785407263711865852178399035283762922498299458738401578630390014269380294779316383439085770229476757191232117160663444732091384233773351768758493024955288275641038122745045194664472037934254227566971152291618451611474082904279666061674137398913102072361584369088590459649940625202013092062429185

In [97]: decimal.Decimal((i+2.0)//2)
Out[97]: Decimal('89884656743115785407263711865852178399035283762922498299458738401578630390014269380294779316383439085770229476757191232117160663444732091384233773351768758493024955288275641038122745045194664472037934254227566971152291618451611474082904279666061674137398913102072361584369088590459649940625202013092062429184')

In [98]: (i+2)//2==i//2+1.0
Out[98]: False

In [99]: (i+2)//2-1==i//2+1.0
Out[99]: True

扩展,最大浮点数,浮点的精度缺失性

数值的显示格式

f=3.14

f
Out[10]: 3.14

str(f)
Out[11]: '3.14'

repr(f)
Out[12]: '3.14'

print('%.10f'%f)
3.1400000000

print('%.20f'%f)
3.14000000000000012434

print('%.30f'%f)
3.140000000000000124344978758018

print('%.50f'%f)
3.14000000000000012434497875801753252744674682617188

print('%.100f'%f)
3.1400000000000001243449787580175325274467468261718750000000000000000000000000000000000000000000000000

import decimal

decimal.Decimal(3.14)
Out[19]: Decimal('3.140000000000000124344978758017532527446746826171875')
>>> b/(2.0+a)
0.80000000000000004
# 书中示例
In [2]: b/(2.0+a)
Out[2]: 0.8
# 实际

这个是Python解释器的默认变量/表达式显示效果,看来每个版本都不一定一样

除了print会自动显示之外,还有多种方式能显示计算机中数字的位数

In [5]: num=1/3.0

In [6]: num
Out[6]: 0.3333333333333333

In [7]: print(num)
0.3333333333333333

In [8]: '%e'%num
Out[8]: '3.333333e-01'

In [10]: '%4.2f'%num
Out[10]: '0.33'

In [11]: '{0:4.2f}'.format(num)
Out[11]: '0.33'

这里使用了格式化字符串的方法,将在第七章介绍。

str和repr显示格式

In [12]: n='\n'

In [13]: str(n)
Out[13]: '\n'

In [14]: repr(n)
Out[14]: "'\\n'"

In [15]: i=11

In [16]: repr(i)
Out[16]: '11'

In [17]: str(i)
Out[17]: '11'

In [18]: t=type(i)

In [19]: t
Out[19]: int

In [20]: str(t)
Out[20]: ""

In [21]: repr(t)
Out[21]: ""

In [22]: str(b'xy','utf8')
Out[22]: 'xy'

In [25]: len(dir(str))
Out[25]: 80

In [26]: len(dir(repr))
Out[26]: 29

In [27]: type(str)
Out[27]: type

In [28]: type(repr)
Out[28]: builtin_function_or_method

In [30]: repr??
Signature: repr(obj, /)
Docstring:
Return the canonical string representation of the object.

For many object types, including most builtins, eval(repr(obj)) == obj.
Type:      builtin_function_or_method

In [31]: str??
Init signature: str(self, /, *args, **kwargs)
Docstring:
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.
Type:           type
Subclasses:     DeferredConfigString, _rstr, LSString, include, SortKey

str和repr都会把任意对象转换成对应的字符串表示:
repr以及默认的交互式命令行显示,会产生看起来像代码的结果
str和print转换为一种对用户更加友好的格式
对象同时拥有这两种方式:str用于一般用途,repr带有额外细节
str作为核心类型,拥有更多的方法,而本身也有多个方法,而repr只是一个单一功能的函数。

这里大家自己尝试下str(True)、repr(True),至于为什么,本章后面的布尔类型会提到

普通比较与链式比较

In [1]: 1<2
Out[1]: True

In [2]: 2.0>1
Out[2]: True

In [3]: 2==2.0
Out[3]: True

In [5]: 1<'1'
TypeError: '<' not supported between instances of 'int' and 'str'

In [6]: 1==1+1j
Out[6]: False

In [7]: 1<1+1j
TypeError: '<' not supported between instances of 'int' and 'complex'

In [9]: 1<decimal.Decimal('2')
Out[9]: True

In [12]: 1<fractions.Fraction(2,1)
Out[12]: True

In [13]: 1+1j<1+2j
TypeError: '<' not supported between instances of 'complex' and 'complex'

比较表达式中,允许数字类型的混合,这里的数字类型仅限于实数(有理数和无理数),不包括复数,甚至复数之间都不能比较大小。

x,y,z=2,4,6
43 ns

x=2;y=4;z=6
43 ns

x=2
y=4
z=6
41.9 ns

随手写了下,这种赋值方法,在行数上更简洁,但比原生写法慢,但这个慢只是相对而言,大家完全可以依照自己的喜好去写,除非团队有规定

In [36]: %%timeit
   ...: x=1
   ...: y=0
   ...: t=x
   ...: x=y
   ...: y=t
   ...:
   ...:
59.2 ns ± 0.102 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [37]: %%timeit
   ...: x=1
   ...: y=0
   ...: x,y=y,x
   ...:
   ...:
55.7 ns ± 0.176 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
# x,y=y,x 是毋庸置疑的快一点,但关键在于省去了t的赋值造成的,是综合的更快而非单纯的更快

%%timeit
t=x;x=y;y=t
UnboundLocalError: local variable 'x' referenced before assignment

%%timeit
x,y=y,x
UnboundLocalError: local variable 'y' referenced before assignment

%%time
x,y=y,x
Wall time: 0 ns

In [58]: a=b=0

In [59]: %%timeit
   ...: a=1+b
   ...:
   ...:
63.9 ns ± 0.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [60]: a
Out[60]: 0

In [61]: %%timeit
   ...: a=1+b
   ...: b=1
UnboundLocalError: local variable 'b' referenced before assignment

In [66]: %%timeit
   ...: b=0
   ...: a=1+b
   ...:
   ...:
59.3 ns ± 0.143 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [74]: %%timeit
   ...: b=0
   ...: a=1+b
   ...: b=1
   ...:
   ...:
65.7 ns ± 0.159 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

timeit执行时,报过错,emmm…
time就没有报错,而且还修改了变量
timeit则不会影响变量
timeit的报错原因应该是产生了歧义,没有报错时,a=1+b,赋值符号=的右侧表达式,调用了全局的b。
timeit报错时,语句块中有b的赋值,而下一次循环,是选择调用又被赋值的b,还是选择全局的b,我认为这是timeit的一个bug!
很多开源项目在github上有页面,那里可以发帖沟通!
In[59]和In[66],后者多了一条语句,反而更加的快,这就是timeit不大合理的机制造成的!

Python允许我们把多个比较链接起来执行范围测试。
链式比较是更大的布尔表达式的简写。
例如,表达式(A 他等同于布尔测试(A

In [1]: X=2
   ...: Y=4
   ...: Z=6

In [2]: X<Y<Z
Out[2]: True

In [3]: X<Y and Y<Z
Out[3]: True

In [4]: X<Y>Z
Out[4]: False

In [5]: X<Y and Y>Z
Out[5]: False

In [6]: import decimal,fractions

In [7]: 1<2.0<decimal.Decimal('3')<fractions.Fraction(4,1)
Out[7]: True

In [8]: 1>2.0>decimal.Decimal('3')>fractions.Fraction(4,1)
Out[8]: False

In [9]: 1==2<3
Out[9]: False

In [62]: (1==2)<3
Out[62]: True

In [10]: 2==2<3
Out[10]: True

In [11]: 1==2 and 2<3
Out[11]: False

In [12]: 2==2 and 2<3
Out[12]: True

1<2 and 2<3 # 81.2 ns
1<2<3 # 89.1 ns

在这里,我获知了==和<是同级的,并回头完善了优先级
意料中的,链式比较是需要额外的消耗的,虽然更直观,是否使用,看自己喜好

In [63]: 1.1+2.2==3.3
Out[63]: False

In [64]: 1.1+2.2
Out[64]: 3.3000000000000003

In [66]: int(1.1+2.2)==int(3.3)
Out[66]: True

In [68]: decimal.Decimal('1.1')+decimal.Decimal('2.2')==decimal.Decimal('3.3')
Out[68]: True

In [69]: 1+0.5+0.25==1.75
Out[69]: True

由于浮点数的精度限制,我们不能确定他们的数值是否相等
但书中提到,之后学到的小数和分数能够避免这些限制,虽然我之前就一直频繁的使用小数和分数来演示了

除法:经典除法、向下取整除法和真除法

In [110]: 10/4
Out[110]: 2.5

In [111]: 10/2
Out[111]: 5.0

In [112]: 10//4
Out[112]: 2

In [113]: 10//4.0
Out[113]: 2.0

/ 真除法,结果是浮点数
// 整除,结果类型与被运算数值类型一样

In [114]: 131//10
Out[114]: 13

In [115]: int(131/10)
Out[115]: 13

In [116]: %%timeit
     ...: 131//10
     ...:
     ...:
15 ns ± 0.0364 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

In [117]: %%timeit
     ...: int(131/10)
     ...:
     ...:
123 ns ± 0.0576 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

曾经有过类似应用场景,目前书中并未提及运算结果是随版本不确定的,所以大家以后遇到这种情况,可以优先使用//,那次应用场景,被运算数不可能是浮点数,如果大家遇到的场景是浮点数的话,需要考虑下浮点数是否对之后的运算有影响!

向下取整除法 vs 截断除法

// 运算符有一个非正式的别名:截断除法,更准确的说法是向下整除法
//把结果向下截断到他的下层,即真正结果之下的最近的整数。其直接效果是向下舍入,并不是严格的阶段,并且这对负数也有效。

In [118]: 2.5//1
Out[118]: 2.0

In [119]: -2.5//1
Out[119]: -3.0

In [120]: import math

In [121]: math.floor(2.5)
Out[121]: 2

In [122]: math.floor(-2.5)
Out[122]: -3

In [123]: math.trunc(-2.5)
Out[123]: -2

In [124]: math.trunc(2.5)
Out[124]: 2

In [125]: math.ceil(2.5)
Out[125]: 3

In [126]: math.ceil(-2.5)
Out[126]: -2

int(2.5)
Out[86]: 2

int(-2.5)
Out[87]: -2

math.floor(2.5) # 109 ns
math.trunc(2.5) # 111 ns
int(2.5) # 118 ns

ceil:天花板,向上取整,向正无穷取整
floor:地板,向下取整,向负无穷取整
trunc:截断
在截断float时,math.trunc()与int()同效,int不需要导入。

In [133]: 5/2,5/-2
Out[133]: (2.5, -2.5)

In [134]: 5//2,5//-2
Out[134]: (2, -3)

In [135]: 5/2.0,5/-2.0
Out[135]: (2.5, -2.5)

In [136]: 5//2.0,5//-2.0
Out[136]: (2.0, -3.0)

-5,-2结果与5,2一样,-5,2结果与5,-2结果一样。

为什么截断很重要

除了上面的//实例,大家很容易接触到需要截断的场景。书中提到了Python 2.X与3.X的区别,这里不详说了,//是2.X就有的

整数精度

In [4]: mem.data(-1)
ff ff ff ff ff ff ff ff 01 00 00 00

In [5]: mem.data(0)
00 00 00 00 00 00 00 00

In [6]: mem.data(1)
01 00 00 00 00 00 00 00 01 00 00 00

In [7]: mem.data(2**30-1)
01 00 00 00 00 00 00 00 ff ff ff 3f

In [8]: mem.data(2**30)
02 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00

In [9]: mem.data(-2**30)
fe ff ff ff ff ff ff ff 00 00 00 00 01 00 00 00

In [10]: 2**200
Out[10]: 1606938044258990275541962092341162602522202993782792835301376

In [11]: mem.data(2**200)
07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 10 00

详情请见上一章我对整数结构的分析。
块数+符号+数值,4字节递增,但前两比特不使用。
无限精度整数是一个方便的内置工具,例如可以再Python中以美分的单位来计算美国国家财政赤字。

由于Python需要为支持扩展精度而进行额外的工作,因此在实际应用中,长整型的数学运算通常比正常的整数运算更慢。
然而,如果你确实要求精度,更应该庆幸Python为你提供了内置的长整数支持,而不是抱怨其性能上的不足。

复数

复数比其他的类型少见一些,但复数是一种独特的核心对象类型。
它们通常在工程和科学应用程序中使用。如果你知道复数是什么,你就会知道她的重要性,如果不知道的话,那么请将此当作选读。

复数表示为两个浮点数(实部和虚部),并在虚部增加j的后缀。
可以把实部非零的复数写成实部与虚部相加的形式,并以+相连。
当虚部为负时,可以用-相连。

In [2]: mem.data(1+1j)
00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 f0 3f
# 无论如何显示,存储时是浮点型

In [13]: 1-1j
Out[13]: (1-1j)

In [14]: 1+-1j
Out[14]: (1-1j)

In [15]: -3j
Out[15]: (-0-3j)

In [16]: 3j
Out[16]: 3j

In [19]: 1j*1J
Out[19]: (-1+0j)

In [20]: 2+1j*3
Out[20]: (2+3j)

In [21]: (2+1j)*3
Out[21]: (6+3j)

In [25]: i.conjugate()
Out[25]: (1-1j)

In [26]: i.imag
Out[26]: 1.0

In [27]: i.real
Out[27]: 1.0

复数允许我们将她的实部和虚部作为属性来访问,并支持所有一般的数学表达式。
同时还可以通过标准的cmath模块(复数版的math模块)中的工具进行处理。
复数在绝大多数编程领域都较为罕见,因此这里跳过复数的其他内容。详情请参阅文档。

十六进制、八进制和二进制:字面量与转换

Python的整数能以十六进制、八进制和二进制记数法来编写,作为对十进制记数法的补充。
对于十指生物而言,其他三种进制第一眼看上去充满异域风情,但是他们能很容易的对应到字节和位。

八进制(基数为8)表示法在早期的计算机系统中很常见,bai因此,偶尔我们还能看到人们使用八进制表示法。八进制适用于12位和36位计算机系统(或者其他位数为3的倍数的计算机系统)。但是,对于位数为二的幂(8位,16位,32位与64位计算机系统)的计算机系统来说,八进制就不算很好了。因此,在过去几十年里,八进制渐渐地淡出了。不过,还是有一些程序设计语言提供了使用八进制符号来表示数字的能力,而且还是有一些比较古老的unix应用在使用八进制。

0o1,0o20,0o377
Out[1]: (1, 16, 255)

0x01,0x10,0xff
Out[2]: (1, 16, 255)

0b1,0b10000,0b11111111
Out[3]: (1, 16, 255)

注意,这里只是输入用的字面量,十进制的输入的替代方式,但是在内存中,都是(十进制)整数型

0xFF,(15*(16**1))+15*16**0
Out[4]: (255, 255)

0X2F,2*16**1+15*16**0
Out[6]: (47, 47)

0xf,0b1111,1*2**3+1*2**2+1*2**1+1*2**0
Out[7]: (15, 15, 15)

16进制:0-9a-f即0-15
2**10,有十个0,共11位
当使用者想看到某内存基数态的数据时,用十六进制会很有用

这里原文是bases’ digit strings、the numeric base、base-specific strings,我数学不好,不知道底数用在什么地方,但是我更习惯称之为基数,很容易理解,而底数,联想到的是幂运算的底数
我经常使用2**20*100,来表示100MB,用来生成100MB的字符串进而做更多的操作,观察内存的变化!之前聊为什么使用1024时,2**10即1024,十指兽的程序员喜欢2**10,工业大佬喜欢1024这个靠近1000的数,所以用了他!我也可以用2**10来表示KB,2**20表示MB,以此类推!
而书中这段,我很少用,但需要的时候也会用,例如我通常会直接用2**30来验证整数类型,而不是0x100000000或是0b1000000000000000000000000000000,但遇到需要的情况时,还是很好用的,例如0x233621,然后我通过内存查找来快速定位。

oct(64),hex(64),bin(64)
Out[1]: ('0o100', '0x40', '0b1000000')

64,0o100,0x40,0b1000000
Out[2]: (64, 64, 64, 64)

int('64'),int('100',8),int('40',16),int('1000000',2)
Out[3]: (64, 64, 64, 64)

int('0x40',16),int('0b1000000',2)
Out[4]: (64, 64)

int('0xff',0),int('0xff',16),int('ff',16)
Out[17]: (255, 255, 255)

输入时,不加引号的0o1会转换成整数类型保存,输出是十进制,那么输出是二进制的方法就是bin(),将数字转换成对应进制的字符串。而int拥有将其它类型转换成整数类型的功能,包括字符串,此时带第二个参数i是数字的话2<=i<=36 or 0,会将字符串以对应进制进行转换,0即自适应。若带0b等进制标识符,可以使用0或对应进制数字,无法使用不带或使用其它数字。会报错或者非标识计算:

int('0xff')
ValueError: invalid literal for int() with base 10: '0xff'

int('0xff',2)
ValueError: invalid literal for int() with base 2: '0xff'

int('0xff',36)
Out[3]: 43323

int('xff',36)
Out[4]: 43323

int('xff',16)
ValueError: invalid literal for int() with base 16: 'xff'

第三行语句,是将0x也当作数字而非标识计算了

eval('64'),eval('0o100'),eval('0x40'),eval('0b1000000')
Out[7]: (64, 64, 64, 64)

0x11 # 15.6 ns
eval('0x11') # 12.9 μs
e=compile('0x11','','eval') # 11.1 μs
eval(e) # 486 ns

eval函数,将括号内的字符串当作语句执行,若括号内是表达式,则返回其结果。
eval、exec,以及配套的compile函数。
有网友说,用exec就是错,很多时候exec都有更好的替代方案。
eval好一些,但是两者同样都不安全,除非其字符串是可控的,万一是来自第三方,可能会执行很危险的操作。

分析compile,作者在第二篇文章更正,是一般人用不到亿次级别的正则等,compile涉及的功能,所以没必要去写,而专业的去写,也建议一开始不使用compile,在后期优化的时候加上…

也可以用字符串格式化方法调用,将整数转换为指定基数/进制的字符串

'{0:o},{1:x},{2:b}'.format(64,64,64)
Out[15]: '100,40,1000000'

'%o,%x,%x,%X'%(64,64,255,255)
Out[16]: '100,40,ff,FF'

'%(1)o,%(2)x,%(3)x,%(4)X'%{
     '1':64,'2':64,'3':255,'4':255}
Out[24]: '100,40,ff,FF'

'%(1)#o,%(2)#x,%(3)#x,%(4)#X'%{
     '1':64,'2':64,'3':255,'4':255}
Out[30]: '0o100,0x40,0xff,0XFF'

'{0:#o},{1:#x},{2:#b}'.format(64,64,64)
Out[31]: '0o100,0x40,0b1000000'

'{0:^+#9x}'.format(255)
Out[149]: '  +0xff  '

format的使用:官方文档
因为一长串的附加内容,找了一圈,在官方文档找到合适的内容
填充、定位、符号、形式、宽度、类型
填充:字符
定位:< 左对齐 > 右对齐 = 两端对齐 ^ 居中对齐
符号:+ - space
形式:# 体换形式
宽度:整数
以上是与这次进制相关的正确排序,更多内容查看官方文档。
另,复数是浮点类型,可以使用f或g

'{0:f}'.format(1+1j)
Out[156]: '1.000000+1.000000j'

'{0:g}'.format(1+1j)
Out[157]: '1+1j'

字符串格式化将在第七章中详细介绍

In [1]: X=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

In [2]: X
Out[2]: 374144419156711147060143317175368453031918731001855

In [3]: oct(X)
Out[3]: '0o77777777777777777777777777777777777777777777777777777777'

In [4]: bin(X)
Out[4]: '0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'

In [6]: mem.data(0x233621)
01 00 00 00 00 00 00 00 21 36 23 00

In [7]: mem.data(0xffffffff)
02 00 00 00 00 00 00 00 ff ff ff 3f 03 00 00 00

嗯,之前我提到过用0x233621做标记,但不要忽略Python的无限精度整数

按位操作

除了一般的数学运算,Python也支持C语言中大多数的数学表达式,这包括那些把整数作为二进制位串处理的运算。
如果你的Python代码必须处理像网络数据包、串行端口或C程序产生的打包二进制数据的这些内容,就会十分有用。

x=1

x<<2
Out[2]: 4

x|2
Out[3]: 3

x&3
Out[4]: 1

x^3
Out[8]: 2

0xff^0o77
Out[11]: 192

~0xff
Out[13]: -256

0xff
Out[14]: 255

~0o77
Out[15]: -64

0o77
Out[16]: 63

print('x:1\n{1:04b}\n\nx<<2\n{1:04b}<<2\n{4:04b}\n\nx|2\n{1:04b}|{2:04b}\n{3:04b}\n\nx&3\n{1:04b}&{3:04b}\n{1:04b}\n\nx^3\n{1:b}^{3:b}\n{2:b}\n\n0xff^0o77\n{5:b}^{6:b}\n{7:b}\n\n~0xff\n~{5:b}\n{8:b}'.format(0,1,2,3,4,0xff,0o77,0xff^0o77,~0xff))
x:1
0001

x<<2
0001<<2
0100

x|2
0001|0010
0011

x&3
0001&0011
0001

x^3
1^11
10

0xff^0o77
11111111^111111
11000000

~0xff
~11111111
-100000000

一元运算符 ~ (取反) 的结果是对其整数参数按位取反。x 的按位取反被定义为 -(x+1)。它只作用于整
数。(from reference.pdf 69p 6.6 一元算术和位运算)

X=99

bin(X),X.bit_length(),len(bin(X))-2
Out[34]: ('0b1100011', 7, 7)

0xff,2**8-1,bin(0xff),int.bit_length(0xff),len(bin(0xff))-2
Out[37]: (255, 255, '0b11111111', 8, 8)

int.bit_length(-0xff)
Out[38]: 8

-0xff
Out[39]: -255

bin(-0xff)
Out[40]: '-0b11111111'

0xff.bit_length()
Out[41]: 8

255.bit_length()
SyntaxError: invalid syntax

另外,Python 2.7和3.X引入了新的整数方法bit_length,它允许我们查询二进制表示一个数字的值时所需的位数,负数只取二进制部分。
0xff可以直接根.bit_length(),但是数字不行,但可以用(255),把数字括起来,也是一个表达式。

这里不会涉及更多关于“位运算”的细节。如果你需要,Python是支持的,但是按位运算在Python这样的高级语言中,并不像在C这样的底层语言中那么重要,经验法则是,如果你需要在Python中反转位,那么你应该考虑一下到底现在使用的是哪一门语言

意思是,C语言才是比特级运算最合适的语言,Python可以,但是他底层的不支持,只是表层的支持,使用Python的比特级运算,效率很低,所以尽可能不要在Python中使用比特级运算当作核心操作,这时你需要考虑C语言,可以用C语言编写相关模块,集成到Python中!

我们将在接下来的几章中看到,Python的列表、字典以及其他数据结构提供了比位数字串更为丰富(且通常也更好的)编码信息的方式,而且这些方式可读性更强。

比特级运算的好处,Python的内置工具确实结构上更合理,但也更加耗费内存,例如一个元素在列表中占用至少32字节(列表地址1+元素表头2+数据1 * 8),在C语言的比特级运算中,最少占用1比特,256倍的差距,这涉的元素数越多,占用内存的空间越明显。而让Python强制使用(伪)比特级运算,效率又比核心类型低。

其他内置数值工具

除了核心对象类型和运算符以外,Python还提供了用于数据处理的内置函数和内置模块。

内置函数,不同于运算符和核心类型方法的函数。
例如pow(base,exp[,mod]),不同于x**y,他还增加了mod这个参数。
例如sum(iterable,start=0),操作对象是元素是数字的容器,不是任意容器,所以没有加入到容器的内置方法。
例如abs(x),返回一个数的绝对值。参数可以是整数、浮点数或任何实现了__abs__() 的对象。如果参数是一个复数,则返回它的模。abs、len等,都是调用了操作对象的__abs__()、len()等多态方法,查看一下int、float、complex以及decimal.Decimal、fractions.Fraction,他们都是数字类型,都定义了__abs__(),实例对象都可以使用abs()函数。
而类似于__add__(),这个没有对应的内置函数,但是有对应的操作符+。而在内置模块operator中,有对应的add函数。
而内置模块,不常用的方法、类型的集合体,非核心内容,所以作成了模块,减小核心负载。但仍是Python原生态的一部分。

pow(38,0)
Out[64]: 1

pow(38,0,mod=97)
Out[65]: 1

pow(38,1,mod=97)
Out[66]: 38

pow(38,2,mod=97)
Out[67]: 86

pow(38,3,mod=97)
Out[68]: 67
38**3%97==67

pow(38,-1,mod=97)
Out[69]: 23

pow(38,-2,mod=97)
Out[70]: 44

pow(38,-3,mod=97)
Out[71]: 42

23*38%97==1
44*38%97==23
42*38%97==44

38**3%97 # 14.8 ns
pow(38,3)%97 # 426 ns
pow(38,3,97) # 904 ns
pow(38,3,mod=97) # 946 ns

这种三参数运算的意义为何,我目前并不知晓,3.8版,允许第二个参数为负值

abs(-1)
Out[95]: 1

abs(-1) # 79.4 ns
(-1).__abs__() # 107 ns
int.__abs__(-1) # 154 ns

x=-1
x.__abs__() # 114 ns
x=-1
(-1).__abs__() # 118 ns
#这里为了平衡运算,(-1)前也加了x=-1

abs(x),更迅捷,若至少用一次的话,又非要用__abs__或者其他内置方法的话,建议使用(-1)

下面是一些内置模块math(包含在C语言math库中的绝大多数工具):

import math

math.pi,math.e
Out[2]: (3.141592653589793, 2.718281828459045)

math.sin(2*math.pi/180)
Out[3]: 0.03489949670250097

math.sqrt(144),math.sqrt(2)
Out[4]: (12.0, 1.4142135623730951)

pow(2,4),2**4,2.0**4.0
Out[5]: (16, 16, 16.0)

abs(-42.0),sum((1,2,3,4))
Out[7]: (42.0, 10)

min((3,1,2,4)),max((3,1,2,4))
Out[8]: (1, 4)
#数学运算

math.floor(2.567),math.floor(-2.567)
Out[10]: (2, -3)

math.trunc(2.567),math.trunc(-2.567)
Out[11]: (2, -2)

int(2.567),int(-2.567)
Out[12]: (2, -2)

round(2.567),round(2.467),round(2.567,2)
Out[13]: (3, 2, 2.57)

'%.1f'%2.567,'{0:.2f}'.format(2.567)
Out[14]: ('2.6', '2.57')
#数值舍入,字符串显示舍入

(1/3.0),round(1/3.0,2),('%.2f'%(1/3.0))
Out[16]: (0.3333333333333333, 0.33, '0.33')


144**.5 # 15 ns
math.sqrt(144) # 210 ns
pow(144,.5) # 329 ns
12.0

1234567890**.5 # 15.1 ns
math.sqrt(1234567890) # 308 ns
pow(1234567890,.5) # 432 ns
35136.41828644462


import random

random.choice(['Life of Brian','Holy Grail','Meaning of Life'])
Out[40]: 'Life of Brian'

random.choice(['Life of Brian','Holy Grail','Meaning of Life'])
Out[41]: 'Meaning of Life'

suits=['hearts','clubs','diamonds','spades']

random.shuffle(suits)
#洗牌
suits
Out[44]: ['hearts', 'diamonds', 'spades', 'clubs']

random.shuffle(suits)

suits
Out[46]: ['diamonds', 'hearts', 'clubs', 'spades']

其他数值类型

之前已经介绍了Python的核心数值类型:整数、浮点数、复数。
对于绝大多数程序员来说,这足以应对大部分的使用需求,然而,Python还自带了一些更少见的数值类型。

小数类型

Python 2.4引入的一种新的核心数据类型:小数对象,其正式的名称是Decimal。
从语法上讲,需要导入模块中的函数来创建小数,而不是通过运行字面量表达式来创建。
从功能上讲,小数对象很像浮点数,但他们有固定的位数合小数点。因此小数是精度固定的浮点数。

使用小数对象,我们可以定义如何省略和截断额外的小数数字。
小数对于浮点数来说,性能较差,但是对表大固定精度的特性乙级队实现更好精度而言,是一个理想的工具。
Python 3.3开始,小数模块的性能也得到了极大的提升,新版本宣称速度提升了10到100倍。

s=str(2**-1074)

s
Out[13]: '5e-324'

D=decimal.Decimal

float('0.5') # 364 ns
D('0.5') # 647 ns
D(s) # 875 ns
decimal.Decimal(s) # 929 ns
float(s) # 1.04 μs
#在小数额转换时,float性能大于decimal,在大数额时,反之。

d=D(s)
f=float(s)
f*10 # 141 ns
d*10 # 175 ns
#在运算时,浮点数性能高于小数

f=2**-1074

f
Out[25]: 5e-324

mem.data(f)
01 00 00 00 00 00 00 00

'%.1100f'%f
Out[26]: '0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000494065645841246544176568792868221372365059802614324764425585682500675507270208751865299836361635992379796564695445717730926656710355939796398774796010781878126300713190311404527845817167848982103688718636056998730723050006387409153564984387312473397273169615140031715385398074126238565591171026658556686768187039560310624931945271591492455329305456544401127480129709999541931989409080416563324524757147869014726780159355238611550134803526493472019379026810710749170333222684475333572083243193609238289345836806010601150616980975307834227731832924790498252473077637592724787465608477820373446969953364701797267771758512566055119913150489110145103786273816725095583738973359899366480994116420570263709027924276754456522908753868250641971826553344726562500000000000000000000000000'

decimal.Decimal(f)
Out[27]: Decimal('4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625E-324')
#小数完全没有精度的损失,但是浮点型轻易就会有精度损失

In [10]: f=decimal.Decimal(2**-1074)

In [11]: mem.data(f)
ff ff ff ff ff ff ff ff 10 ff ff ff ff ff ff ff
ce fb ff ff ff ff ff ff ef 02 00 00 00 00 00 00
28 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00
50 ed 85 e9 2f 02 00 00 01 00 00 00 00 00 00 00
20 27 70 72 65 5f 72 75 6e 5f 63 6f 64 65 5f 68
6f 6f 6b 27 00 00 00 00

50 ed 85 e9 2f 02 00 00:

59 DD 0B 5A 03 37 DE 86 11 5E 9A A8 F3 06 86 5A
35 1F 32 D1 55 FE 69 58 86 A6 69 78 39 05 DC 32
1B 8F C3 BC 69 26 B7 46 A8 39 08 73 D0 43 14 0E
FB 10 25 21 53 51 8E 4E B0 17 70 2F B4 23 40 41
08 0E 7D E7 BB 59 FF 6B 54 87 4D 03 0D D8 7B 58
F5 C2 C6 42 76 F1 96 28 BF 77 B4 4F 58 35 60 70
F9 F5 EE 9D 54 52 21 74 C1 1B 77 88 67 23 02 2D
98 D8 B6 07 8D 11 1B 2E 77 5F A2 FF C2 BC 99 34
13 05 1D D7 17 EB DE 01 A3 32 BC FB 4D 39 2A 25
63 B9 2F E2 78 6F 09 2D 33 19 0E A6 21 CE CF 1A
92 80 44 6E B8 8F 90 01 44 5C FF ED 20 87 57 44
27 51 FA D6 BA 8F 4F 04 54 16 29 0C A3 83 43 51
3A 7E 2A CE 10 E5 84 11 3A D3 C9 B1 9C 0F 55 47
E1 67 85 02 32 9F 2B 79 33 4C 55 D8 25 ED E2 00
EA EE 5E A5 15 70 D9 77 67 8D 95 DD C4 3E D3 17
FA C8 2B 85 18 9C F9 62 DC 06 CA 50 65 DA 8E 42
45 B8 C0 87 D3 90 B3 4E 52 C0 DC F2 28 03 5E 4E
94 21 9B EB CA C6 81 88 61 81 35 53 38 92 C9 68
AE 12 2F 73 3E F3 11 2D F6 45 9A 79 9B 2E 18 72
B6 18 AF E9 56 B7 3D 39 4A 6F 7C 26 01 00 00 00

In [25]: c=0
    ...: l1=[]
    ...: for i in range(0,len(l:=s.split()),8):
    ...:    l1.append(int(bytes.fromhex(''.join(l[i:i+8]))[::-1].hex(),16)*(0x8ac7230489e7ffff+1)**c)
    ...:    c+=1
    ...:

In [26]: sum(l1)
Out[26]: 4940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625

第5章 数值类型_第2张图片

小数基础知识

浮点数运算缺乏精确性,这是因为用来存储数值的空间有限:

0.1+0.1+0.1-0.3
Out[60]: 5.551115123125783e-17

from decimal import Decimal

Decimal('0.1')+Decimal('0.1')+Decimal('0.1')-Decimal('0.3')
Out[2]: Decimal('0.0')
#如果使用小数对象,结果将更准确

Decimal('0.1')+Decimal('0.1')+Decimal('0.1')-Decimal('0.30')
Out[3]: Decimal('0.00')
#当表达式中回合使用不同精度的小数时,Python会自动转换为最高的小数位

Decimal(0.1)+Decimal(0.1)+Decimal(0.1)-Decimal(0.3)
Out[3]: Decimal('2.775557561565156540423631668E-17')

Decimal接受的参数默认是字符串,也可以接受整数、浮点数。另外Decimal有一个方法Decimal.from_float()接受浮点数。

设置全局小数精度

from decimal import Decimal

Decimal('0.1')+Decimal('0.1')+Decimal('0.1')-Decimal('0.30')
Out[3]: Decimal('0.00')

Decimal(1)/Decimal(7)
Out[4]: Decimal('0.1428571428571428571428571429')

Decimal(1/7)
Out[5]: Decimal('0.142857142857142849212692681248881854116916656494140625')

import decimal

decimal.getcontext().prec=4
Decimal(1)/Decimal(7)
Out[10]: Decimal('0.1429')

decimal.getcontext().prec=4
Decimal(0.1)+Decimal(0.1)+Decimal(0.1)-Decimal(0.3)
Out[11]: Decimal('1.110E-17')

decimal.getcontext().prec=100
Decimal(1)/Decimal(7)
Out[12]: Decimal('0.1428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571429')

decimal.getcontext().prec
Out[13]: 28

IPython语句块有上下文管理器的作用,若.prec单独一个语句块,是无法对之后有影响的,而prec预设值是28

1999+1.33
Out[4]: 2000.33

decimal.getcontext().prec=2
pay=decimal.Decimal(str(1999+1.33))

pay
Out[6]: Decimal('2000.33')

这对于处理货币的应用尤其有用,使用两位小数标识分
在这个上下文中,小数实际上是手动射入和字符串格式化的一种替代方式

小数上下文管理器

在Python 2.6\3.0及之后版本,可以使用with上下文管理器语句来临时重置小数精度
在with语句退出后,精度又重置为初始值


import decimal
decimal.Decimal('1.00')/decimal.Decimal('3.00')
Out[1]: Decimal('0.3333333333333333333333333333')

with decimal.localcontext() as ctx:
    ctx.prec=2
    decimal.Decimal('1.00')/decimal.Decimal('3.00')

with decimal.localcontext() as ctx:
    ctx.prec=2
    d=decimal.Decimal('1.00')/decimal.Decimal('3.00')

d
Out[4]: Decimal('0.33')

with更多细节请参阅本书第34章,或者库手册3.3.9 with 语句上下文管理器、8.5
with 语句

由于小数类型在实际中仍然较少用到,因此请参考Python的标准库手册来了解更多细节
小数和分数类型都解决了浮点数的某些精度问题

decimal.Decimal(str(1999+1.33))
Out[6]: Decimal('2000.33')

decimal.Decimal(1999+1.33)
Out[7]: Decimal('2000.329999999999927240423858165740966796875')

decimal.getcontext().prec=1000
decimal.Decimal(str(1999+1.33))
Out[11]: Decimal('2000.33')

decimal.getcontext().prec=2
decimal.Decimal(1999+1.33)
Out[21]: Decimal('2000.329999999999927240423858165740966796875')
#对Decimal函数生成无效

decimal.getcontext().prec=2
decimal.Decimal(1999+1.33)/decimal.Decimal(1)
Out[22]: Decimal('2.0E+3')

decimal.Decimal(1999+1.33)/decimal.Decimal(1)
Out[23]: Decimal('2000.329999999999927240423858')
#仅对运算表达式结果有效

decimal.getcontext().prec=2
decimal.Decimal(1)/decimal.Decimal(3) # 786 ns
Out[18]: Decimal('0.33')

decimal.getcontext().prec=100
decimal.Decimal(1)/decimal.Decimal(3) # 966 ns
Out[17]: Decimal('0.3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333')
#精度不同,运算时长不同

分数类型

Python 2.6/3.0首次引入了新的数值类型Fraction(分数),他实现了一个有理数对象
本质上,他显式的保持了一个分子和一个分母,从而避免了浮点运算的某些不精确性和局限性
与小数一样,分数的实现并不像浮点数靠近计算机的底层硬件
这意味着他们的性能可能不会和浮点数一样优秀,但是这也允许他们在必要时作为一种有用的标准工具

分数基础知识

Fraction与Decimal固定精度类型十分相似,他们都可以用来处理浮点类型的数值不精确性

from fractions import Fraction

x=Fraction(1,3)

y=Fraction(4,6)

x
Out[4]: Fraction(1, 3)

y
Out[5]: Fraction(2, 3)

print(y)
2/3

x+y
Out[7]: Fraction(1, 1)

x-y
Out[8]: Fraction(-1, 3)

x*y
Out[9]: Fraction(2, 9)

Fraction('.25')
Out[10]: Fraction(1, 4)

Fraction('1.25')
Out[11]: Fraction(5, 4)

Fraction('.25')+Fraction('1.25')
Out[12]: Fraction(3, 2)

>>> mem.data(Fraction('3/7'))
f0 69 f5 bb db 01 00 00 70 69 f5 bb db 01 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7                       3

help(Fraction)
 |  as_integer_ratio(self)
 |      Return the integer ratio as a tuple.

 |  limit_denominator(self, max_denominator=1000000)
 |      Closest Fraction to self with denominator at most max_denominator.
 |
 |      >>> Fraction('3.141592653589793').limit_denominator(10)
 |      Fraction(22, 7)
 |      >>> Fraction('3.141592653589793').limit_denominator(100)
 |      Fraction(311, 99)
 |      >>> Fraction(4321, 8765).limit_denominator(10000)
 |      Fraction(4321, 8765)

 |      >>> Fraction(10, -8)
 |      Fraction(-5, 4)
 |      >>> Fraction(Fraction(1, 7), 5)
 |      Fraction(1, 35)
 |      >>> Fraction(Fraction(1, 7), Fraction(2, 3))
 |      Fraction(3, 14)
 |      >>> Fraction('314')
 |      Fraction(314, 1)
 |      >>> Fraction('-35/4')
 |      Fraction(-35, 4)
 |      >>> Fraction('3.1415') # conversion from numeric string
 |      Fraction(6283, 2000)
 |      >>> Fraction('-47e-2') # string may include a decimal exponent
 |      Fraction(-47, 100)
 |      >>> Fraction(1.47)  # direct construction from float (exact conversion)
 |      Fraction(6620291452234629, 4503599627370496)
 |      >>> Fraction(2.25)
 |      Fraction(9, 4)
 |      >>> Fraction(Decimal('1.47'))
 |      Fraction(147, 100)

Fraction('3.141592653589793') # 9.54 ?s
Fraction('3.141592653589793').limit_denominator(10) # 34.8 ?s
Fraction('3.141592653589793').limit_denominator(100) # 35 ?s
Fraction('3.141592653589793').limit_denominator(1000) # 36.7 ?s

limit_denominator是一个很有趣的话题,如果你的数学成绩很好,你可以尝试去实现并优化这个功能!在你有思路,实现后,可以看一下我的随记:limit_denominator算法

分数和小数中的数值精度

分数和小数中的数值精度与浮点数运算有所区别(浮点数运算受到浮点数硬件底层限制的约束)

9900K架构解读
现在cpu都含有fpu或fp fma等(双精度)浮点运算单元,所以Python以及其他编程语言的浮点运算都是交由fpu等底层硬件运算的,所以Python这里的float使用了双精度浮点数的结构,而整数是自己独有的无限精度长整数,作为更高精度的小数类型:decimal、fraction,是基于Python引擎处理的自己独有的类型,和长整数一样,都需要软件和硬件结合处理的

a=1/3.0

b=4/6.0

a
Out[3]: 0.3333333333333333

b
Out[4]: 0.6666666666666666

a+b
Out[5]: 1.0

a-b
Out[6]: -0.3333333333333333

a*b
Out[7]: 0.2222222222222222

from decimal import Decimal

Decimal(a+b)
Out[9]: Decimal('1')

Decimal(a-b)
Out[10]: Decimal('-0.333333333333333314829616256247390992939472198486328125')

Decimal(a*b)
Out[11]: Decimal('0.22222222222222220988641083749826066195964813232421875')

Decimal(a)
Out[12]: Decimal('0.333333333333333314829616256247390992939472198486328125')

Decimal(b)
Out[13]: Decimal('0.66666666666666662965923251249478198587894439697265625')

对于那些用内存中给定的有限位数无法精确表示的值,浮点数的局限性尤为明显。
Fraction和Decimal都提供了得到精确结果的方式,但这需要付出一些速度和代码冗余性的代价。

0.1+0.1+0.1-0.3
Out[14]: 5.551115123125783e-17

from fractions import Fraction

Fraction(1,10)+Fraction(1,10)+Fraction(1,10)-Fraction(3,10)
Out[16]: Fraction(0, 1)

Decimal('0.1')+Decimal('0.1')+Decimal('0.1')-Decimal('0.3')
Out[17]: Decimal('0.0')

此外,分数和小数都能提供比浮点数更直观和准确的结果,他们以不同的方式做到这点:使用有理数表示以及通过限制精度。

1/3
Out[18]: 0.3333333333333333

Fraction(1,3)
Out[19]: Fraction(1, 3)

str(Fraction(1,3))
Out[27]: '1/3'

import decimal

decimal.getcontext().prec=2
Decimal(1)/Decimal(3)
Out[29]: Decimal('0.33')

分数还可以自动简化结果

(1/3)+(6/12)
Out[3]: 0.8333333333333333

Fraction(6,12)
Out[4]: Fraction(1, 2)

Fraction(1/3)+Fraction(6,12)
Out[5]: Fraction(15011998757901653, 18014398509481984)

Fraction(1,3)+Fraction(6,12)
Out[6]: Fraction(5, 6)

Decimal(str(1/3))+Decimal(str(6/12))
Out[7]: Decimal('0.8333333333333333')

1000.0/1234567890
Out[8]: 8.100000073710001e-07

Fraction(1000,1234567890)
Out[9]: Fraction(100, 123456789)

分数转换和混用类型

在int、float、Decimal、Fraction中,都有.as_integer_ratio()这个方法,他返回一个分子和分母,而Fraction正好可以接受这些数值生成一个实例,而Fraction还有一个from_float的方法。
以下用到*语法,是将元组分解为元素语句对待,另外还有**语法,是将对象分解为俯值表达式对待。

(3.1415).as_integer_ratio()
Out[14]: (7074029114692207, 2251799813685248)

(2.5).as_integer_ratio()
Out[15]: (5, 2)

f=2.5

z=Fraction(*f.as_integer_ratio())

z
Out[18]: Fraction(5, 2)

Fraction(*(2.5).as_integer_ratio()) # 2.84 ?s
Fraction(2.5) # 3.84 ?s
Fraction(*(3.1415).as_integer_ratio()) # 4.21 ?s
Fraction(3.1415) # 4.58 ?s
Fraction.from_float(2.5) # 4.89 ?s
Fraction.from_float(3.1415) # 6.37 ?s
#耗时偏差很多,至于Fraction究竟如何运行,有能力的可以去看源码

x=Fraction(1,3)

x+z
Out[29]: Fraction(17, 6)

float(x)
Out[30]: 0.3333333333333333

float(z)
Out[31]: 2.5

float(x+z)
Out[32]: 2.8333333333333335

17/6
Out[33]: 2.8333333333333335

Fraction.from_float(1.75)
Out[34]: Fraction(7, 4)

Fraction(*(1.75).as_integer_ratio())
Out[35]: Fraction(7, 4)

表达式中允许某些类型的混合

x
Out[37]: Fraction(1, 3)

x+2 # Fraction + int -> Fraction
Out[38]: Fraction(7, 3)

x+2.0 # Fraction + float -> float
Out[39]: 2.3333333333333335

x+(1./3)
Out[40]: 0.6666666666666666

x+(4./3)
Out[41]: 1.6666666666666665

x+Fraction(4,3) # Fraction + Fraction -> Fraction
Out[42]: Fraction(5, 3)

从表达式的混合规则看,float的复杂度>Fraction>int,那我突然想到一个问题

3.1415
Out[43]: 3.1415

Decimal(3.1415)
Out[44]: Decimal('3.141500000000000181188397618825547397136688232421875')

3.1415+1
Out[45]: 4.141500000000001

4.1415
Out[46]: 4.1415

Decimal(4.1415)
Out[47]: Decimal('4.14149999999999973709918776876293122768402099609375')

Decimal(3.1415+1)
Out[48]: Decimal('4.14150000000000062527760746888816356658935546875')

浮点型拥有最高优先级,这个很可能和FPU有关,因为有FPU,相关运算自然优先交给FPU,如浮点型相关的加减乘除,所以即便是+1,1很可能也是转换为FPU兼容的类型即浮点型。
在这,且不考虑FPU的问题,在Fraction与float的运算结果是float这一客观现象来说,不可能先float—>Fraction->float这一操作,首先float大几率是不精确地,所以这种转换无意义且低效,所以是Fraction->float!
因为float在最初可能就是不精确地,所以转换到Fraction时,很可能会得到不想要的结果,这里可以限制分母的最大值来简化这样的结果。

4.0/3
Out[49]: 1.3333333333333333

(4.0/3).as_integer_ratio()
Out[50]: (6004799503160661, 4503599627370496)

x
Out[51]: Fraction(1, 3)

a=x+Fraction(*(4.0/3).as_integer_ratio())

a
Out[56]: Fraction(22517998136852479, 13510798882111488)

float(a)
Out[57]: 1.6666666666666665

a.limit_denominator(10)
Out[58]: Fraction(5, 3)

Fraction(3.1415)
Out[59]: Fraction(7074029114692207, 2251799813685248)

Fraction(3.1415).limit_denominator(10000)
Out[60]: Fraction(6283, 2000)

更多Fraction类型的细节,请参考库手册以及其他文档

集合

除了小数以外,Python2.4还引入了一种新的类型:集合
(set n. 一组 一套 一伙 一批 一局 一盘 集 集合 设备…)
这是一些唯一的,不可变的对象的一个无序集合体(collection),这些对象支持与数学集合理论相对应的操作
按照定义,一个元素在集合中只能出现一次,不管他被添入了多少次,因此集合有着广泛的应用,尤其是在涉及数值和数据库的工作中

因为集合是其他对象的集合体,因此它具有列表和字典的某些共同行为,例如集合是可迭代对象,可以按需增长或缩短,可以包含多种对象类型。集合的行为很像一个有键无值的字典,不过集合还支持更多的操作。

然而,由于集合是无序的,而且不会把键映射到值,因此他们既不是序列,也不是映射,他们是自成一体的类型。此外,由于集合本质上具有基本的数学特性,他对于很多读者来说,集合可能更加学院派,并且比像字典这样更为普遍的对象用的要少很多。

Python 2.6及之前版本中的集合基础知识

x=set('abcde')

y=set('bdxyz')

x
Out[3]: {
     'a', 'b', 'c', 'd', 'e'}

y
Out[4]: {
     'b', 'd', 'x', 'y', 'z'}

x-y
Out[5]: {
     'a', 'c', 'e'}

x|y
Out[6]: {
     'a', 'b', 'c', 'd', 'e', 'x', 'y', 'z'}

x&y
Out[7]: {
     'b', 'd'}

x^y
Out[8]: {
     'a', 'c', 'e', 'x', 'y', 'z'}

x>y,x<y
Out[9]: (False, False)

x&y<x,x|y>x
Out[3]: (True, True)

'e' in x
Out[6]: True

'e' in 'Camelot',22 in [11,22,33]
Out[7]: (True, True)

z=x.intersection(y) # 内置方法,交叉,交集

x.intersection(y) # 334 ns
x&y # 344 ns
x.__iand__(y) # 411 ns 这里是原地放回

x
Out[60]: {
     'b', 'd'}

z
Out[9]: {
     'b', 'd'}

z.add('SPAM') # 类似list.append(e)

z
Out[11]: {
     'SPAM', 'b', 'd'}

z.update(set(['X','Y'])) # 类似list.extend(l),dict.update(d)

z
Out[15]: {
     'SPAM', 'X', 'Y', 'b', 'd'}

z.remove('b') # list.remove(e)、del list[i]/dict[k],pop是有返回值的,list.pop、dict.pop/popitem

z
Out[17]: {
     'SPAM', 'X', 'Y', 'd'}

len(x)
Out[62]: 5

for item in x:print(item)
d
e
c
b
a

S=set([1,2,3])

S|set([3,4])
Out[65]: {
     1, 2, 3, 4}

S|[3,4]
TypeError: unsupported operand type(s) for |: 'set' and 'list'

S.union([3,4]) # 这些内置方法支持set与可迭代对象之间的比较
Out[67]: {
     1, 2, 3, 4}

S.intersection((1,3,5))
Out[68]: {
     1, 3}

S.issubset(range(-5,5))
Out[69]: True

尽管机和操作可以再Python中通过其他类型(列表、字典等)手动实现,但是Python的内置集合类型使用了更高效的算法和实现技术来提供快速和标准的操作。

集合字面量

set([1,2,3,4])
Out[78]: {
     1, 2, 3, 4}

{
     5,6,7,8}
Out[79]: {
     5, 6, 7, 8}

set()
Out[80]: set()

不可变性限制与冻结集合

集合是强大而灵活的对象,但也有所限制:
集合只能包含不可变(可哈希化的)对象类型,所以列表和字典不能嵌入到集合中,但是元组可以。


S=set()

S.add(1.23)

S
Out[83]: {
     1.23}

S.add([1,2,3])
TypeError: unhashable type: 'list'

S.add({
     'a':1})
TypeError: unhashable type: 'dict'

S.add((1,2,3))

S
Out[88]: {
     (1, 2, 3), 1.23}

S|{
     (4,5,6),(1,2,3)}
Out[89]: {
     (1, 2, 3), (4, 5, 6), 1.23}

S.union({
     (4,5,6),(1,2,3)})
Out[91]: {
     (1, 2, 3), (4, 5, 6), 1.23}

S.__or__({
     (4,5,6),(1,2,3)})
Out[96]: {
     (1, 2, 3), (4, 5, 6), 1.23}

S
Out[97]: {
     (1, 2, 3), 1.23}

S.__ior__({
     (4,5,6),(1,2,3)})
Out[98]: {
     (1, 2, 3), (4, 5, 6), 1.23}

S
Out[99]: {
     (1, 2, 3), (4, 5, 6), 1.23}

(1,2,3) in S
Out[100]: True

(1,4,3) in S
Out[101]: False

集合中的元组可以用来表示日期、记录、IP地址等,集合也可以包含模块、类型对象,集合本身也是可变的,因此不能直接嵌入其它集合中。如果需要一个集合中存储一个集合,可以调用集合的内置函数frozenset,frozenset会创建一个不可变集合,该集合不可修改,并且可以嵌套到其他集合中。

集合推导

除了字面量,Python 3.X引入了一种集合推导构造,同时也被向前移植到Python 2.7中。

{
     x**2 for x in [1,2,3,4]}
Out[102]: {
     1, 4, 9, 16}

推到式类似于列表的推到式,但方括号变成花括号

{
     x for x in 'spam'}
Out[104]: {
     'a', 'm', 'p', 's'}

{
     c*4 for c in 'spam'}
Out[107]: {
     'aaaa', 'mmmm', 'pppp', 'ssss'}

{
     c*4 for c in 'spamham'}
Out[109]: {
     'aaaa', 'hhhh', 'mmmm', 'pppp', 'ssss'}

S={
     c*4 for c in 'spam'}

S
Out[111]: {
     'aaaa', 'mmmm', 'pppp', 'ssss'}

S|{
     'mmmm','xxxx'}
Out[113]: {
     'aaaa', 'mmmm', 'pppp', 'ssss', 'xxxx'}

S&{
     'mmmm','xxxx'}
Out[114]: {
     'mmmm'}

关于推导的知识要依赖于我们现在还不准备介绍的底层概念,这些之后章节介绍,之后将会遇到所有推到(列表、集合、字典、生成器),还有额外的语法:嵌套循环、if测试

为什么使用集合

级和操作有各种各样常见的用途,其中一些比他的数学意义更加实用。
集合可以用于过滤其他容器中的重复项,尽管元素可能会在该过程中常年更新排序。

L=[1,2,1,3,2,4,5]

set(L)
Out[116]: {
     1, 2, 3, 4, 5}

L=list(set(L))

L
Out[118]: [1, 2, 3, 4, 5]

list(set(['yy','cc','aa']))
Out[119]: ['cc', 'aa', 'yy']

集合也可以用于提取列表、字符串以及其他可迭代对象中的差异

set([1,3,5,7])-set([1,2,4,5,6])
Out[120]: {
     3, 7}

set('abcdefg')-set('abdghij')
Out[121]: {
     'c', 'e', 'f'}

set('spam')-set(['h','a','m'])
Out[122]: {
     'p', 's'}

set(dir(bytes))-set(dir(bytearray))
Out[123]: {
     '__getnewargs__'}

set(dir(bytearray))-set(dir(bytes))
Out[124]: 
{
     '__alloc__',
 '__delitem__',
 '__iadd__',
 '__imul__',
 '__setitem__',
 'append',
 'clear',
 'copy',
 'extend',
 'insert',
 'pop',
 'remove',
 'reverse'}

你可以通过转换成集合,借助集合进行顺序无关的等价性测试

L1,L2=[1,3,5,2,4],[2,5,3,4,1]

L1==L2
Out[126]: False

set(L1)==set(L2)
Out[127]: True

sorted(L1)==sorted(L2)
Out[128]: True

L1==L2
Out[129]: False

L1
Out[131]: [1, 3, 5, 2, 4]

sorted(L1)
Out[132]: [1, 2, 3, 4, 5]

L1.sort()

L1
Out[134]: [1, 2, 3, 4, 5]

'spam'=='asmp',set('spam')==set('asmp'),sorted('spam')==sorted('asmp')
Out[135]: (False, True, True)

遍历一个图或其他有环结构时,集合可用于记录以访问的位置,避免循环访问。

处理较大的数据集时,例如数据库查询结果,两个集合的交集包含了两个种类中共有的对象,并集包含了两个集合中的所有元素。

假想公司的人员名单:

engineers={
     'bob','sue','ann','vic'}

managers={
     'tom','sue'}

'bob' in engineers
Out[3]: True

engineers & managers
Out[4]: {
     'sue'}

engineers | managers
Out[5]: {
     'ann', 'bob', 'sue', 'tom', 'vic'}

engineers-managers
Out[6]: {
     'ann', 'bob', 'vic'}

managers - engineers
Out[7]: {
     'tom'}

engineers > managers
Out[8]: False

{
     'bob','sue'} < engineers
Out[9]: True

(engineers | managers) > managers
Out[10]: True

managers ^ engineers
Out[11]: {
     'ann', 'bob', 'tom', 'vic'}

(managers | engineers) - (managers ^ engineers)
Out[12]: {
     'sue'}

你可以在库手册以及关系数据库理论的一些相关资料中,找到关于集合操作的更多细节
在第8章字典视图对象时,将再次回顾这里所见的一些集合操作

布尔型


type(True)
Out[13]: bool

isinstance(True,int)
Out[14]: True

isinstance(True,bool)
Out[15]: True

True==1
Out[16]: True

True is 1 # is在python的含义是内存地址一样
SyntaxWarning: "is" with a literal. Did you mean "=="?
Out[17]: False

1.1 is 1.1
SyntaxWarning: "is" with a literal. Did you mean "=="?
Out[5]: True

#################
f1=1.1

f2=1.1

f1 is f2
Out[8]: False

i1=1000+1

i2=1000+1

i1==i2
Out[17]: True
#################

True or False
Out[18]: True

True + 4
Out[19]: 5

True==-1
Out[1]: False

True==bool(-1)
Out[2]: True

bool(-1)
Out[3]: True

True-1==False
Out[4]: True

有些人会认为Python的布尔类型(bool)本质上是数值的,因为它包含两个值True和False就是整数1和0的定制版,只不过打印时有所不同。
更正式的,今天Python有一个名为bool的显式布尔数据类型,带有True和False作为可用且预赋值的内置名称。
在内部,True和False是bool的实例,而bool实际上只是内置整数类型int的子类(从面向对象的角度来看)。
True和False的行为与整数1和0是一样的,只不过他们有独特的显示逻辑
他们作为关键字True和False显式,而不是数字1和0,bool为这两个对象重新定义了str和repr的字符串格式

str(True)
Out[19]: 'True'

repr(True)
Out[20]: 'True'

由于该定制,布尔表达式在交互式命令行模式的输出就作为关键字True和False来显式,而不是曾经的1和0。
此外,布尔型让针织在你的代码中更为明显,例如while True,比while 1更直观。

if 1:pass # 14.5 ns
if 0:pass # 14.5 ns
if True:pass # 14.5 ns
if False:pass # 14.5 ns

运行耗时,1、0、True、False是相等的

通过flag=False可以更清楚的设置标志位。将在第三部分讨论这些语句。

这里的意思和我之前提到的用’inf’等float特殊数值思路一样,不过False等不需要多余的函数处理,爱了~ 但前提是flag在句中的功能,是用作对比还是用作存储,用作对比的话,个人还是推荐’inf’之类的

对于大多数实际场景,你可以把True和False看作预定义的设置为整数1和0的名称。例如,True+4 => 5

数值扩展

尽管Python的核心数值类型提供的功能对于大多数应用程序而言已经够了,但还是有大量的第三方开源扩展可以用来解决更加专门的需求。

例如NumPy(Numeric Python),提供了高级的数值编程工具,例如矩阵数据类型、向量处理和精密的计算库。洛斯阿拉莫斯国家实验室(世界第一个原子弹诞生的地方)、美国国家航空航天局(NASA),都是用NumPy,此前使用C++、FORTRAN、Matlab,NumPy可以看作是一款免费的、更灵活的Matlab。

很庆幸这本书中不会介绍他,否则,数学劝退

本章小结

本章介绍了Python树脂对象类型及其运算:
整数、浮点数
奇特少见的类型:复数、小数、分数、集合
表达式语法
类型转换
按位运算
字面量

下一章介绍变量赋值的机制

本章习题

1、Python中表达式2*(3+4)的值是多少

这里我建议直接写出来,而非去Python中码字

2、~ 23+4
3、~ 2+3
4
4、你可以使用什么工具来计算一个数字的平方根以及他的平方?
5、表达式1+2.0+3的结果是什么类型?
6、如何截断或舍去浮点数的小数部分?
7、如何将一个整数转换为浮点数?
8、如何将一个整数显示成八进制、十六进制或二进制的形式?
9、如何将一个八进制、十六进制或二进制的字符串转换成一般的整数?

习题解答

4、

3**(1/2) # 15.3 ns
3**2 # 15.4 ns
math.sqrt(3) # 215 ns
math.pow(3,1/2) # 295 ns
math.pow(3,2) # 320 ns
pow(3,1/2) # 337 ns
pow(3,2) # 373 ns
pow(3,1/2) # 356 ns
pow(3,.5) # 357 ns

书中这里用了.5,而非1/2,耗时无明显差别

pow(64,1/3)
Out[24]: 3.9999999999999996

from fractions import Fraction

pow(64,Fraction(1,3))
Out[26]: 3.9999999999999996

误差级别,看来只能精确,然后二度测试,之后再给出精确值,这样做一步检测步骤

from decimal import Decimal

Decimal(4.001)
Out[29]: Decimal('4.00100000000000033395508580724708735942840576171875')

i=1
while i>0.001:
    i/=2
    print(i)
    
0.5
0.25
0.125
0.0625
0.03125
0.015625
0.0078125
0.00390625
0.001953125
0.0009765625

Decimal(4.0009765625)
Out[33]: Decimal('4.0009765625')

Decimal(4.0009765625)**2
Out[34]: Decimal('16.00781345367431640625')

Decimal(4.0009765625)**3
Out[35]: Decimal('64.04688644502311944961547852')

Decimal(4.0009765625)**4
Out[36]: Decimal('256.2500915676364456885494292')

Decimal(4.0009765625)**5
Out[37]: Decimal('1025.250610500592302720690441')

pow(64.04688644502311944961547852,1/3)
Out[38]: 4.0009765625

Decimal(pow(64,1/3))
Out[39]: Decimal('3.999999999999999555910790149937383830547332763671875')

这里我用一个小数来实现1/3,其实这里有个问题,我目前没有有效且高效的手段判断一个数是否是精确值,这是一个有意思的东西,可以写一段实用的程序,感兴趣的同学可以试着写一下
6、

int(1.6)
Out[1]: 1

import math

math.floor(1.6)
Out[3]: 1

math.floor(-1.6)
Out[4]: -2

math.trunc(1.6)
Out[5]: 1

math.trunc(-1.6)
Out[6]: -1

int(-1.6)
Out[7]: -1

round(1.6)
Out[8]: 2

round(-1.6)
Out[9]: -2

math.ceil(-1.6)
Out[10]: -1

math.trunc(1.6) # 105 ns
math.floor(1.6) # 107 ns
math.trunc(-1.6) # 108 ns

int(N)
math.trunc(N) truncate v. 截断
round(N,digit) 这里给出了一个附加参数,默认是None,是指保留小数点后位数,可以是负数
math.floor(N)

round(1.125)
Out[45]: 1

round(1.125,1)
Out[46]: 1.1

round(1.125,2)
Out[47]: 1.12

round(1.125,-1)
Out[48]: 0.0

round(11.125,-1)
Out[49]: 10.0

round(11.125,0)
Out[50]: 11.0

7、

1*1. # 15.3 ns
1+0. # 15.4 ns
1/1 # 15.5
float(1) # 125 ns

很意外,虽然常规来说,是使用float,但是效率相对表达式,低很多,但为了直观,可能只能这样了
这里书中指明的混合表达式是除法,这确实是个很直观的方法,爱了
8、

bin(10)
Out[53]: '0b1010'

oct(10)
Out[54]: '0o12'

hex(10)
Out[55]: '0xa'

'%#x'%10
Out[58]: '0xa'

'{:#x}'.format(10)
Out[59]: '0xa'

这里我忽略了字符串格式化,仅想到了三个函数
9、

int('0xa',16)
Out[60]: 10

int('a',16)
Out[61]: 10

eval('0xa')
Out[62]: 10

这里我下意识的忽略了eval,虽然我也想到了,但是在现实考试中,这个可能是我不会写到纸上的答案,但很明显他是占分的

int(S,base)
eval(S)函数也能够用作这个目的,但是运行起来开销更大且可能导致安全问题。

注意,整数总是在计算机内存中以二进制形式存储,这些只不过是显示字符串格式的转换而已

int('a',16) # 205 ns
int('0xa',16) # 207 ns
eval('0xa') # 12.5 µs

mem.data(10)
01 00 00 00 00 00 00 00 0a 00 00 00

你可能感兴趣的:(啃书笔记)