【Python】Dict(字典)和 Set(集合)

字典(Dict)

一.介绍

Dict(字典)与Java中的Map接口相似,很多想法可以直接转移过来。

创建dict:

hash1={ }
hash2=dict()

key要是不可变的数据类型,但是value可以是任意一种python类型。

下面例子中使用的数据都是这一个:

hash={
    "id":1,
    "name":"Tom"
}

二.常用方法

clear()删除字典内所有元素
copy()返回一个字典的浅复制
fromkeys(seq[,val)创建一个新字典,以序列 seq 中元素做字典的键,val 为字典所有键对应的初始值
get(key)返回指定键的值,如果值不在字典中返回default值
has_key(key)如果键在字典dict里返回true,否则返回false。Python3 不支持。
items()以列表返回可遍历的(键, 值) 元组数组
keys()以列表返回一个字典所有的键
setdefault(key)和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default
update(dict2)把字典dict2的键/值对更新到dict里
values()以列表返回字典中的所有值
pop(key[,default])删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。
popitem()返回并删除字典中的最后一对键和值

除了使用一些方法,也可以直接使用“下标”这种方式访问或操作元素:

# in 判断in前面的key是不是在集合中
print("id" in hash)
# 通过key取value
print(hash["id"])

# 如果新增的key没在集合中,添加新的
hash["class"]=1
# 如果新增的key已经存在在集合中,更新key的value
hash["id"]=2

使用小技巧:字典解包

举一个连接数据库的例子方便理解:

config = {
    "host": "localhost",  # 数据库主机地址(本地一般为localhost)
    "port": 3306,  # 端口号(默认3306)
    "user": "root",  # 用户名
    "password": "123456",  # 密码(替换为你的实际密码)
    "database": "test",  # 要连接的数据库名(需提前创建)
    "charset": "utf8mb4"  # 字符集,支持中文
}

def connect_db():
    try:
        # 建立连接
        conn = pymysql.connect(**config)
    except pymysql.MySQLError as e:
        print(f"数据库连接失败:{e}")

"""
conn = pymysql.connect(**config)等价于:
conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="123456",
    database="test_db",
    charset="utf8mb4"
)
"""

三.Pythonic技巧

1.在检索 dict 值时提供默认参数

先看一下根据 key 获取 value 的两种方式:

# 第一种
value1 = hash["id"]
print(value1)

# 第二种
value2 = hash.get("name")
print(value2)

对于第一种,如果说字典里没有这个 key,那么会直接报错;

对于第二种,如果说字典里没有这个 key,那么获取到的 value 就是 None。

但是在应用,如果我们想要检索 dict 值,存在就取这个 key 的 value,不存在就取一个默认值。

如果使用第二种方式获取,那么要写一些逻辑判断:

data = None

if "name" in hash:
    data = hash["name"]
else:
    data = "Default"

但是,我们可以使用提供默认参数的方法来简化:

data = hash.get("name","Default")

这样写是不是就简洁很多了。

2.字典模拟 switch-case

python 中没有内置的 switch-case 语句,如果想要模拟其效果,就要使用 if-elif-else 来实现。

其实可以使用字典来实现的,核心思路如下:将 case 条件作为字典的 key,将 case 对应的执行逻辑(如函数、表达式)作为字典的 value,通过 “键查找” 替代 case 匹配,实现分支跳转。

def switch_calc_short(operation, a, b):
    calc_dict = {
        "+": lambda x, y: x + y,
        "-": lambda x, y: x - y,
        "*": lambda x, y: x * y,
        "/": lambda x, y: x / y if y != 0 else "除数不能为0"
    }
    return calc_dict.get(operation, lambda x, y: "无效操作符")(a, b)

print(switch_calc_short("*", 3, 4))

3.通过字典推导来优化 dict 构造

字典推导式的语法如下:

# 语法:{key表达式: value表达式 for 变量 in 可迭代对象 if 条件判断}
new_dict = {k: v for 变量 in 可迭代对象 if 条件}

举个例子:

list_of_users =[User('KaKa',"kaka@local"),User('JoJo',"jojo@local")]
# 循环
emails_for_user ={}
for user in list_of_users:
    if user.email:
        emails_for_user[user.name]=user.email
# 推导式
emails_for_user = {user.name: user.email for user in list_of_users if user.email}

4.排序字典的方法

根据键、值或其他相关属性的某些条件来实现字典中元素的自定义排序。

但是用注意,字典本身是键值对,排序需要将其转化为可迭代的键/值/键值对序列(使用items()),再用 sorted() 。

可以使用 sorted 函数进行排序,默认是根据key进行排序:

d={'a':400,'c':200,'b':300,'d':100}
print(sorted(d.items()))
# output: [('a', 400), ('b', 300), ('c', 200), ('d', 100)]

根据value进行排序:

# 正序
print(sorted(d.items(),key=lambda x:x[1]))
print(sorted(d.items(),key=operator.itemgetter(1)))
# 逆序
print(sorted(d.items(),key=lambda x:x[1],reverse=True))
print(sorted(d.items(),key=operator.itemgetter(1),reverse=True))

想要实现自定义排序就要使用lambda表达式

5.合并字典的方法

合并字典最简单的方法就是使用update(),update()会遍历字典,然后将这个字典里的内容添加到结果字典里,这也说明了一件事,如果存在重复的key,那么只会保留最后的value值。

fruit_price_a = {
    "apple": 2.5,
    "banana": 1.2,
    "cherry": 3.0}

fruit_price_b = {
    "banana": 1.0,
    "dragonfruit": 5.0,
    "elderberry": 4.0}

combined_fruit_price = {}
combined_fruit_price.update(fruit_price_a)
combined_fruit_price.update(fruit_price_b)
print(combined_fruit_price)
# Output: {'apple': 2.5, 'banana': 1.0, 'cherry': 3.0, 'dragonfruit': 5.0, 'elderberry': 4.0}

这里就可以注意到,香蕉的价格是1而不是1.2。

除了使用update(),还可以使用内置的 dict() 方法合并几个字典,并使用 ** 运算符来解包对象。

combined_fruit_price = {**fruit_price_a, **fruit_price_b}
print(combined_fruit_price)

使用 ** 运算符还可以在大型字典的情况下加快速度,因为它在语言结构本身中进行了优化。

6.优雅打印字典

第一种,使用 json.dumps() :

print(json.dumps(book, indent=4, sort_keys=False))

json 包中的方法仅适用于 dict 仅包含原始数据类型的情况,如果在字典中存储诸如函数之类的实体,就不能处理了。使用 json.dumps() 的另一个缺点是它不能字符串化复杂的数据类型,比如集合。

第二种,使用 pprint :

book = {'name': 'The Great Gatsby',
        'author': 'F. Scott Fitzgerald',
        'year': 1925,'info': {'publisher': 'Charles Scribner\'s Sons',
                             'pages': 218,
                             'language': 'English'}}
pprint(book, indent=4,sort_dicts=False)

与 json.dumps() 相比,pprint 在视觉上并不能很好地表示嵌套结构。

四.dict 子类

1.defaultdict 带默认值字典

defaultdict 是 python 标准库 collections 提供的增强版字典,与普通字典不同的是,其在访问不存在的键时会自动生成一个默认值。

这里不要和普通字典的 get 方法设置默认值弄混,get方法是当访问一个不存在的 key 时,会返回默认值,但是不会创建新的 key。而使用 defaultdict 时会自动创建一个新的 key。

对于一些统计分组处理数据,使用 defaultdict 比较方便:

# 统计字符串s中每个字符出现的次数
s = "abcdefg"
d = defaultdict(int)
for key in s:
    d[key]+=1
print(d)

2.OrderedDict 有序字典

在 Python 2 和更早的版本中,字典被实现为简单的哈希表,可以存储键值对。当检索键列表时,无法保证或指定它们返回的顺序。在这种情况下,Python 中标准库的集合模块提供了 OrderedDict 实现,该实现保留了字典中插入元素的顺序,并在检索键时返回相同的顺序。

在 Python 3 及更高版本中,排序功能已包含在标准 dict 实现中。 Python 3 中的字典保留了元素的插入顺序。

3.ChainMap 链式字典

ChainMap 将多个字典穿成一条链。

使用 ChainMap 进行查找时,从左到右依次搜索底层字典,直到找到键。如果有多次出现,则只返回第一次出现。插入、更新和删除仅影响添加到链中的第一个映射。

下面举个例子对上面所说进行测试:

# 定义
m1 = {'a': 1, 'b': 2, 'c': 13}
m2 = {'c': 3, 'd': 4, 'e': 5}
cmap = ChainMap[m1, m2]
# 如果有多次出现,则只返回第一次出现的值
print(cmap['c'])  # 输出 13
# 插入、更新和删除仅影响添加到链中的第一个映射
cmap['f'] = 6
cmap['b'] = 20
del cmap['c']
print(cmap)  # 输出: ChainMap({'a': 1, 'b': 20, 'f': 6}, {'c': 3, 'd': 4, 'e': 5})

4.Counter 计数器

对可迭代对象的元素频次进行统计,大量简化了代码

lst = ["a", "b", "a", "c", "a", "b", "d"]
cnt = Counter(lst)
print(cnt)  # 输出:Counter({'a': 3, 'b': 2, 'c': 1, 'd': 1})

下面介绍几个常用的方法:

1)most_common()

返回前 n 个出现次数最多的元素及频次,默认返回所有元素(按频次降序排列)

cnt = Counter(["a", "b", "a", "c", "a", "b", "d"])
# 获取前 2 个高频元素
print(cnt.most_common(2))  # 输出:[('a', 3), ('b', 2)]

2)elements()

生成一个迭代器,按元素的频次重复生成元素

cnt = Counter(a=3, b=2, c=1)
# 转为列表查看
elements = list(cnt.elements())
print(elements)  # 输出:['a', 'a', 'a', 'b', 'b', 'c'](顺序按插入顺序,频次对应重复次数)

3)update()

cnt = Counter(a=2, b=1)
# 传入可迭代对象,累加频次
cnt.update(["a", "c", "a"])  # "a" 频次 +2,"c" 新增(频次 1)
print(cnt)  # 输出:Counter({'a': 4, 'b': 1, 'c': 1})

4)subtract()

cnt = Counter(a=4, b=3, c=1)
# 传入可迭代对象,递减频次
cnt.subtract(["a", "b", "b"])  # "a" 频次 -1,"b" 频次 -2
print(cnt)  # 输出:Counter({'a': 3, 'b': 1, 'c': 1})

集合(Set)

一.介绍

Set(集合)跟Java中的Set接口类似,可类比学习。

创建Set:

set_test = {'a','b','c'}
set_test = set()

这里要注意创建一个空集,需要调用 set() 构造函数。使用空花括号 {} 是不明确的,其会创建一个空字典(Dict)。

通过打印,我们发现,打印出的结果不是按照要定义的顺序来的:

二.常用方法

add()为集合添加元素
clear()移除集合中的所有元素
copy() 拷贝一个集合
difference()返回多个集合的差集
difference_update()移除集合中的元素,该元素在指定的集合也存在
discard()删除集合中指定的元素
intersection()返回集合的交集
intersection_update()返回集合的交集
isdisjoint()判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False
issubset()判断指定集合是否为该方法参数集合的子集
issuperset()判断该方法的参数集合是否为指定集合的子集
pop()随机移除元素
remove()移除指定元素
symmetric_difference()返回两个集合中不重复的元素集合
symmetric_difference_update()移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中
union()返回两个集合的并集
update()给集合添加元素
len()计算集合元素个数

三.Pythonic技巧

1.集合推导

数据量越大,使用推导式的优势越大。

age = [1,2,3,4,5]
set_age={i for i in age if i%2==0}
print(set_age)
# Output: {2, 4}

2.集合高效检测列表

set_test1 = {'a','b','c'}
set_test2 = {'b','c','d'}

并集:A 和 B 中的元素(语法:A | B)

print(set_test1 | set_test2)
# output: {'a', 'b', 'c', 'd'}

交集:A 和 B 共有的元素(语法:A & B)

print(set_test1 & set_test2)
# output: {'c', 'b'}

差集:A 中的元素但 B 中没有的元素(语法: A – B ,不可交换)

print(set_test1 - set_test2)
# Output: {'a'}

对称差异:A 或 B 中的元素排除掉共同元素(语法:A^B)

print(set_test1 ^ set_test2)
# Output: {'a', 'd'}

3.frozenset 创建不可变集合

frozenset 是 Python 内置的不可变集合类型,可以理解成set的只读版本。因为 frozenset 是不可变的,可哈希,因此其可以当作 dict 的key。

创建 frozenset :

# 1. 空 frozenset
fs1 = frozenset()
print(fs1)  # 输出:frozenset()

# 2. 从列表创建(自动去重)
fs2 = frozenset([1, 2, 2, 3, 4])
print(fs2)  # 输出:frozenset({1, 2, 3, 4})

# 3. 从字符串创建(按字符去重)
fs3 = frozenset("abacbc")
print(fs3)  # 输出:frozenset({'a', 'b', 'c'})

# 4. 从集合创建
s = {3, 4, 5}
fs4 = frozenset(s)
print(fs4)  # 输出:frozenset({3, 4, 5})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值