python字典和集合——笔记

news2024/11/16 19:24:27

一、介绍
1、泛映射类型
collections.abc模块中有Mapping和MutableMapping这两个抽象基类,它们的作用是为dict和其他类似的类型定义形式接口(在Python 2.6到Python 3.2的版本中,这些类还不属于collections.abc模块,而是隶属于collections模块)。
在这里插入图片描述
然而,非抽象映射类型一般不会直接继承这些抽象基类,它们会直接对dict或是collections.UserDict进行扩展。
这些抽象基类的主要作用是作为形式化的文档,它们定义了构建一个映射类型所需要的最基本的接口。然后它们还可以跟isinstance一起被用来判定某个数据是不是广义上的映射类型。

isinstance(my_dict, abc.Mapping)
Out[138]: True

标准库里的所有映射类型都是利用dict来实现的,因此它们有个共同的限制,即只有可散列的数据类型才能用作这些映射里的键。

1.1如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,而且这个对象需要实现__hash__( )方法。另外可散列对象还要有__eq__( )方法,这样才能跟其他键做比较。如果两个可散列对象是相等的,那么它们的散列值一定是一样的……
原子不可变数据类型(str、bytes和数值类型)都是可散列类型,frozenset也是可散列的,因为根据其定义,frozenset里只能容纳可散列类型。元组的话,只有当一个元组包含的所有元素都是可散列类型的情况下,它才是可散列的。

tt=(1,2,[30,40])
t1=(1,2,(30,40))
hash(tt)
TypeError: unhashable type: 'list'
hash(t1)
Out[142]: -3907003130834322577

1.2 创建字典的不同方式

a = dict(one=1, two=2, three=3)
b = {'one':1,'two':2, 'three':3}
c = dict(zip(['one', 'two', 'three'], [1,2,3]))
d = dict([('two', 2), ('one', 1), ('three',3)])
e = dict({'three':3, 'one':1, 'two':2})
a==b==c==d==e
Out[148]: True

2、字典推导
字典推导(dictcomp)可以从任何以键值对作为元素的可迭代对象中构建出字典。下面就展示了利用字典推导可以把一个装满元组的列表变成了字典。

country = ['China', 'Brazil', 'Russia', 'Japan']
country_code={co:len(co) for co in country}
country_code
Out[154]: {'China': 5, 'Brazil': 6, 'Russia': 6, 'Japan': 5}

3、映射类型的常见方法

# 返回国家对应的值,没有的话返回-1
country_code.get('China',-1)
Out[155]: 5
country_code.get('USA',-1)
Out[156]: -1
# 返回country_code中的所有键值对
country_code.items()
Out[157]: dict_items([('China', 5), ('Brazil', 6), ('Russia', 6), ('Japan', 5)])
# 随机返回一个键值对,并从字典中移除它
country_code.popitem()
Out[158]: ('Japan', 5)
country_code
Out[159]: {'China': 5, 'Brazil': 6, 'Russia': 6}
# 返回brazil所对应的值,并删除这个键值对。 如果没有,则返回默认值
country_code.pop('Brazil')
Out[160]: 6
country_code.pop('Brazil',-1)
Out[161]: -1
country_code
Out[162]: {'China': 5, 'Russia': 6}

OrderedDict.popitem()会移除字典里最先插入的元素(先进先出);同时这个方法还有一个可选的last参数,若为真,则会移除最后插入的元素(后进先出)

4、映射的弹性键查询
有时候为了方便起见,就算某个键在映射里不存在,我们也希望在通过这个键读取值的时候能得到一个默认值。有两个途径能帮我们达到这个目的。

  • 一个是通过defaultdict这个类型而不是普通的dict
  • 另一个是给自己定义一个dict的子类,然后在子类中实现__missing__方法。
    4.1 defaultdict:处理找不到的键的一个选择
    在实例化一个defaultdict的时候,需要给构造方法提供一个可调用对象,这个可调用对象会在__getitem__碰到找不到的键的时候被调用,让__getitem__返回某种默认值。
    比如,我们新建了这样一个字典:dd=defaultdict(list),如果键’new-key’在dd中还不存在的话,表达式dd[‘new-key’]会按照以下的步骤来行事。
    (1)调用list( )来建立一个新列表。
    (2)把这个新列表作为值,'new-key’作为它的键,放到dd中。
    (3)返回这个列表的引用。
    而这个用来生成默认值的可调用对象存放在名为default_factory的实例属性里。
import collections
names = collections.defaultdict(list)
names['1班'].append('李明')
names
Out[166]: defaultdict(list, {'1班': ['李明']})

names.get('2班').append('李明')
Traceback (most recent call last):
AttributeError: 'NoneType' object has no attribute 'append'

defaultdict里的default_factory只会在__getitem__里被调用,在其他的方法里完全不会发挥作用。比如,dd是个defaultdict,k是个找不到的键, dd[k]这个表达式会调用default_factory创造某个默认值,而dd.get(k)则会返回None。

所有这一切背后的功臣其实是特殊方法__missing__。它会在defaultdict遇到找不到的键的时候调用default_factory,而实际上这个特性是所有映射类型都可以选择去支持的
4.2 特殊方法__missing__
所有的映射类型在处理找不到的键的时候,都会牵扯到__missing__方法。这也是这个方法称作“missing”的原因。虽然基类dict并没有定义这个方法,但是dict是知道有这么个东西存在的。也就是说,如果有一个类继承了dict,然后这个继承类提供了__missing__方法,那么在__getitem__碰到找不到的键的时候,Python就会自动调用它,而不是抛出一个KeyError异常。
missing__方法只会被__getitem__调用(比如在表达式d[k]中)。提供__missing__方法对get或者__contains_(in运算符会用到这个方法)这些方法的使用没有影响。这也是我在上一节最后的警告中提到,defaultdict中的default_factory只对__getitem__有作用的原因。

示例 StrKeyDict0在查询的时候把非字符串的键转换为字符串

class StrKeyDict0(dict):
    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError(key)
        return self[str(key)]
    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default
d = StrKeyDict0([('2', 'two'),('4', 'four')])
d['2']
Out[170]: 'two'
d[4]
Out[171]: 'four'
d[1]
Traceback (most recent call last):
KeyError: '1'
d.get('2')
Out[173]: 'two'
d.get(4)
Out[174]: 'four'
d.get(1, 'N/A')
Out[175]: 'N/A'

5、字典的变种
这一节总结了标准库里collections模块中,除了defaultdict之外的不同映射类型。
5.1 collections.OrderedDict:这个类型在添加键的时候会保持顺序,因此键的迭代次序总是一致的。OrderedDict的popitem方法默认删除并返回的是字典里的最后一个元素,但是如果像my_odict.popitem(last=False)这样调用它,那么它删除并返回第一个被添加进去的元素。
5.2 collections.ChainMap:该类型可以容纳数个不同的映射对象,然后在进行键查找操作的时候,这些对象会被当作一个整体被逐个查找,直到键被找到为止。这个功能在给有嵌套作用域的语言做解释器的时候很有用,可以用一个映射对象来代表一个作用域的上下文。在collections文档介绍ChainMap对象的那一部分里有一些具体的使用示例,其中包含了下面这个Python变量查询规则的代码片段:

import collections
chain_map = collections.ChainMap({'a':1, 'b':2},{'c':3},{'d':4})
all_dict = dict(chain_map)
all_dict
Out[5]: {'d': 4, 'c': 3, 'a': 1, 'b': 2}

5.3 Counter:这个映射类型会给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。所以这个类型可以用来给可散列表对象计数,或者是当成多重集来用——多重集合就是集合里的元素可以出现不止一次。Counter实现了+和-运算符用来合并记录,还有像most_common([n])这类很有用的方法。

ct = collections.Counter('ababdadfawwwwweijisafl')
ct
Out[7]: Counter({'a': 5, 'b': 2,'d': 2,'f': 2,'w': 5,'e': 1, 'i': 2,'j': 1,'s': 1, 'l': 1})
ct.update('ddddddeeeeeee')
ct
Out[9]: Counter({'a': 5, 'b': 2, 'd': 8, 'f': 2, 'w': 5,'e': 8,'i': 2,'j': 1,'s': 1,'l': 1})
ct.most_common(3)
Out[10]: [('d', 8), ('e', 8), ('a', 5)]

5.4 collections.UserDict:就创造自定义映射类型来说,以UserDict为基类,总比以普通的dict为基类要来得方便。

import collections
class StrKeyDict(collections.UserDict):
    def __missing__(self, key):
        if isinstance(key,str):
            raise KeyError(key)
        return self[str(key)]
    def __contains__(self, key):
        return str(key) in self.data
    def __setitem__(self, key, item):
        self.data[str(key)] = item

因为UserDict继承的是MutableMapping,所以StrKeyDict里剩下的那些映射类型的方法都是从UserDict、MutableMapping和Mapping这些超类继承而来的。特别是最后的Mapping类,它虽然是一个抽象基类(ABC),但它却提供了好几个实用的方法。以下两个方法值得关注。

  • MutableMapping.update:这个方法不但可以为我们所直接利用,它还用在__init__里,让构造方法可以利用传入的各种参数(其他映射类型、元素是(key, value)对的可迭代对象和键值参数)来新建实例。因为这个方法在背后是用self[key]=value来添加新值的,所以它其实是在使用我们的__setitem__方法。
  • Mapping.get:在StrKeyDict0中,我们不得不改写get方法,好让它的表现跟__getitem__一致。而在示例3-8中就没这个必要了,因为它继承了Mapping.get方法,

6、不可变映射类型
标准库里所有的映射类型都是可变的,但有时候你会有这样的需求,比如不能让用户错误地修改某个映射。从Python 3.3开始,types模块中引入了一个封装类名叫MappingProxyType。如果给这个类一个映射,它会返回一个只读的映射视图。虽然是个只读视图,但是它是动态的。这意味着如果对原映射做出了改动,我们通过这个视图可以观察到,但是无法通过这个视图对原映射做出修改。

from types import MappingProxyType
d = {1:'A'}
d_proxy = MappingProxyType(d)
d_proxy
Out[12]: mappingproxy({1: 'A'})
d_proxy[1]
Out[13]: 'A'
d_proxy[2] = 'b'
Traceback (most recent call last):
TypeError: 'mappingproxy' object does not support item assignment
d[2] = 'c'
d_proxy
Out[16]: mappingproxy({1: 'A', 2: 'c'})

7、集合论
集合的本质是许多唯一对象的聚集。因此,集合可以用于去重

l = ['spam', 'spam', 'eggs', 'spam']
set(l)
Out[18]: {'eggs', 'spam'}
list(set(l))
Out[19]: ['spam', 'eggs']

除了保证唯一性,集合还实现了很多基础的中缀运算符。给定两个集合a和b,a | b返回的是它们的合集,a & b得到的是交集,而a-b得到的是差集。

7.1 集合字面量
除空集之外,集合的字面量——{1}、{1, 2},等等——看起来跟它的数学形式一模一样。如果是空集,那么必须写成set( )的形式。
7.2 集合操作
集合的数学运算:这些方法或者会生成新集合,或者会在条件允许的情况下就地修改集合。

# 定义集合
a = {1, 2, 3, 4, 5, 6}
b = {4,5,6,7,8,9,20}
# 求交集操作
a.__and__(b)
Out[25]: {4, 5, 6}
a&b
Out[26]: {4, 5, 6}
# 把a更新为a与b的交集
a.__iand__(b)
Out[28]: {4, 5, 6}
a
Out[29]: {4, 5, 6}
a &= b
Out[30]: {4, 5, 6}
# a和b的并集
a|b
Out[30]: {4, 5, 6, 7, 8, 9, 20}
a.__or__(b)
Out[32]: {4, 5, 6, 7, 8, 9, 20}
# 把a更新为a和b的并集
a.__ior__(b)
Out[33]: {4, 5, 6, 7, 8, 9, 20}
a
Out[34]: {4, 5, 6, 7, 8, 9, 20}
# 求a和b的差集
a -b
Out[37]: set()
a.__sub__(b)
Out[38]: set()
# 把a更新为a和b的差集
a -= b
a
Out[40]: set()

8、set和dict的背后
8.1 字典中的散列表
散列表其实是一个稀疏数组(总是有空白元素的数组称为稀疏数组)。
因为Python会设法保证大概还有三分之一的表元是空的,所以在快要达到这个阈值的时候,原有的散列表会被复制到一个更大的空间里面。如果要把一个对象放入散列表,那么首先要计算这个元素键的散列值。Python中可以用hash( )方法来做这件事情,接下来会介绍这一点。

  • 内置的hash( )方法可以用于所有的内置类型对象。如果是自定义对象调用hash( )的话,实际上运行的是自定义的__hash__。如果两个对象在比较的时候是相等的,那它们的散列值必须相等,否则散列表就不能正常运行了。例如,如果1==1.0为真,那么hash(1)==hash(1.0)也必须为真,但其实这两个数字(整型和浮点)的内部结构是完全不一样的。
  • 如果search_key和found_key不匹配的话,这种情况称为散列冲突。发生这种情况是因为,散列表所做的其实是把随机的元素映射到只有几位的数字上,而散列表本身的索引又只依赖于这个数字的一部分。为了解决散列冲突,算法会在散列值中另外再取几位,然后用特殊的方法处理一下,把新得到的数字再当作索引来寻找表元。[插图]若这次找到的表元是空的,则同样抛出KeyError;若非空,或者键匹配,则返回这个值;或者又发现了散列冲突,则重复以上的步骤。
    8.2 dict的实现及其导致的结果
    8.2.1 下面的内容会讨论使用散列表给dict带来的优势和限制都有哪些。
  • 键必须是可散列的: 一个可散列的对象必须满足以下要求。(1)支持hash( )函数,并且通过__hash__( )方法所得到的散列值是不变的。(2)支持通过__eq__( )方法来检测相等性。(3)若a==b为真,则hash(a)hash(b)也为真。所有由用户自定义的对象默认都是可散列的,因为它们的散列值由id( )来获取,而且它们都是不相等的。
    **如果你实现了一个类的__eq__方法,并且希望它是可散列的,那么它一定要有个恰当的__hash__方法,保证在a
    b为真的情况下hash(a)==hash(b)也必定为真。否则就会破坏恒定的散列表算法,导致由这些对象所组成的字典和集合完全失去可靠性,这个后果是非常可怕的。另一方面,如果一个含有自定义的__eq__依赖的类处于可变的状态,那就不要在这个类中实现__hash__方法,因为它的实例是不可散列的**
  • 字典在内存上的开销巨大:由于字典使用了散列表,而散列表又必须是稀疏的,这导致它在空间上的效率低下。
  • 键查询很快:dict的实现是典型的空间换时间:字典类型有着巨大的内存开销,但它们提供了无视数据量大小的快速访问——只要字典能被装在内存里。
  • 键的次序取决于添加顺序:当往dict里添加新键而又发生散列冲突的时候,新键可能会被安排存放到另一个位置。于是下面这种情况就会发生:由dict([(key1, value1), (key2,value2)])和dict([(key2,value2), (key1, value1)])得到的两个字典,在进行比较的时候,它们是相等的;但是如果在key1和key2被添加到字典里的过程中有冲突发生的话,这两个键出现在字典里的顺序是不一样的。
    8.3 set的实现以及导致的结果
    set和frozenset的实现也依赖散列表,但在它们的散列表里存放的只有元素的引用(就像在字典里只存放键而没有相应的值)。在set加入到Python之前,我们都是把字典加上无意义的值当作集合来用的。
  • 集合里的元素必须是可散列的
  • 集合很消耗内存。
  • 可以很高效地判断元素是否存在于某个集合。
  • 元素的次序取决于被添加到集合里的次序。
  • 往集合里添加元素,可能会改变集合里已有元素的次序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/390805.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【震撼发布】《致敬未来的攻城狮计划》| 文末赠书3本

《致敬未来的攻城狮计划》—— 文末有福利 摘要: 一个崭新的计划,寻找那群有志于向嵌入式发展的未来工程师! 文章目录1 活动计划初衷2 活动计划形式3 活动计划收获4 活动计划要求5 活动计划时间6 活动计划致谢7 活动计划特别说明8 温馨提示9 …

Kerberos 域委派攻击之非约束性委派

CSDN文章自动迁移自博客在Windows 2000 Server 首次发布 Active Directory 时,Microsoft 必须提供一种简单的机制来支持用户通过 Kerberos 向 Web Server 进行身份验证并需要代表该用户更新后端数据库服务器上的记录的方案。这通常称为“Kerberos 双跳问题”&#x…

零入门kubernetes网络实战-20->golang编程syscall操作tun设备介绍

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 本篇文章主要是使用golang自带的syscall包来创建tun类型的虚拟网络设备。 注意: 目前只能使用syscall包来创建tun类型的虚拟设备。 tun虚拟网…

【RockerMQ】003-Windows 安装 RocketMQ

【RockerMQ】003-Windows 安装 RocketMQ 一、准备工作 1、环境要求 64位JDK 1.8;Maven 3.2.x;64位操作系统系统,本文档在Windows上安装 2、下载解压 下载地址 https://archive.apache.org/dist/rocketmq/5.1.0/ 下载目标 解压 到不含中文路径的目录下 环境变…

Python进阶-----面对对象7.0(细谈__new__方法和__init__方法)

目录 前言: __init__方法 __new__方法(重点!) 1.__new__方法的调用过程 2.重写__new__方法 3.__new__方法不同返回值的情况 3.单例模式 前言: 上一期初步介绍了__new__()方法,但是实际上这个方法还有非常多的内…

操作系统——18.进程互斥的软件实现方法

这篇文章我们来讲一下进程互斥的软件实现方法 目录 1.概述 2.单标志法 3.双标志检查法 4.双标志后检查法 5.Perterson算法 6.小结 1.概述 首先,我们来看一下这节的大体框架 学习提示: 理解各个算法的思想、原理结合上小节学习的“实现互斥的四个逻辑部分”…

通用业务平台设计(五):预警平台建设

前言 在上家公司,随着业务的不断拓展(从支持单个国家单个主体演变成支持多个国家多个主体),对预警的诉求越来越紧迫;如何保障业务的稳定性那?预警可以帮我们提前甄别风险,从而让我们可以在风险来临前将其消灭&#xff…

深度学习笔记:dropout和调优超参数方法

1 Dropout Dropout是一个常用于深度学习的减轻过拟合的方法。该方法在每一轮训练中随机删除部分隐藏层神经元。被删除的神经元不会进行正向或反向信号传递。在测试阶段所有神经元都会传递信号,但对各个神经元的输出要乘以训练时删除比例。 Dropout实现程序&#xf…

毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测

基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测1、项目简介1.1 系统构成1.2 系统功能2、部分电路设计2.1 STM32F103C8T6核心系统电路设计2.2 光敏采集电路设计2.3 温度采集电路设计3、部分代码展示3.1 读取DS18B20温度值3.2 定时器初始化1、项目简介 选题指导&#xff0c…

Learning Typescript and React in ts

目录 Basic typescript What is typescript? Configuring the TypeScript Compiler Fundamental build in types TypeScript Simple Types TypeScript Special Types Type: unknown Type: never Type: undefined & null Arrays Tuple Enums functions Ob…

Java集合专题

文章目录框架体系CollectionListArrayListLinkedListVectorSetHashSetLinkedHashSetTreeSetMapHashMapHashtableLinkedHashMapTreeMapPropertiesCollections框架体系 1、集合主要分了两组(单列集合,双列集合) 2、Collection接口有两个重要的子…

2.SpringSecurity认证

2.1登录校验流程 2.2认证原理 *源码流程: *自定义认证流程: *校验流程: *认证和校验连接: 2.3思路分析 *登录:

SQLI-Labs通关(2)5-7关

跟之前一样首先传参,然后查看注入点以及闭合 用and 11 and 12都没问题,接下来测试单引号 利用 and 12的时候会报错 利用order by来判断列数 得出一共三列 接下来就是联合查询 但是这个并不会回显 那么就利用盲注或者报错注入 在这里我们利用报错来测…

Vue3的生命周期函数

文章目录🌟 写在前面🌟 生命周期钩子函数🌟 组合式API生命周期🌟 写在最后🌟 写在前面 专栏介绍: 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章,应粉丝要求开始更新 Vue3 的相关技术文章&am…

OPPO 数据恢复:如何从 OPPO 手机恢复已删除的文件?

Oppo 手机以其精美的外观和拍摄的精美照片和视频而闻名。如果您不小心丢失了 OPPO 手机中珍贵的照片、视频等重要文件,并且为如何找回而苦恼,那么您来对地方了。我们其实有很多OPPO数据恢复方案,现在最重要的是尽快尝试这些方法,防…

Git 相关内容

目录 Git 相关流程和常用命令 Git workflow Git hooks Git 相关流程和常用命令 Git远程操作详解 - 阮一峰的网络日志 Git 使用规范流程 - 阮一峰的网络日志 常用 Git 命令清单 - 阮一峰的网络日志 Git workflow 啥玩意: 就是一个工作流程。可以比喻成一个河流…

用逻辑回归制作评分卡

目录 一.评分卡 二.导库,获取数据 三.探索数据与数据预处理 1.去除重复值 2.填补缺失值 3.描述性统计处理异常值 4.为什么不统一量纲,也不标准化数据分布 5.样本不均衡问题 6.分训练集和测试集 三.分箱 1.分多少个箱子才合适 2.分箱要达成什么…

Antlr4: 为parser rule添加label

1. parser rule中的label 1.1 简介 Antrl4语法文件Calculator.g4,stat和expr两个parser rule含有多个rule element,我们这两个parse rule的每个rule element添加了Alternative labels(简称label) 按照Antlr4的语法规则&#xff…

2022年显卡性能跑分排名表

2022年显卡性能跑分排名表(数据来源于快科技)这个版本的电脑显卡跑分榜第一的是NVIDIA GeForce RTX 3090 Ti显卡。由于显卡跑分受不同的测试环境、不同的显卡驱动版本以及不同散热设计而有所不同,所以显卡跑分会一直变化。 前二十名的台式电…

Linux(进程概念详解)

进程是如今编程领域非常重要的一个概念,进程是比较抽象的,不容易直接理解。因为进程与操作系统息息相关,因此在介绍进程之前,笔者打算先简易讲一下操作系统的工作流程,理解操作系统是如何管理软件和硬件的,…