今天 , 我们来介绍 一下python 中 一些 集合类 ,python中 一些 内置的一些库 已经提供了很多 比较好的数据结构,方便 我们在今后的工程中进行使用, 之前 有把 python 基础的数据结构 , list, set ,tuple ,int ,float 这些基础的数据结构 介绍完了,想必 你们也有一定的了解. 今天 我来介绍一些 . 这些 数据结构 有时候 特别方便使用 .
比如 namedtuple ,deque 等 这些 数据结构 都在 collections
这个内置包里面.
目前来说 这里 有那么多, 这里从官方文档 拷贝下来的.
namedtuple() |
factory function for creating tuple subclasses with named fields |
---|---|
deque |
list-like container with fast appends and pops on either end |
ChainMap |
dict-like class for creating a single view of multiple mappings |
Counter |
dict subclass for counting hashable objects |
OrderedDict |
dict subclass that remembers the order entries were added |
defaultdict |
dict subclass that calls a factory function to supply missing values |
UserDict |
wrapper around dictionary objects for easier dict subclassing |
UserList |
wrapper around list objects for easier list subclassing |
UserString |
wrapper around string objects for easier string subclassing |
看到这个名字 感觉它 是什么呢? , 翻译一下 命名元祖, 我们知道元祖 是通过 下标 来访问元素的, 命名元祖 难道 是要给下标 起个名字吗? 答案是 是的.
来让我们 感受一下 这个 容器 如何使用?
>>> from collections import namedtuple
>>> # 定义一个命名元祖
>>> Point = namedtuple('point', ['x', 'y'])
>>> p = Point(10, 20)
>>> print(p)
...
point(x=10, y=20)
>>> print(p.x, p.y)
10 20
>>> print(p[0], p[1])
10 20
从上面的 例子看出来, p 这个 新生成的对象. 有两个属性, x,y 可以通过 p.x
, p.y
来访问 这两个属性, 同样 p 还有了元素的性质, 可以通过 下标 进行访问.
这里下标为0 的索引, 同时可以索引访问 p[0]
还可以使用 p.x
进行访问. 相当于 给 下标为0 的索引 '新起’了一个名字 叫 x
. 我想 这就是namedtuple 的来历吧.
打开 consle , 查看一下,
我发现确实 有两个属性, x,y
, 这和我刚刚猜测的差不多,这样 p 对象里面 已经新生成了两个属性. x,y 分别 用来 表示小标 0,下标 1 的位置.
首先 通过上面的例子演示 ,我们可以知道 namedtuple 可以通过 预先定义的属性,来访问元素.
如何定义一个 namedtuple , 首先 第一个位置 可以认为 是类名, 第二个位置 就是属性值, 这些属性值可以用 逗号 隔开 或者 通过list 来区分 就好.
大部分 情况下,通过这两种方式 生成 namedtuple , 之后 给这个nametuple 赋值 就可以了. 注意赋值的时候 位置要对应起来, 比如 0 --> name , 1 —> age , 这个 位置顺序是不能错的. 这样 计算机 才能明白,如何把 位置 和属性 建立起来 联系.
>>>
>>> Person = namedtuple('Person','name,age,height,gender,hobby')
...
... person = Person('frank',19,'165','male','swimming')
...
>>>
>>> person
Person(name='frank', age=19, height='165', gender='male', hobby='swimming')
>>>
>>> Person = namedtuple('Person',['name', 'age', 'height', 'gender', 'hobby'])
>>> Person
<class '__main__.Person'>
>>> person = Person('frank',19,'165','male','swimming')
...
>>> person
Person(name='frank', age=19, height='165', gender='male', hobby='swimming')
当然 关于创建 namedtuple 还有一些其他的 参数,这里我不展开了,可以参考 我给出的一个链接python中namedtuple的用法
说到 nameetuple 的特点,首先之前还记得 我们说过 python 的 中tuple 的特点吗? 让我来回忆一下,
01-Python中的数据类型-04-tuple,dict,set
最大的特点 就是只读,就是不可修改. 即一旦 元祖创建完成,就不可以 被修改了,所有的值 只能 作为 读的特点. 还有就是 tuple 本身 是有序的,通过索引 进行访问 .
好,那么namedtuple是不是 也是这样的 特点呢? 回答: 是的
来看下面的例子:
>>> Person = namedtuple('Person','name,age,height,gender,hobby')
>>> person = Person('frank',19,'165','male','swimming')
>>> person
Person(name='frank', age=19, height='165', gender='male', hobby='swimming')
>>> # 尝试修改tuple
>>> person.name='test111'
Traceback (most recent call last):
File "", line 1, in <module>
AttributeError: can't set attribute
>>> person[0]='aaaaa'
Traceback (most recent call last):
File "", line 1, in <module>
TypeError: 'Person' object does not support item assignment
其实这个问题 很简单, 假设 你有一个元祖里面 有大量的元素 , 我并不知道 他们的位置信息,我就可以通过一个属性,找到对应的值.
从计算机的角度来说,只要给一个位置, 我就是那个位置对应的元素, 但是 实际上, 人脑 不能 长期记住那么多的位置, 会忘记. 这个时候 我就通过属性 轻易获取 对应的值.
使用这个方便 人 来记住某个值, 给某个值 起一个 名字 ,这样 我们 就可以 找到他们了.
有了namedtuple 你可能会想 , 为啥不直接使用字典呢?
person = {
'name':'frank',
'age':19,
'height':165,
'gender':'male',
'hobby':'swimming'
}
这样 我一样可以实现 直接 获取 对应 key 的值呀.
首先, 这种想法 没有错, 当然 可以通过定义字典,来通过key 来获取对应的值. 也很方便.
但是 字典本身 是可以改变的,就是说, name 的值 可能 有被修改的风险, 这个namedtuple 就可以保证 值不能修改.
第二点 字典 本身很灵活, 而我们设计nametuple 就是来保存一些常量,或者不会变化的值. 还有 nametuple 满足tuple 的所有特性,本质来说 是 tuple , 只是把tuple 做了一点小小的改进.
defaultdict 这个实际工作中 用的挺多的, 所以还是要介绍一下.
首先我们应该知道dict 的一些特点 , 无序,可变性, 主要是这两个特点.
如果记不住了,可以看看 之前的文章. 01-Python中的数据类型-04-tuple,dict,set
在字典中获取一个 key 有两种方法, 第一种 get , 第二种 通过 [] 获取.
如果通过第二种方式获取要求,字典本身要有这个key ,如果没有则会报错KeyError, 因为这个key 不存在.
>>>
>>> person
{
'name': 'frank', 'age': 19, 'height': 165, 'gender': 'male', 'hobby': 'swimming'}
>>> person.get('name')
'frank'
>>> person['aaa']
Traceback (most recent call last):
File "", line 1, in <module>
KeyError: 'aaa'
有时候 为了 解决一类问题, 如果 我们想要 设置一个 key ,
如果这个key 存在 就设置一个值, 这个时候可以直接设置这个值.
如果不存在 就添加 这个key 并且 给一个 默认值.
举一个场景
如果我有篮子 水果. 苹果 ,橘子,梨,葡萄,香蕉, 现在 让 你统计每个品种的个数.
现在 我们开始使用最原始的方式字典,
from collections import defaultdict
from typing import Dict, List
fruits = ['apple', 'orange', 'pear', 'bananna',
'orange', 'pear', 'bananna', 'orange',
'pear', 'pear', 'bananna', 'grape',
'bananna', 'orange', 'pear', 'bananna',
'pear', 'bananna', 'grape']
def count_fruits(fruits: List) -> Dict:
counter = {
}
for fruit in fruits:
if fruit in counter:
counter[fruit] += 1
else:
counter[fruit] = 1
return counter
if __name__ == '__main__':
print(count_fruits(fruits)) # {'apple': 1, 'orange': 4, 'pear': 6, 'bananna': 6, 'grape': 2}
pass
上面的方式 好像没有什么问题 . 但是每次 遍历 都要判断 fruit 有没有在字典中 出现过, 就是 for 循环里面 有一个 一个 if 语句. 这样写 功能 是没有问题, 但是并不好.
有没有更好的方式呢? 你还记得 setdefault 的方法吗 ?
def count_fruits2(fruits: List) -> Dict:
counter = {
}
for fruit in fruits:
counter.setdefault(fruit, 0)
counter[fruit] += 1
return counter
上面 看起来 比之前 好一些 的方法, 但是没有更好的方式呢 ? defaultdict 就是可以实现更加优雅的方式.
def count_fruits3(fruits: List) -> Dict:
counter = defaultdict(int)
for fruit in fruits:
counter[fruit] += 1
return dict(counter)
这里把 counter 定义成一个 defaultdict . counter[fruit] += 1
当执行 这句话的时候,如果存在key 那么直接 加一, 如果没有 这个key ,直接生成一个key, value 初始值为0 . 看看是不是很方便.
首先 defaultdict 继承 dict 所有dict 的方法, defaultdict 都可以使用. 这就是继承的好处.直接复用了之前已经写好的代码.
class defaultdict(dict):pass
能够生成 dict 的方法, defaultdict 也可以直接生成.
在 defaultdict 中 有一个参数, defaul_factory 这个就是代表 你要给默认值的类型. 比如 int, list ,dict 等等.
def __init__(self, default_factory=None, **kwargs):pass
看下面的例子:
>>>
>>> couter = defaultdict(int)
>>> couter['one'] =1
>>> couter['two']
0
>>> couter
defaultdict(<class 'int'>, {
'one': 1, 'two': 0})
当我尝试访问 key=two 的时候,这个时候字典没有这个key, 它 会自动生成一个默认值,作为这个key 的value 值.
>>> from collections import defaultdict
>>> counter = defaultdict(list)
>>> counter
defaultdict(<class 'list'>, {
})
>>> counter['number'].append(1)
>>> counter['number'].append(2)
>>> counter['number'].append(3)
>>> counter
defaultdict(<class 'list'>, {
'number': [1, 2, 3]})
看看这样 我们就不用担心 设置一个默认值了, 如果发现 没有这个值,defaultdict 会根据 default_factory 来生成 对应的值的.
这就是 defaultdict 的作用了. 实际应用中 可以有很多方式, 知道这样的方法,可以使用代码更加的简洁.
举例 leetcode 习题 leetcode217 题目 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true
。如果数组中每个元素都不相同,则返回 false
。
这个题目思路:
只要统计每个元素出现的次数 就可以了, 如果有一个元素出现两次 返回True, 所有的元素 都统计完了,还没有出现两次, 就返回 False .
from collections import defaultdict
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
counter = defaultdict(int)
for num in nums:
counter[num] += 1
if counter[num] >= 2:
return True
return False
这篇文章 介绍 python 中的集合类 namedtuple, defaultdict . 集合类 有很多,后面在介绍, 有了集合类 可以更加方便的写出 更加优美的代码,灵活运用这些集合类,可以提高自己的工作效率. 后续 我会继续 写一下 其他的集合类,
加油,继续保持学习哦.
分享一句心灵鸡汤
一个人 在才华被认可前, 都要苦熬一段时间的 不是吗?
——《 短片小说选》欧.亨利
collections 官方文档
python中namedtuple的用法
01-Python中的数据类型-04-tuple,dict,set