Skip to content

内置类型

约 1517 个字 29 行代码 预计阅读时间 5 分钟

首先非常重要的一点,Python里面一切皆对象,包括内置的基础类型,比如float,如果按照C/C++的习惯,这就是一个基础类型,不算对象,但是在Python中它就是对象,而且是用C实现的,其实现为: * ob_refcnt:对象的引用计数 * ob_type:指向对象的指针 * ob_fval:一个C语言的double类型,存放float的值

丰富的序列

分类

Python 标准库用C语言实现了丰富的序列类型,包括:

  • 容器序列:可以存放不同的项,包括嵌套容器,比如list和tuple
  • 扁平容器:仅存放一种简单类型的项,比如str和array.array

也可以分为,可变序列继承不可变序列的所有方法: * 可变序列:list,array.array等 * 不可变容器:tuple,str等

一点技巧

列表推导式

可以快速构建一个序列,此外Python会忽略[]、{}和()内部的换行,所以过长的代码可以跨行写

# 不使用列表推导式
l1 = []
for i in range(10):
    l1.append(i)
# 使用列表推导式
l2 = [j for j in range(10)]

生成器表达式

列表推导式显然直接只能生成列表,如果需要其他序列还要加一次转换,会产生不必要的列表对象。生成式表达式语法几乎和列表推导式一模一样,只不过把方括号换成圆括号而已。比如

colors = ['black', 'white']
sizes = ['S', 'M' , 'L']
for tshirt in (f'{c} {s}' for c in colors for s in sizes):
    print(tshirt)

元组

元组是Python中比较特殊的存在,它不仅仅是不可变列表

用作记录

其实感觉就像是把元组当成一个特殊的数据对象,项的位置决定一个字段的数据

用作不可变列表

这个在初学Python的时候就是这么说的,在代码中看见元组,你就知道它的长度永不可改变。但是,元组的不可变特性仅仅针对元组中的引用而言,如果引用的是可变对象,那么元组中的值会随对象的改变而改变。比如

t = (1, 2, [30,40])
t[2] += [50,60]
这个有问题吗?显然是有的,毕竟元组“不可变”,但是它的结果确实是很诡异的,可以看到确实是报错了,但是同时确实真的加进去了

alt text

拆包

拆包的特点是不需要手动通过索引从序列中提取元素,这样减少了出错的可能性。

最明显的拆包形式是并行赋值

pos = (1,1)
x, y = pos
或者比如
a, b = b, a
以及在调用函数时可以拆包当作形参或者从整合的返回值中提取想要的值
# 上下两种写法等价
f(1,2)
t=(1,2)
f(*t)

切片

切片的功能比你想象的更加强大,常规的切片语法无非是[start:end:step],区间排除最后一项,得到的结果是一个切片对象,也就是说你可以给一个切片赋值

拼接

都知道+用于连接序列,*用于重复序列,但是一般来说得是不可修改的同类对象。如果对可变对象使用a*n这种语法,会出现一些出乎意料的问题,比如my_list = [[]] * 3初始化一个嵌套列表,确实得到了一个嵌套列表,但是嵌套的三个引用指向了同一个列表。

排序

list.sort方法就地排序,即不创建副本,返回值为None。这是Python API 的一个重要约定,就地更改对象的函数或方法应该返回 None。此外,Python 还提供 sorted 用于返回排好序的对象。

Python 的标准库使用的是 Timsort,这是一种混合的、稳定的排序算法,它巧妙结合了插入排序和归并排序的优点,针对数据集中的有序性进行了精确的优化,尤其适合处理包含大量部分有序子序列的数据集。最优情况时间复杂度接近\(O(N)\)

其它序列

数组

如果一个列表只含数值,那么使用array.array会更高效。Python的数组像C语言一样精简,创建Array时需要提供对应类型代码,用于决定底层使用什么C类型存储数组中的各项。

memoryview

说实话,我以前都没听说过这个(

内置的 memoryview 是一种共享内存的序列类型,可在不复制字节的情况下处理数组的切片。比如同一段6字节数组,既可以是6字节数组,也可以处理成1*6,2*3.3*2的矩阵。但是如果要做一些高级数值处理,则推荐使用 NumPy 库。

字典和集合

Python 是一个巨大的字典,没有Python程序不使用字典,即使不直接出现在自己编写的代码中,也会间接用到。Python中的字典和集合都是基于哈希表实现,所以它们的元素都必须是可以哈希的。

字典

字典推导式

字典推导式可以从任何可迭代对象中获取键值对

dial_codes = [
    (86, 'China'),
    (81, 'Japan'),
    (1, 'United States')
]
country_dial = {country: code for code, country in dial_codes}

映射拆包

首先,调用函数时,不止参数可以使用。但是,所有键都要是字符串,而且在所有参数中是唯一的。

def dump(**kwargs):
    return kwargs
dump(**{'x':1},y=2,**{'z':3})
# output: {'x': 1, 'y': 2, 'z': 3}
其次,可以在dict字面量中使用,同样可以多次使用

Python3.9开始支持使用||=合并映射

默认值字典

根据Python的“快速失败”原则,当键k不存在时,d[k]抛出错误。但是如果你想要默认值呢?这种情况下可以使用collection中的defaultdict

集合

集合是一组值不重复的对象,基本作用就是去重。空set没有字面量语法,必须写作set(){}默认是空字典。

实现与实践

set也是用哈希实现,所以集合元素必须是可哈希对象。元素的顺序取决于插入顺序,但是顺序是没有意义的,其一是集合本身就并非顺序容器,其二是哈希表里面有个操作是rehashing,这个操作会改变顺序。

集合运算

集合有一些额外的运算: * 交集:& * 并集:| * 差集:\ * 对称差集:^