【python学习笔记】09 元祖、字典、集合 常用操作及字典底层原理

本系列为自己学习Python的笔记,如有误,欢迎大家指正

元祖tuple

列表属于可变序列,可以任意自古列表中的元素,元素属于不可变序列,不能修改元祖中的元素,因此,元祖没有增加元素、修改元素、删除元素等相关的方法。

元祖支持如下操作:

  1. 索引访问
  2. 切片操作
  3. 连接操作
  4. 成员关系操作
  5. 比较运算操作
  6. 计数:元祖长度len() 最大值max() 最小值min() 求和sum()

元祖的创建

  1. 通过()创建元祖,小括号可以省略

    a = (10,20,30) 或者 a = 10,20,30

    如果元祖只有一个元素,则必须后面加逗号,这是因为解释器会把(1)解释为整数1 (1,)解释为元祖。

    a = (1,)
    type(a)
    
  2. 通过tuple()创建

    tuple(可迭代的对象)

    a = tuple()
    b = tuple("a,b,c")
    c = tuple([2,3,4])
    d = tuple(range(3))
    

    总结:tuple()可以接收列表、字符串、其他序列类型、迭代器生成元祖。

    list()可以接收元祖、字符串、其他序列类型、迭代器等生成列表

元祖的元素访问和计数

  1. 元祖的元素不能修改

    a = (10,20,30,40)
    a[3] = 15 #报错 TypeError
    
  2. 元祖的元素访问和列表一样,只不过返回的仍然是元祖对象

    a = (10,20,30)
    a[1]
    a[0:2]
    
  3. 列表关于排序的方法list.sorted()是修改原列表对象,元祖没有该方法,如果要对元祖排序,只能使用内置函数sorted(tupleObj),并生成新的列表对象

zip

zip(列表1,列表2…)将多个列表对应的位置的元素组合成为元祖,并返回这个zip对象。

a = [10,20,30]
b = [40,50,60]
c = [70,80,90]
d = zip(a,b,c)
list(d)#[(10,40,70),(20,50,80),(30,60,90)]

生成器推导式创建元祖

从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号,列表推导式直接生成列表对象,生成器推导式生成的不是列表也不是元祖,而是一个生成器对象。

我们可以通过生成器对象,转换成列表或者元祖,也可以使用生成器对象的__next__()方法进行遍历,或者直接作为迭代器对象来使用。不管什么方法,元素访问结束后,如果需要重新访问其中的元素,必须重新创建该元素的生成器对象。

s = (x*2 for x in range(5))#迭代器对象
s
tuple(s)
list(s) #此时为空 因为只能访问一次
s = (x*2 for x in range(5))
s.__next__() #访问下一个元素

元祖总结

  1. 元祖的核心是不可变序列
  2. 元祖的访问和处理速度比列表块
  3. 与整数和字符串一样,元祖可以作为字典的键,列表则永远不能作为字典的键使用

字典

字典是键值对的无序可变序列,字典中的每个元素都是一个“键值对”,包含:“键对象”和“值对象”.可以通过键对象实现快速获取、删除、更新对应值对象。

列表中我们通过下标数字找到对应的对象,字典中通过键对象来找对应的值对象。键是任意的不可变对象,比如:整数 浮点数 字符串 元祖 ;但是列表、字典、激活这些可变对象,不能作为“键”,并且“键”不可重复。

值可以是任意的数据,并且可重复。

字典的创建

  1. 我们可以通过{} dict()来创建字典对象

    a = {
          
          'name':'slp','age':18,'job':'teacher'}
    b = dict(name='slp',age=18,job='teacher')
    a = dict([("name","18"),("age",18)])
    c = {
          
          } #空字典对象
    d = dict() #空字典对象
    
  2. 通过zip()创建字典对象

    k = ['name','age','job']
    v = ['slp',18,'teacher']
    d = dict(zip(k,v))
    
  3. 通过fromkeys创建值为空的字典

    a = dict.fromkeys(['name','age','job'])
    

字典元素的访问

  1. 通过键获得值,若键不存在,则抛出异常

    a = {
          
          'name':'slp','age':18}
    a['name'] #slp
    a['job'] #KeyError
    
  2. 通过get()方法获得值,推荐使用,有点是:指定键不存在,返回None,也可以设定指定键不存在时默认返回的对象

    a = {
          
          'name':'slp','age':18}
    a.get('name')
    
  3. 列出多有的键值对

    a.items()
    
  4. 列出所有的键,列出所有的值

    a.keys()
    a.values()
    
  5. len()键值对的个数

  6. 检测一个键是否在字典中

    'name' in a
    

字典元素添加、修改、删除

  1. 给字典新增“键值对”。如果“键”已经存在,则覆盖旧的值,如果不存在,则新增键值对

    a = {
          
          'name','slp'}
    a['address']='sx'
    
  2. 使用update()将新字典中所有键值对全部添加到旧字典上,如果key重复,则直接覆盖

    a = {
          
          'name':'slp','age':18}
    b = {
          
          'name':'hp','address':'sx'}
    a.update(b)
    
  3. 字典中元素的删除,可以使用del()方法,或者clear()删除所有键值对;pop()删除指定键值对,并返回对应的“值对象”

    a={
          
          'name':'slp',
     	'age':18}
    del(a['name'])
    
  4. popitem():随机删除和返回该键值对,字典是无序可变序列,因此没有第一个元素,最后一个元素的概念,popitem()弹出随机的项,因为字典并没有最后的元素或其他顺序概念,若想一个一个删除,则这个非常有效

    a = {
          
          'name':'slp','age':18}
    a.popitem()
    

序列解包

序列解包是可以用于元祖、列表、字典。序列解包可以让我们方便的对多个变量赋值、

x,y,z = (20,30,10)
x #20
(a,b,c)=(9,8,10)
a #9
[a,b,c]=[10,20,30]
c #30

序列解包用于字典时,默认是对键进行操作;如果需要对键值进行操作,则需要使用items(),如果需要对值进行操作,则需要使用values()

s = {
    
    'name':'slp','age':18}
name,age = s
name #'name'
name,age=s.items()
name#{'name','slp'}

例子

表格数据使用字典和列表存储,并实现访问

姓名 年龄 薪资 城市
高小一 18 30000 北京
高小二 19 20000 上海
高小五 20 10000 深圳
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 15 14:06:22 2021

@author: sangliping
"""

r1 = {
    
    'name':'高小一','age':18,'salary':30000,'city':'北京'}
r2 = {
    
    'name':'高小二','age':19,'salary':10000,'city':'上海'}
r3 = {
    
    'name':'高小五','age':20,'salary':10000,'city':'深圳'}
tb = [r1,r2,r3]
print(tb)
# 获得第二行人的薪资
print(tb[1].get('salary'))
#打印表中所有的薪资
for i in range(len(tb)):
    print(tb[i].get('salary'))
    
# 打印表的所有数据
for i in range(len(tb)):    
    print(tb[i].get('name'),tb[i].get('age'),tb[i].get('salary'),tb[i].get('city'))
    

字典核心底层原理

字典对象的核心是散列表,散列表是一个稀疏矩阵,数组的每个单元叫做bucket,每个bucket有两部分:一个是键对象的引用,一个是值对象的引用

由于所有bucket结构和大小一致,我们可以通过偏移量来读取指定bucket
在这里插入图片描述

将一个键值对放进字典的底层过程

a = {
    
    }
a['name']='slp'

假设字典a对象创建完成后,数组长度为8:
在这里插入图片描述

我们要把’name’='slp’这个键值对放到字典对象a中,首先第一步需要计算键的散列值,python中可以通过hash()来计算 bin(hash('name'))

由于数组长度为8,我们可以拿计算出的散列值的最右边3位数字作为偏移量,即101,十进制是5,我们查看偏移量5对应的bucket是否为空,如果为空,则将键值对放进去,如果不为空则依次取右边3位作为偏移量,也就是100,查看偏移量为4的bucket是否为空,直到找到为空的bucket将键值对放进去。
在这里插入图片描述

根据键查找“键值对”的底层过程

当我们调用a.get(‘name’)的时候,就是根据name查找到键值对,从而找到该对象。第一步仍然是计算散列值。

和存储的底层流程算法一致,也是依次取散列值的不同位置的数字。假设数组长度为8我们可以拿计算出的散列值的最右边3位数字作为偏移量,查看对应10进制位置的bucket是否为空如果为空返回None,如果不为空,则进行比较,如果相等,则作为值返回,如果不相等,则依次取其他几位进行比较。如果最后依然没有找到,则返回None.
在这里插入图片描述

总结:

  1. 键必须可散列
    • 数字 字符串 元祖都是可散列的
    • 自定义对象需要支持下面三点
      • 支持hash()函数
      • 支持__eq__()方法检测相等性
      • a==b为真,则hash(a)==hash(b)也为真
  2. 字典在内存中开销巨大,典型的空间换时间
  3. 键查询速度很快
  4. 往字典里面添加新键可能导致扩容,导致散列表中键的次序变化,因此,不用在遍历字典的同时进行字典的修改

集合

集合是无序可变,元素不能重复,实际上,集合底层是字典实现,集合的所有元素都是字典中的键对象,因此是不能重复且唯一的

集合创建和删除

  1. 使用{}创建集合对象,并使用add()方法添加元素

    a = {
          
          3,5,7}
    a.add(9)
    
  2. 使用set(),将列表、元组等可迭代对象转换成几个。如果眼里数据存在重复值,则只保留一个。

    a = ['a','b']
    b = set(a)
    
  3. remove()删除指定元素,clear()清空整个集合

集合相关操作

像数学中概念一样,python对集合也提供了并集、交集、差集等运算

a = {
    
    1,2}
b={
    
    's','l'}
a|b #并集
a&b #交集
a-b #差集
a.union(b) #并集
a.intersection(b) #交集
a.difference(b) #差集

微信搜一搜【梓莘】或扫描下方二维码交个朋友共同进步。文章持续更新中。目前在整理python百战学习笔记,期待后续更多的更新哦。在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_51656605/article/details/112667734
今日推荐