# 数据类型概述

# 概述

Python3 中有六个标准的数据类型:

  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

Python3 的六个标准数据类型中:

  • 不可变数据(3 个): Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个): List(列表)、Dictionary(字典)、Set(集合)。

# 字符串

  • 字符串支持转义字符

  • Python 中字符串使用单引号、双引号或三引号表示,三者意义相同,并没有什么区别。其中,三引号的字符串通常用在多行字符串的场景。

  • 字符串支持索引,切片和遍历等操作

  • 字符串可以用 + 运算符连接在一起,用 * 运算符重复。

  • 字符串不可变,除了+=的所有操作都会创建一个新的字符串

    • 在扩充字符串时,若字符串只有当前一个引用,则会尝试在原地扩充buffer大小,而不会重新分配一块内存
    • 字符串内置的append和join方法效率较高,append仅为O(1)复杂度。
  • Python 中字符串的格式化(string.format)常常用在输出、日志的记录等场景。

    • % 老版本
    • string.format() 新版本

# 字符串前的urbf

  1. 字符串前加 u

    例:u"我是含有中文字符组成的字符串。"

    作用:

    后面字符串以 Unicode 格式 进行编码,一般用在中文字符串前面,防止因为源码储存格式问题,导致再次使用时出现乱码。

  2. 字符串前加 r

    例:r"\n\n\n\n”  # 表示一个普通生字符串 \n\n\n\n,而不表示换行了。

    作用:

    去掉反斜杠的转移机制。

    (特殊字符:即那些,反斜杠加上对应字母,表示对应的特殊含义的,比如最常见的”\n”表示换行,”\t”表示Tab等。 )

    应用:

    常用于正则表达式,对应着re模块。

  3. 字符串前加 b

    例: response = b'<h1>Hello World!</h1>' # b' ' 表示这是一个 bytes 对象​​

    作用:

    b" "前缀表示:后面字符串是bytes 类型。

    用处:

    网络编程中,服务器和浏览器只认bytes 类型数据。

    如:send 函数的参数和 recv 函数的返回值都是 bytes 类型

    附:

    在 Python3 中,bytes 和 str 的互相转换方式是

    str.encode('utf-8')
    bytes.decode('utf-8')
    
  4. 字符串前加 f

    import time
    t0 = time.time()
    time.sleep(1)
    name = 'processing'
    
    # 以 开头表示在字符串内支持大括号内的python 表达式****f***
    print(f'{name} done in {time.time() - t0:.2f} s')
    
    # processing done in 1.00 s
    

# 字典和集合

# dict

d = {'b': 1, 'a': 2, 'c': 10}
d_sorted_by_key = sorted(d.items(), key=lambda x: x[0]) # 根据字典键的升序排序
d_sorted_by_value = sorted(d.items(), key=lambda x: x[1]) # 根据字典值的升序排序
d_sorted_by_key
[('a', 2), ('b', 1), ('c', 10)]
d_sorted_by_value
[('b', 1), ('a', 2), ('c', 10)]

# set

  • 集合实现基于哈希表
  • 集合无序,不支持索引操作
  • 集合pop是删除集合中最后一个元素,但集合本身无序,因此无法确定删除的是哪一个元素
s = {3, 4, 2, 1}
sorted(s) # 对集合的元素进行升序排序
[1, 2, 3, 4]

# 字典和集合的相同与区别

相同

  • 都支持创建,访问,增加,删除,更新等操作

  • 实现本质上都是哈希表,经过高度性能优化

    • 字典的哈希表中存储了哈希值、键、值三个元素
    • 集合的哈希表中只有单一的元素
  • 由于哈希冲突的存在(哈希冲突会大大影响操作速度),因此字典和集合内的哈希表会保证其至少有1/3的剩余空间,当剩余空间小于1/3时就会重新获取更大的内存空间,扩充哈希表,并且重新排放所有元素。

不同

  • 字典有序(python37确定的特性),集合无序
  • 字典支持索引键,集合不支持索引操作(本质上是一个哈希表,和列表不一样)

# 字典和集合的工作原理

插入操作

首先计算键的哈希值(hash(key)),再和 mask = PyDicMinSize - 1 做与操作,计算这个元素应该插入哈希表的位置 index = hash(key) & mask,若对应位置为空则插入。

若对应位置不空,判断键是否相等,相等则更新,不等则继续寻找空位插入,

寻找空位最简单的方法是线性寻找,即当前位置开始向后逐个寻找。

查找操作

首先根据哈希值,找到其应该处于的位置;

然后,比较哈希表这个位置中元素的哈希值和键,与需要查找的元素是否相等。如果相等,则直接返回;如果不等,则继续查找,直到找到空位或者抛出异常为止。

删除操作

暂时对这个位置的元素,赋于一个特殊的值,等到重新调整哈希表的大小时,再将其删除。

# 列表和元组

# list

  • 空列表需要使用40字节来存储信息
  • 每次分配存储四个元素的空间来保证增加/删除的时间复杂度为O(1)

l = []
l.__sizeof__() // 空列表的存储空间为40字节
40
l.append(1)
l.__sizeof__() 
72 // 加入了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
l.append(2) 
l.__sizeof__()
72 // 由于之前分配了空间,所以加入元素2,列表空间不变
l.append(3)
l.__sizeof__() 
72 // 同上
l.append(4)
l.__sizeof__() 
72 // 同上
l.append(5)
l.__sizeof__() 
104 // 加入元素5之后,列表的空间不足,所以又额外分配了可以存储4个元素的空间

# tuple

与字符串一样,元组的元素不能修改。

元组与字符串类似,可以被索引且下标索引从0开始,-1 为从末尾开始的位置,也可以进行截取。其实,可以把字符串看作一种特殊的元组。

虽然tuple的元素不可改变,但它可以包含可变的对象,比如list列表。

# 列表和元组的异同

相同:

  • 都支持负数索引
  • 都支持切片操作
  • 都可以随意嵌套
  • 可以通过list()和tuple()函数相互转换

区别:

  • 列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素(mutable)。

  • 元组是静态的,长度大小固定,无法增加删减或者改变(immutable)。

  • 列表由于需要维护更多的信息(长度,当前指针等),存储空间略大于元组,性能略差于元组

  • 列表不再使用会触发垃圾回收机制,元组不会(内存空间会被缓存)

# 列表和元组在性能上的差别|python垃圾回收

由于python垃圾回收机制的存在,对于不再使用的变量,python会回收所占用的内存返回给操作系统,但对于静态变量例如元组,如果不被使用且占用空间不大时,python会缓存这部分内存。这样下次再创建同样大小的元组时,python就不会再向操作系统发出请求,去寻求内存,而是直接分配之前缓存的内存空间。

python3 -m timeit -s 'x=[1,2,3,4,5,6]' 'y=x[3]'
10000000 loops, best of 5: 22.2 nsec per loop
python3 -m timeit -s 'x=(1,2,3,4,5,6)' 'y=x[3]'
10000000 loops, best of 5: 21.9 nsec per loop

# 列表和元组如何选择

  1. 存储数据和数量不变,选用元组更合适
  2. 存储数据或数量可变,选用列表更合适

# 列表和元组的嵌套

针对可以随意嵌套进行总结:

  • 列表嵌套列表:本质是列表,内部列表和外部列表的内容可以进行修改元素,插入,删除元素。也就是二维数组。
  • 列表嵌套元组:本质是列表,所以可以对列表中除元组外的其他元素可以修改插入、删除。但元组中的内容不可以改变。
  • 元组嵌套列表:本质是元组,元组中的任何元素不能进行改变,但是对于元素本身是列表的情况,可以对列表中的值进行修改。这是因为:列表对象是不变的,只是的列表中的内容进行变化。列表本来就是动态的。
  • 元组嵌套元组:本质元组,元组中的元素还是元组。所以这种情况下,不能进行任何改变。也就是不可变的二维数组。