本文是在天池学习路线中的『python数据结构』板块, 之前学习过部分, 在此记录一些易错点和不熟悉的地方. 仅作补充, 并不是全部知识点.
详细知识点的所有代码均可在我的GitHub仓库TianChi-Studying中找到.
数据类型
常见的数据类型如下:
- 简单数据类型
- 整型
<class 'int'>
- 浮点型
<class 'float'>
- 布尔型
<class 'bool'>
- 整型
- 容器数据类型
- 列表
<class 'list'>
- 字典
<class 'dict'>
- 集合
<class 'set'>
- 元组
<class 'tuple'>
- 字符串
<class 'str'>
- 列表
在我们的数据类型中, 有些数据类型的不可修改的, 从而没有增删改查, 修改操作基本上都是重新创建对象后赋值.
- 可变类型(不可被hash)
- 列表、集合、字典
- 不可变类型(可被hash)
- 数值、字符、元组
检测类型是否可变, 可以使用
id()
观察修改前后地址是否发生变化, 但更常用的是使用hash()
, 不可哈希的将会被报错, 也就是是可变类型将会报错
列表 list
在list
中可以存放任意对象, 且由于存放的是对象指针, 所以即使是简单的[1,2,3]
中也存放了3个对象和3个指针.
列表可以进行如下乘法计算
1 | x = [0] * 5 |
以上结果符合期望, 而看下面这个例子
1 | x = [[0] * 3] * 4 |
x是将[0] * 3
这个对象复制了4遍, 所以x
列表中保存的是1个对象和4个指向该对象的指针!
那么对[0] * 3
对象进行修改的时候, 就会得到上述的结果. 这是一个小坑⚠, 需要注意一下.
浅拷贝与深拷贝
列表的浅拷贝与深拷贝是在编程时容易遇到的坑, 这里表述一下.
- 浅拷贝是: 简单的将元素对象完全复制过来.
- 深拷贝是: 创建新的元素对象, 仅复制原本的对象值.
1 | list1 = [1,2,[3,4],5] |
容易发现, 列表list2
, 采用直接赋值的方法, 相当于将原list1
中的所有元素对象地址直接复制过来, 那么两个列表完全等价.
而list3
是使用了切片的方法, 对列表元素的对象值进行了深拷贝, 所以未受到修改999
的影响. 但是其中的[3,4]
对象的是一个指针, 复制的依旧是指向该对象的指针, 所以修改777
的作用生效了.
除了切片, copy方法、列表推导式和
for+append
也同样的效果
也就是说, 这种深拷贝只能深拷贝一层, 更多层次的由指针指向的对象则只能浅拷贝.
那么想要进行『完全的深拷贝』, 创建一个与原对象值相同, 但毫无关系的对象, 则需要使用copy.deepcopy
方法进行拷贝.
1 | import copy |
列表排序
列表的排序可以使用list.sort()
或sorted
来实现, 前者会改变列表本身, 而后者会返回一个排序后的新列表对象.
1 | list1 = [[9,33,666], [1,44,666],[1,33,555]] |
当我们想要对列表进行排序的时候, python似乎不能像c++一样定义cmp, 实现一列升序一列降序的复杂排序, 若需如此则可以进行两次排序.
我找到了, 可以使用functools.cmp_to_key
来自定义cmp进行复杂比较. 只不过这是python 2.x
版本的旧方法, 在python 3.x
中被集成到了functools
工具中.
1 | from functools import cmp_to_key |
元组 tuple
单个元素需要加逗号, 与括号运算符区分开.
1 | print(8 * (8)) # 64 |
解压元组
使用多个变量来接收元组, 多余的可以全部使用*rest
来接收, 接收后为列表形式.
1 | t = (1, 2, 3, 4, 5, 'python') |
字符串 str
字符映射
将字符串中的部分字符替换为另外的字符, 可以联合使用maketrans
和translate
方法, maketrans
是得到一个可供translate
使用的字符映射表, 遵循Unicode
编码.
1 | str7 = 'this is string example....好!!!' |
格式化
格式化操作符的一些辅助指令
符号 | 功能 |
---|---|
m.n |
m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话) |
- |
用作左对齐 |
+ |
在正数前面显示加号( + ) |
# |
在八进制数前面显示零('0'),在十六进制前面显示'0x'或者'0X'(取决于用的是'x'还是'X') |
0 |
显示的数字前面填充'0'而不是默认的空格 |
下面是一些使用%
格式化的例子, 个人更喜欢使用f-string
格式化(python 3.6+), 这些指令也都可以使用.
1 | print('%5.1f' % 27.658) # ' 27.7' |
集合
集合操作就只整理一下数学中的一些操作.
操作符 | 函数 | 作用 |
---|---|---|
a&b |
a.intersection(b) |
交集 |
a|b |
a.union(b) |
并集 |
a-b |
a.difference(b) |
差集 |
a^b |
a.symmetric_difference(b) |
异或 |
a<=b |
a.issubset(b) |
被包含 |
a>=b |
a.issuperset(b) |
包含 |
不可变集合
集合本身是可变的数据类型, 但是也提供了不可变的集合, 即将集合冻结, 无法进行增删改等操作.
1 | a = frozenset('lsgogroup') |
enumerate和zip
这两个函数是针对可迭代对象的方法.
enumerate(sequence, [start=0])
用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列, 也就是给原本每一个元素增加一个从
start
开始的索引, 一般用在for
循环当中.1
2
3b = list(enumerate(seasons, 1))
print(b)
# [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
zip(iter1 [,iter2 [...]])
用于将可迭代的对象作为参数, 将对象中对应的元素打包成一个个元组, 然后返回由这些元组组成的对象, 可以节约内存.
返回的是一个不可打印的变量, 需要使用
list()
转换来输出列表.如果各个迭代器的长度不一致, 则返回与列表长度与最短的对象相同.
1
2
3
4
5
6
7
8
9a = [1, 2, 3]
b = ['a', 'b', 'c']
c = ['A', 'B', 'C', 'D', 'E']
zipped = zip(a, b)
print(zipped) # <zip object at 0x0000017880901D40>
print(list(zipped)) # [(1, 'a'), (2, 'b'), (3, 'c')]
zipped = zip(a, c)
print(list(zipped)) # [(1, 'A'), (2, 'B'), (3, 'C')]反过来, 可以利用
*
号操作符, 可以将元组解压为列表.1
2
3
4
5
6a = [1, 2, 3]
b = ['a', 'b', 'c']
a1, a2 = zip(*zip(a, b))
print(list(a1)) # [1, 2, 3]
print(list(a2)) # ['a', 'b', 'c']
参考
- 内容来自python训练营
- Python中List的复制(直接复制、浅拷贝、深拷贝)
- python官网-排序指南-使用 cmp 参数的旧方法
- https://blog.csdn.net/qq_39478403/article/details/105863783
个人收获
- 列表中的深浅拷贝做了一次详细的整理, 彻底捋清楚了. 以及在刷题时常用自定义排序
cmp
也学到了. - 还有字符串的格式化, 除了保留小数还有其他几种辅助指令.
- 对可变/不可变有了更深的理解.