『天池训练营』python基础入门——数据结构大汇总

本文是在天池学习路线中的『python数据结构』板块, 之前学习过部分, 在此记录一些易错点和不熟悉的地方. 仅作补充, 并不是全部知识点.

详细知识点的所有代码均可在我的GitHub仓库TianChi-Studying中找到.

本笔记为阿里云天池龙珠计划Python训练营的学习内容,链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython;

文章目录

    • 数据类型
    • 列表 list
      • 浅拷贝与深拷贝
      • 列表排序
    • 元组 tuple
      • 解压元组
    • 字符串 str
      • 字符映射
      • 格式化
    • 集合
      • 不可变集合
    • enumerate和zip
    • 参考
    • 个人收获

数据类型

常见的数据类型如下:

  • 简单数据类型
    • 整型
    • 浮点型
    • 布尔型
  • 容器数据类型
    • 列表
    • 字典
    • 集合
    • 元组
    • 字符串

在我们的数据类型中, 有些数据类型的不可修改的, 从而没有增删改查, 修改操作基本上都是重新创建对象后赋值.

  • 可变类型(不可被hash)
    • 列表、集合、字典
  • 不可变类型(可被hash)
    • 数值、字符、元组

检测类型是否可变, 可以使用id()观察修改前后地址是否发生变化, 但更常用的是使用hash(), 不可哈希的将会被报错, 也就是是可变类型将会报错

列表 list

list中可以存放任意对象, 且由于存放的是对象指针, 所以即使是简单的[1,2,3]中也存放了3个对象和3个指针.

列表可以进行如下乘法计算

x = [0] * 5
print(x, type(x)) # [0, 0, 0, 0, 0] 

x[1] = 3
print(x, type(x)) # [0, 3, 0, 0, 0] 

以上结果符合期望, 而看下面这个例子

x = [[0] * 3] * 4
print(x, type(x))
# [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]] 

x[0][0] = 1
print(x, type(x))
# [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]] 

x是将[0] * 3这个对象复制了4遍, 所以x列表中保存的是1个对象4个指向该对象的指针!

那么对[0] * 3对象进行修改的时候, 就会得到上述的结果. 这是一个小坑⚠️, 需要注意一下.

浅拷贝与深拷贝

列表的浅拷贝与深拷贝是在编程时容易遇到的坑, 这里表述一下.

  • 浅拷贝是: 简单的将元素对象完全复制过来.
  • 深拷贝是: 创建新的元素对象, 仅复制原本的对象值.
list1 = [1,2,[3,4],5]
list2 = list1
list3 = list1[:]

list1[0] = 999
list1[2][0] = 777
print(list2)  # [999, 2, [777, 4], 5]
print(list3)  # [1, 2, [777, 4], 5]

容易发现, 列表list2, 采用直接赋值的方法, 相当于将原list1中的所有元素对象地址直接复制过来, 那么两个列表完全等价.

list3是使用了切片的方法, 对列表元素的对象值进行了深拷贝, 所以未受到修改999的影响. 但是其中的[3,4]对象的是一个指针, 复制的依旧是指向该对象的指针, 所以修改777的作用生效了.

除了切片, copy方法、列表推导式和for+append也同样的效果

也就是说, 这种深拷贝只能深拷贝一层, 更多层次的由指针指向的对象则只能浅拷贝.

那么想要进行『完全的深拷贝』, 创建一个与原对象值相同, 但毫无关系的对象, 则需要使用copy.deepcopy方法进行拷贝.

import copy
list1 = [1,2,[3,4],5]
list2 = copy.deepcopy(list1)

list1[0] = 999
list1[2][0] = 777
print(list2)  # [1, 2, [3, 4], 5]

列表排序

列表的排序可以使用list.sort()sorted来实现, 前者会改变列表本身, 而后者会返回一个排序后的新列表对象.

list1 = [[9,33,666], [1,44,666],[1,33,555]]

list2 = sorted(list1,key=(lambda x: x[1]))
print(list2)  # 第2列升序 [[9, 33, 666], [1, 33, 555], [1, 44, 666]]

list3 = sorted(list1,key=(lambda x: [x[1],x[2]]))
print(list3)  # 第2列升序, 相同按第3列升序 [[1, 33, 555], [9, 33, 666], [1, 44, 666]]

当我们想要对列表进行排序的时候, python似乎不能像c++一样定义cmp, 实现一列升序一列降序的复杂排序, 若需如此则可以进行两次排序.

我找到了, 可以使用functools.cmp_to_key来自定义cmp进行复杂比较. 只不过这是python 2.x版本的旧方法, 在python 3.x中被集成到了functools工具中.

from functools import cmp_to_key

def mycmp(a, b):
    """
    按照第1列降序排序
    第1列相等按照第2列升序
    """
    if a[0] != b[0]:
        return a[1] < b[1]
    return a[0] > b[0]


list1 = [[98,88],[66,99],[77,44],[77,77]]
list2 = sorted(list1, key=cmp_to_key(mycmp))
print(list2)  # [[98, 88], [66, 99], [77, 44], [77, 77]]

元组 tuple

单个元素需要加逗号, 与括号运算符区分开.

print(8 * (8))  # 64
print(8 * (8,))  # (8, 8, 8, 8, 8, 8, 8, 8)

解压元组

使用多个变量来接收元组, 多余的可以全部使用*rest来接收, 接收后为列表形式.

t = (1, 2, 3, 4, 5, 'python')
a, b, *rest = t
print(a, b, rest)  # 1 2 [3, 4, 5, 'python']
# 1 10.31 python

字符串 str

字符映射

将字符串中的部分字符替换为另外的字符, 可以联合使用maketranstranslate方法, maketrans是得到一个可供translate使用的字符映射表, 遵循Unicode编码.

str7 = 'this is string example....好!!!'
intab = 'aeiou好'  # 被映射字符
outtab = '12345棒'  # 映射字符一一对应
trantab = str7.maketrans(intab, outtab)
print(trantab)  # {97: 49, 111: 52, 117: 53, 101: 50, 105: 51}
print(str7.translate(trantab))  # th3s 3s str3ng 2x1mpl2....w4w!!!

格式化

格式化操作符的一些辅助指令

符号 功能
m.n m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话)
- 用作左对齐
+ 在正数前面显示加号( + )
# 在八进制数前面显示零(‘0’),在十六进制前面显示’0x’或者’0X’(取决于用的是’x’还是’X’)
0 显示的数字前面填充’0’而不是默认的空格

下面是一些使用%格式化的例子, 个人更喜欢使用f-string格式化(python 3.6+), 这些指令也都可以使用.

print('%5.1f' % 27.658)  # ' 27.7'
print('%.2e' % 27.658)  # 2.77e+01
print('%10d' % 10)  # '        10'
print('%-10d' % 10)  # '10        '
print('%+d' % 10)  # +10
print('%#o' % 10)  # 0o12
print('%#x' % 108)  # 0x6c
print('%010d' % 5)  # 0000000005

集合

集合操作就只整理一下数学中的一些操作.

操作符 函数 作用
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) 包含

不可变集合

集合本身是可变的数据类型, 但是也提供了不可变的集合, 即将集合冻结, 无法进行增删改等操作.

a = frozenset('lsgogroup')
print(a)
# frozenset({'g', 's', 'p', 'r', 'u', 'o', 'l'})

enumerate和zip

这两个函数是针对可迭代对象的方法.

  • enumerate(sequence, [start=0])

    • 用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列, 也就是给原本每一个元素增加一个从start开始的索引, 一般用在for循环当中.

      b = list(enumerate(seasons, 1))
      print(b)  
      # [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
      
  • zip(iter1 [,iter2 [...]])

    • 用于将可迭代的对象作为参数, 将对象中对应的元素打包成一个个元组, 然后返回由这些元组组成的对象, 可以节约内存.

    • 返回的是一个不可打印的变量, 需要使用 list() 转换来输出列表.

    • 如果各个迭代器的长度不一致, 则返回与列表长度与最短的对象相同.

      a = [1, 2, 3]
      b = ['a', 'b', 'c']
      c = ['A', 'B', 'C', 'D', 'E']
      
      zipped = zip(a, b)
      print(zipped)  # 
      print(list(zipped))  # [(1, 'a'), (2, 'b'), (3, 'c')]
      zipped = zip(a, c)
      print(list(zipped))  # [(1, 'A'), (2, 'B'), (3, 'C')]
      
    • 反过来, 可以利用 * 号操作符, 可以将元组解压为列表.

      a = [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']
      

参考

  1. 内容来自python训练营
  2. Python中List的复制(直接复制、浅拷贝、深拷贝)
  3. python官网-排序指南-使用 cmp 参数的旧方法
  4. https://blog.csdn.net/qq_39478403/article/details/105863783

个人收获

  1. 列表中的深浅拷贝做了一次详细的整理, 彻底捋清楚了. 以及在刷题时常用自定义排序cmp也学到了.
  2. 还有字符串的格式化, 除了保留小数还有其他几种辅助指令.
  3. 对可变/不可变有了更深的理解.

你可能感兴趣的:(python,python,数据结构,开发语言)