Python常用容器(defaultdict, deque, namedtuple)

与C++ STL所对应,这里我们来下Python常用的容器。
 
1. Counter (数数和排序)
# 比如分析文本中每个单词出现过的次数,并只保留出现次数最高的若干个。
# Counter是一个dict子类,主要是用来对你访问的对象的频率进行计数。
# elements():返回一个迭代器,每个元素重复计算的个数,如果一个元素的计数小于1,就会被忽略。
# most_common([n]):返回一个列表,提供n个访问频率最高的元素和计数
# subtract([iterable-or-mapping]):从迭代对象中减去元素,输入输出可以是0或者负数
# update([iterable-or-mapping]):从迭代对象计数元素或者从另一个 映射对象 (或计数器) 添加。
 
# 统计字符出现的次数
>>> import collections
>>> collections.Counter('hello world')
Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
# 统计单词数
>>> collections.Counter('hello world hello world hello nihao'.split())
Counter({'hello': 3, 'world': 2, 'nihao': 1})
>>> c = collections.Counter('hello world hello world hello nihao'.split())
>>> c
Counter({'hello': 3, 'world': 2, 'nihao': 1})
# 获取指定对象的访问次数,也可以使用get()方法
>>> c['hello']
3
>>> c = collections.Counter('hello world hello world hello nihao'.split())
# 查看元素
>>> list(c.elements())
['hello', 'hello', 'hello', 'world', 'world', 'nihao']
# 追加对象,或者使用c.update(d)
>>> c = collections.Counter('hello world hello world hello nihao'.split())
>>> d = collections.Counter('hello world'.split())
>>> c
Counter({'hello': 3, 'world': 2, 'nihao': 1})
>>> d
Counter({'hello': 1, 'world': 1})
>>> c + d
Counter({'hello': 4, 'world': 3, 'nihao': 1})
# 减少对象,或者使用c.subtract(d)
>>> c - d
Counter({'hello': 2, 'world': 1, 'nihao': 1})
# 清除
>>> c.clear()
>>> c
Counter()
 
# 除此之外,它的构造函数还接收dict类型。我们可以直接通过一个value是int类型的dict来初始化一个  
# Counter,比如:
c = Counter({'apple': 5, 'pear': 4})
c = Counter(apple=4, pear=3)
 
 
 
2. defaultdict(主要可以解决两个问题)
第一、当key为空的时候,dict会抛出异常,当然也可以用dict.get(key,None)来解决这解决这个问题。
第二、当key存在重复,我们希望将相同value存到一个list中。
 
collections.defaultdict(default_factory)为字典的没有的key提供一个默认的值。参数应该是一个函数,当没有参数调用时返回默认值。如果没有传递任何内容,则默认为None。
 
# defaultdict(None, {})
d = collections.defaultdict()
 
defaultdict的一个典型用法是使用其中一种内置类型(如str、int、list或dict)作为默认工厂,这些内置类型在没有参数调用时返回空类型。
# defaultdict(<class 'str'>, {})
e = collections.defaultdict(str)
# ''
e['hello']
 
# 使用int作为default_factory的例子
from collections import defaultdict
fruit = defaultdict(int)
# defaultdict(<class 'int'>, {'apple': 2})
fruit['apple'] += 2 
# 0
fruit['banana']  # 没有对象时,返回0
# defaultdict(<class 'int'>, {'apple': 2, 'banana': 0})
fruit
 
# 使用list作为default_factory的例子
s = [('NC', 'Raleigh'), ('VA', 'Richmond'), ('WA', 'Seattle'), ('NC', 'Asheville')]
d = collections.defaultdict(list)
for k,v in s:
    d[k].append(v)
# defaultdict(<class 'list'>, {'NC': ['Raleigh', 'Asheville'], 'VA': ['Richmond'], 'WA': # ['Seattle']})
d
 
3. deque(双端队列)
# 在日常的使用当中,真正用到双端队列的算法其实不太多。大多数情况下我们使用deque主要有两个原   
# 因,第一个原因是deque收到GIL的管理,它是线程安全的。而list则没有GIL锁,因此不是线程安全的。 
# 也就是说在并发场景下,list可能会导致一致性问题,而deque不会。另一个原因是deque支持固定长度,
# 当长度满了之后,当我们继续append时,它会自动弹出最早插入的数据。并且deque优化了从左端插入和 
# 弹出的时间复杂度(现在都是O(1))
 
# append(x):添加x到右端
# appendleft(x):添加x到左端
# clear():清楚所有元素,长度变为0
# copy():创建一份浅拷贝
# count(x):计算队列中个数等于x的元素
# extend(iterable):在队列右侧添加iterable中的元素
# extendleft(iterable):在队列左侧添加iterable中的元素,
# 注:在左侧添加时,iterable参数的顺序将会反过来添加
# index(x[,start[,stop]]):返回第 x 个元素(从 start 开始计算,在 stop 之前)。
# 返回第一个匹配,如果没找到的话,升起 ValueError 。 
# insert(i,x):在位置 i 插入 x 。注:如果插入会导致一个限长
# deque超出长度 maxlen 的话,就升起一个 IndexError 。
# pop():移除最右侧的元素
# popleft():移除最左侧的元素
# remove(value):移去找到的第一个 value。没有抛出ValueError
# reverse():将deque逆序排列。返回 None 。
# maxlen:队列的最大长度,没有限定则为None。
 
>>> from collections import deque
>>> d = deque(maxlen=10)
>>> d
deque([], maxlen=10)
>>> d.extend('python')
>>> [i.upper() for i in d]
['P', 'Y', 'T', 'H', 'O', 'N']
>>> d.append('e')
>>> d.appendleft('f')
>>> d
deque(['f', 'p', 'y', 't', 'h', 'o', 'n', 'e'], maxlen=10)
 
4. namedtuple(命名元组)
# namedtuple很特殊,它涉及到元编程的概念。在常见的面向对象当中。我们都是定义类,然后通过类的构
# 造函数来创建实例。而元编程指的是我们定义元类,根据元类创建出来的并不是一个实例,而是一个类。
# namedtuple是一个非常简单的元类,通过它我们可以非常方便地定义我们想要的类。它的第一个参数是命
# 名元组的构造器
 
# 如果我们要定义一个学生类,这个类中有name, score, age三个字段,则这个类会写成:
class Student:
    def __init__(self, name=None, score=None, age=None):
        self.name = name
        self.score = score
        self.age = age
# 这还只是粗略的写法,如果考虑规范,还需要定义property等注解,又需要很多代码。如果我们使用   
# namedtuple可以简化这个工作,我们来看代码:
from collections import namedtuple
Student = namedtuple('Student', ['name', 'score', 'age'])
# 这个是实例
student = Student(name='xiaoming', score=99, age=10)
print(student.name)
 
# 通过使用namedtuple,我们只需要一行就定义了一个类,但是这样定义的类是没有缺省值的,但是      
# namedtuple很强大,我们可以通过传入defaults参数来定义缺省值。
Student = namedtuple('Student', ['name', 'score', 'age'], defaults=(0, 0))
 
# 可以注意到,虽然我们定义了三个字段,但是我们只设置了两个缺省值。在这种情况下,namedtuple会自
# 动将缺失值匹配上score和age两个字段。因为在Python的规范当中,必选参数一定在可选参数前面。所以
# nuamdtuple会自动右对齐。
 
5. OrderedDict(有序字典)
# Python字典中的键的顺序是任意的:它们不受添加的顺序的控制。collections.OrderedDict类提供了保 
# 留他们添加顺序的字典对象。
>>> from collections import OrderedDict
>>> o = OrderedDict()
>>> o['key1'] = 'value1'
>>> o['key2'] = 'value2'
>>> o['key3'] = 'value3'
>>> o
OrderedDict([('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3')])
# 如果在已经存在的key上添加新的值,将会保留原来的key的位置,然后覆盖value值。



6. Queue, LifoQueue, PriorityQueue
# queue模块是python内置的模块,queue提供了3种数据结构供我们使用
# queue.Queue         FIFO,先进先出队列
# queue.LifoQueue   LIFO,后进先出队列
# queue.PriorityQueue       优先级队列
# 这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

# 这3种数据类型,对外暴露的接口是完全一致的。因此,在此处做一个总结。
# 此包中的常用方法(q =queue.Queue()):
# q.qsize() 返回队列的大小
# q.empty() 如果队列为空,返回True,反之False
# q.full() 如果队列满了,返回True,反之False
# q.full 与 maxsize 大小对应
# q.get([block[, timeout]]) 获取队列,timeout等待时间
# q.get_nowait() 相当q.get(False)
# 非阻塞 q.put(item) 写入队列,timeout等待时间
# q.put_nowait(item) 相当q.put(item, False)
# q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
# q.join() 实际上意味着等到队列为空,再执行别的操作


# 有时候,我们需要根据元素的特性来决定元素的处理顺序,
# 而不是简单的按照元素的插入顺序从队列中取出数据。比如,
# 工资部门的打印作业可能就优先于某个开发人员的打印代码的任务。
# 而PriorityQueue就是解决这种问题的。

# 我们需要给PriorityQueue传入一个tuple形式的数据,
# 第一个数字表示优先级,数字越小优先级越高,data表示我们真正要使用的数据。
import queue
 
q = queue.PriorityQueue()
test_list = [(1, "important"), (3, "low"), (2, "middle")]
for i in test_list:
    q.put(i)
 
while not q.empty():
    print(q.get())
 
    (1, 'important')
    (2, 'middle')
    (3, 'low')

# 如果想使用类呢,需要在给对象的实例化时传入一个number,表示优先级,
# 然后我们还必须实现比较魔法函数,因为q.get()会先比较两个对象的大小,
# 而对象的比较大小,就需要实现比较魔法函数。

import functools
import queue
import threading
from numbers import Number
 
 
@functools.total_ordering
class Job:
    def __init__(self, priority, description):
        self.priority = priority
        self.description = description
        print("New job: ", description)
        return
 
    def __eq__(self, other):
        if isinstance(other, Job):
            return self.priority == other.priority
        elif isinstance(other, Number):
            return self.priority == other
        else:
            raise AttributeError("The of other is incorrect!")
 
    def __gt__(self, other):
        if isinstance(other, Job):
            return self.priority > other.priority
        elif isinstance(other, Number):
            return self.priority > other
        else:
            raise AttributeError("The of other is incorrect!")
 
 
q = queue.PriorityQueue()
q.put(Job(2, "middle Level"))
q.put(Job(1, "important Level"))
q.put(Job(3, "Low level"))
 
 
def process_job(q):
    while True:
        next_job = q.get()
        print("Processing Job: ", next_job.description)
        q.task_done()
 
 
workers = [
    threading.Thread(target=process_job, args=(q,)),
    threading.Thread(target=process_job, args=(q,)),
]
 
for worker in workers:
    worker.setDaemon(True)
    worker.start()
 
q.join()
 
 
# 输出
New job:  middle Level
New job:  important Level
New job:  Low level
Processing Job:  important Level
Processing Job:  middle Level
Processing Job:  Low level

7. ChianMap
# 一个 ChainMap 将多个字典或者其他映射组合在一起,创建一个单独的可更新的视图。 如果没有 maps  
# 被指定,就提供一个默认的空字典 。ChainMap是管理嵌套上下文和覆盖的有用工具。
>>> from collections import ChainMap
>>> d1 = {'apple':1,'banana':2}
>>> d2 = {'orange':2,'apple':3,'pike':1}
>>> combined_d = ChainMap(d1,d2)
>>> reverse_combind_d = ChainMap(d2,d1)
>>> combined_d 
ChainMap({'apple': 1, 'banana': 2}, {'orange': 2, 'apple': 3, 'pike': 1})
>>> reverse_combind_d
ChainMap({'orange': 2, 'apple': 3, 'pike': 1}, {'apple': 1, 'banana': 2})
>>> for k,v in combined_d.items():
...      print(k,v)
... 
pike 1
apple 1
banana 2
orange 2
>>> for k,v in reverse_combind_d.items():
...      print(k,v)
... 
pike 1
apple 3
banana 2
orange 2

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值