Python 模块collections

1、深入理解python中的tuple的功能

基本特性

# 可迭代
name_tuple = ('0bug', '1bug', '2bug')
for name in name_tuple:
print(name) # 不可变
name_tuple = ('0bug', '1bug', '2bug')
name_tuple[0] = 'bug' # TypeError: 'tuple' object does not support item assignment # 不可变不是绝对的
change_tuple = (1, 2, [3, 4])
change_tuple[2][1] = 5
print(change_tuple) # (1, 2, [3, 5]) # 拆包
lcg_tuple = ('0bug', '25', '175')
name, age, height = lcg_tuple
print(name, age, height) lcg_tuple = ('0bug', '25', '175')
name, *other = lcg_tuple
print(other) # ['25', '175']

tuple比list好的地方在哪?

1,性能优化

2.线程安全

3.可以作为dict的key

# 元祖对象是可哈希的,可作为字典的key
my_tuple = ('0bug','1bug')
dic = {}
dic[my_tuple] = 'name'
print(dic) # {('0bug', '1bug'): 'name'}

4.拆包特性

如果拿c语言来类比,Tuple对应的是struct,而List对应的是array

2、namedtuple的功能详解

我们知道,class创建一个类,他的对象可以通过"."来访问对象的属性。

class User(object):
def __init__(self, name, age):
self.name = name
self.age = age user = User('lcg', '25')
print(user.name) # lcg
print(user.age) # 25

namedtuple也可以创建一个类对象,继承自tuple的。

from collections import namedtuple

User = namedtuple('User', ['name', 'age'])
user = User('lcg', 25)
print(user.name) # lcg
print(user.age) # 25

namedtuple部分源码:

_class_template = """\
from builtins import property as _property, tuple as _tuple
from operator import itemgetter as _itemgetter
from collections import OrderedDict class {typename}(tuple):
'{typename}({arg_list})' __slots__ = () _fields = {field_names!r} def __new__(_cls, {arg_list}):
'Create new instance of {typename}({arg_list})'
return _tuple.__new__(_cls, ({arg_list})) @classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new {typename} object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != {num_fields:d}:
raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result))
return result def _replace(_self, **kwds):
'Return a new {typename} object replacing specified fields with new values'
result = _self._make(map(kwds.pop, {field_names!r}, _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % list(kwds))
return result def __repr__(self):
'Return a nicely formatted representation string'
return self.__class__.__name__ + '({repr_fmt})' % self def _asdict(self):
'Return a new OrderedDict which maps field names to their values.'
return OrderedDict(zip(self._fields, self)) def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self) {field_defs}
""" _repr_template = '{name}=%r' _field_template = '''\
{name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}')
''' def namedtuple(typename, field_names, *, verbose=False, rename=False, module=None):
"""Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y'])
>>> Point.__doc__ # docstring for the new class
'Point(x, y)'
>>> p = Point(11, y=22) # instantiate with positional args or keywords
>>> p[0] + p[1] # indexable like a plain tuple
33
>>> x, y = p # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessible by name
33
>>> d = p._asdict() # convert to a dictionary
>>> d['x']
11
>>> Point(**d) # convert from a dictionary
Point(x=11, y=22)
>>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
Point(x=100, y=22) """ # Validate the field names. At the user's option, either generate an error
# message or automatically replace the field name with a valid name.
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split()
field_names = list(map(str, field_names))
typename = str(typename)
if rename:
seen = set()
for index, name in enumerate(field_names):
if (not name.isidentifier()
or _iskeyword(name)
or name.startswith('_')
or name in seen):
field_names[index] = '_%d' % index
seen.add(name)
for name in [typename] + field_names:
if type(name) is not str:
raise TypeError('Type names and field names must be strings')
if not name.isidentifier():
raise ValueError('Type names and field names must be valid '
'identifiers: %r' % name)
if _iskeyword(name):
raise ValueError('Type names and field names cannot be a '
'keyword: %r' % name)
seen = set()
for name in field_names:
if name.startswith('_') and not rename:
raise ValueError('Field names cannot start with an underscore: '
'%r' % name)
if name in seen:
raise ValueError('Encountered duplicate field name: %r' % name)
seen.add(name) # Fill-in the class template
class_definition = _class_template.format(
typename = typename,
field_names = tuple(field_names),
num_fields = len(field_names),
arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
repr_fmt = ', '.join(_repr_template.format(name=name)
for name in field_names),
field_defs = '\n'.join(_field_template.format(index=index, name=name)
for index, name in enumerate(field_names))
) # Execute the template string in a temporary namespace and support
# tracing utilities by setting a value for frame.f_globals['__name__']
namespace = dict(__name__='namedtuple_%s' % typename)
exec(class_definition, namespace)
result = namespace[typename]
result._source = class_definition
if verbose:
print(result._source) # For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in environments where
# sys._getframe is not defined (Jython for example) or sys._getframe is not
# defined for arguments greater than 0 (IronPython), or where the user has
# specified a particular module.
if module is None:
try:
module = _sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
if module is not None:
result.__module__ = module return result

可以使用拆包特性传递参数

from collections import namedtuple

User = namedtuple('User', ['name', 'age', 'height'])
uer_tuple = ('lcg', 25)
user = User(*uer_tuple, 175)
print(user.name) # lcg
print(user.age) # 25 user_dict = {'name': 'lcg', 'age': 25, 'height': 175}
user2 = User(**user_dict)
print(user2.name) # lcg

1.namedtuple也为我们提供了一个._make方法,无需考虑拆包的参数是位置参数还是关键字参数,但是一定要参数对应上

from collections import namedtuple

User = namedtuple('User', ['name', 'age', 'height'])
uer_tuple = ('lcg', 25, 175)
user = User._make(uer_tuple)
print(user.name) # lcg
print(user.age) # 25 user_dict = {'name': 'lcg', 'age': 25, 'height': 175}
user2 = User._make(user_dict)
print(user2.name) # lcg uer_tuple = ('lcg', 25)
user = User(*uer_tuple, 175)
print(user.height) # 175 # 使用_make的时候,一定要参数对应上
uer_tuple = ('lcg', 25)
user = User._make(uer_tuple, 175) # TypeError: 'int' object is not callable

2._asdict方法将namedtuple对象转换为OrderedDict.

from collections import namedtuple

User = namedtuple('User', ['name', 'age', 'height'])
uer_tuple = ('lcg', 25, 175)
user = User._make(uer_tuple)
user_dic = user._asdict()
print(user_dic) # OrderedDict([('name', 'lcg'), ('age', 25), ('height', 175)])

3.还有一个好处是,namedtuple是继承tuple的,所有有tuple的很多特性,比如拆包

from collections import namedtuple

User = namedtuple('User', ['name', 'age', 'height'])
uer_tuple = ('lcg', 25, 175)
user = User._make(uer_tuple)
name, *other = user
print(name) # lcg

3、defaultdict的功能详解

defaultdict底层调用__missing__这个魔法函数。

对一个列表总元素统计可以使用字典的setdefault方法:

users = ['0bug', '1bug', '2bug', '3bug', '0bug', '0bug', '3bug']
user_dict = {}
for user in users:
user_dict.setdefault(user, 0)
user_dict[user] += 1
print(user_dict) # {'0bug': 3, '1bug': 1, '2bug': 1, '3bug': 2}

defaultdict也是dict的一个扩展子类,传递可调用对象为参数,如int

from collections import defaultdict

default_dict_int = defaultdict(int)

default_dict_str = defaultdict(str)

default_dict_list = defaultdict(list)

default_dict_dict = defaultdict(dict)

print(default_dict_int['不存在'])   # 0
print(default_dict_str['不存在']) # 这个返回空
print(default_dict_list['不存在']) # []
print(default_dict_dict['不存在']) # {}

用defaultdict统计列表中元素

from collections import defaultdict

users = ['0bug', '1bug', '2bug', '3bug', '0bug', '0bug', '3bug']
default_dict = defaultdict(int)
for user in users:
default_dict[user] += 1 print(default_dict) # defaultdict(<class 'int'>, {'0bug': 3, '1bug': 1, '2bug': 1, '3bug': 2}) print(list(default_dict)) # {'0bug': 3, '1bug': 1, '2bug': 1, '3bug': 2}

4、deque的功能详解

deque就是双端队列

部分源码:

class deque(object):
"""
deque([iterable[, maxlen]]) --> deque object A list-like sequence optimized for data accesses near its endpoints.
"""
def append(self, *args, **kwargs): # real signature unknown
""" Add an element to the right side of the deque. """
pass def appendleft(self, *args, **kwargs): # real signature unknown
""" Add an element to the left side of the deque. """
pass def clear(self, *args, **kwargs): # real signature unknown
""" Remove all elements from the deque. """
pass def copy(self, *args, **kwargs): # real signature unknown
""" Return a shallow copy of a deque. """
pass def count(self, value): # real signature unknown; restored from __doc__
""" D.count(value) -> integer -- return number of occurrences of value """
return 0 def extend(self, *args, **kwargs): # real signature unknown
""" Extend the right side of the deque with elements from the iterable """
pass def extendleft(self, *args, **kwargs): # real signature unknown
""" Extend the left side of the deque with elements from the iterable """
pass def index(self, value, start=None, stop=None): # real signature unknown; restored from __doc__
"""
D.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
"""
return 0 def insert(self, index, p_object): # real signature unknown; restored from __doc__
""" D.insert(index, object) -- insert object before index """
pass def pop(self, *args, **kwargs): # real signature unknown
""" Remove and return the rightmost element. """
pass def popleft(self, *args, **kwargs): # real signature unknown
""" Remove and return the leftmost element. """
pass def remove(self, value): # real signature unknown; restored from __doc__
""" D.remove(value) -- remove first occurrence of value. """
pass def reverse(self): # real signature unknown; restored from __doc__
""" D.reverse() -- reverse *IN PLACE* """
pass def rotate(self, *args, **kwargs): # real signature unknown
""" Rotate the deque n steps to the right (default n=1). If n is negative, rotates left. """
pass def __add__(self, *args, **kwargs): # real signature unknown
""" Return self+value. """
pass def __bool__(self, *args, **kwargs): # real signature unknown
""" self != 0 """
pass def __contains__(self, *args, **kwargs): # real signature unknown
""" Return key in self. """
pass def __copy__(self, *args, **kwargs): # real signature unknown
""" Return a shallow copy of a deque. """
pass def __delitem__(self, *args, **kwargs): # real signature unknown
""" Delete self[key]. """
pass def __eq__(self, *args, **kwargs): # real signature unknown
""" Return self==value. """
pass def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass def __getitem__(self, *args, **kwargs): # real signature unknown
""" Return self[key]. """
pass def __ge__(self, *args, **kwargs): # real signature unknown
""" Return self>=value. """
pass def __gt__(self, *args, **kwargs): # real signature unknown
""" Return self>value. """
pass def __iadd__(self, *args, **kwargs): # real signature unknown
""" Implement self+=value. """
pass def __imul__(self, *args, **kwargs): # real signature unknown
""" Implement self*=value. """
pass def __init__(self, iterable=(), maxlen=None): # known case of _collections.deque.__init__
"""
deque([iterable[, maxlen]]) --> deque object A list-like sequence optimized for data accesses near its endpoints.
# (copied from class doc)
"""
pass def __iter__(self, *args, **kwargs): # real signature unknown
""" Implement iter(self). """
pass def __len__(self, *args, **kwargs): # real signature unknown
""" Return len(self). """
pass def __le__(self, *args, **kwargs): # real signature unknown
""" Return self<=value. """
pass def __lt__(self, *args, **kwargs): # real signature unknown
""" Return self<value. """
pass def __mul__(self, *args, **kwargs): # real signature unknown
""" Return self*value.n """
pass @staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""" Create and return a new object. See help(type) for accurate signature. """
pass def __ne__(self, *args, **kwargs): # real signature unknown
""" Return self!=value. """
pass def __reduce__(self, *args, **kwargs): # real signature unknown
""" Return state information for pickling. """
pass def __repr__(self, *args, **kwargs): # real signature unknown
""" Return repr(self). """
pass def __reversed__(self): # real signature unknown; restored from __doc__
""" D.__reversed__() -- return a reverse iterator over the deque """
pass def __rmul__(self, *args, **kwargs): # real signature unknown
""" Return self*value. """
pass def __setitem__(self, *args, **kwargs): # real signature unknown
""" Set self[key] to value. """
pass def __sizeof__(self): # real signature unknown; restored from __doc__
""" D.__sizeof__() -- size of D in memory, in bytes """
pass maxlen = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
"""maximum size of a deque or None if unbounded""" __hash__ = None

append、appendleft ,将元素插入到尾部 / 头部

from collections import deque

user_deque = deque(['0bug', '1bug', '2bug'])
print(user_deque) # deque(['0bug', '1bug', '2bug'])
user_deque.append('3bug')
user_deque.appendleft('-1bug')
print(user_deque) # deque(['-1bug', '0bug', '1bug', '2bug', '3bug'])

clear 清空数据

from collections import deque

user_deque = deque(['0bug', '1bug', '2bug'])
user_deque.clear()
print(user_deque) # deque([])

copy 浅拷贝

from collections import deque

user_deque = deque(['0bug', ['1bug', '2bug']])
user_deque2 = user_deque.copy()
print(id(user_deque), id(user_deque2)) # 2112944554992 2112944556968
user_deque2[1][0] = '3bug'
print(user_deque) # deque(['0bug', ['3bug', '2bug']])
print(user_deque2) # deque(['0bug', ['3bug', '2bug']]) # 浅拷贝只拷贝一层,所以user_deque2修改第二层会影响user_deque

深拷贝需要使用copy模块里的deepcopy方法

from collections import deque
import copy user_deque = deque(['0bug', ['1bug', '2bug']])
user_deque2 = copy.deepcopy(user_deque)
print(id(user_deque), id(user_deque2)) # 1642030214472 1642030214368
user_deque2[1][0] = '3bug'
print(user_deque) # deque(['0bug', ['1bug', '2bug']])
print(user_deque2) # deque(['0bug', ['3bug', '2bug']])

count 计数

from collections import deque

user_deque = deque(['0bug', '1bug', '2bug','0bug'])
print(user_deque.count('0bug')) # 2

extend二合一,动态扩容(无返回值而是扩容!)

from collections import deque

user_deque1 = deque(['0bug', '1bug'])
user_deque2 = deque(['2bug', '3bug'])
user_deque1.extend(user_deque2)
print(user_deque1) # deque(['0bug', '1bug', '2bug', '3bug'])

extendleft 左扩容

from collections import deque

user_deque1 = deque(['0bug', '1bug'])
user_deque2 = deque(['2bug', '3bug'])
user_deque1.extendleft(user_deque2)
print(user_deque1) # deque(['3bug', '2bug', '0bug', '1bug'])

index查找索引,找不到就抛出异常

from collections import deque

user_deque = deque(['0bug', '1bug'])
print(user_deque.index('0bug')) # 0
print(user_deque.index('0xbug')) # ValueError: '0xbug' is not in deque

insert 指定位置插入元素

from collections import deque

user_deque = deque(['0bug', '1bug'])
user_deque.insert(0, '0xbug')
print(user_deque) # deque(['0xbug', '0bug', '1bug'])

pop, popleft 弹出尾部 头部元素(有返回值)

from collections import deque

user_deque = deque(['0bug', '1bug', '2bug'])
user_pop = user_deque.pop()
print(user_pop) # 2bug
print(user_deque) # deque(['0bug', '1bug']) user_deque.popleft()
print(user_deque) # deque(['1bug'])

remove删除某个存在的元素,不存在就抛出异常,无返回值

from collections import deque

user_deque = deque(['0bug', '1bug', '2bug'])
user_deque.remove('0bug')
print(user_deque) # deque(['1bug', '2bug'])

reverse 原地反转(无返回值)

from collections import deque

user_deque = deque(['0bug', '1bug', '2bug'])
user_deque.reverse()
print(user_deque) # deque(['2bug', '1bug', '0bug'])

deque还有一些魔法函数,其实魔法函数也就是python的协议,是python解释器直接调用的。

deque的应用场景

from queue import Queue 这里的Queue就是通过双端队列deque来实现的

Queue部分源码

class Queue:
'''Create a queue object with a given maximum size. If maxsize is <= 0, the queue size is infinite.
''' def __init__(self, maxsize=0):
self.maxsize = maxsize
self._init(maxsize) # mutex must be held whenever the queue is mutating. All methods
# that acquire mutex must release it before returning. mutex
# is shared between the three conditions, so acquiring and
# releasing the conditions also acquires and releases mutex.
self.mutex = threading.Lock() # Notify not_empty whenever an item is added to the queue; a
# thread waiting to get is notified then.
self.not_empty = threading.Condition(self.mutex) # Notify not_full whenever an item is removed from the queue;
# a thread waiting to put is notified then.
self.not_full = threading.Condition(self.mutex) # Notify all_tasks_done whenever the number of unfinished tasks
# drops to zero; thread waiting to join() is notified to resume
self.all_tasks_done = threading.Condition(self.mutex)
self.unfinished_tasks = 0 def task_done(self):
'''Indicate that a formerly enqueued task is complete. Used by Queue consumer threads. For each get() used to fetch a task,
a subsequent call to task_done() tells the queue that the processing
on the task is complete. If a join() is currently blocking, it will resume when all items
have been processed (meaning that a task_done() call was received
for every item that had been put() into the queue). Raises a ValueError if called more times than there were items
placed in the queue.
'''
with self.all_tasks_done:
unfinished = self.unfinished_tasks - 1
if unfinished <= 0:
if unfinished < 0:
raise ValueError('task_done() called too many times')
self.all_tasks_done.notify_all()
self.unfinished_tasks = unfinished def join(self):
'''Blocks until all items in the Queue have been gotten and processed. The count of unfinished tasks goes up whenever an item is added to the
queue. The count goes down whenever a consumer thread calls task_done()
to indicate the item was retrieved and all work on it is complete. When the count of unfinished tasks drops to zero, join() unblocks.
'''
with self.all_tasks_done:
while self.unfinished_tasks:
self.all_tasks_done.wait() def qsize(self):
'''Return the approximate size of the queue (not reliable!).'''
with self.mutex:
return self._qsize() def empty(self):
'''Return True if the queue is empty, False otherwise (not reliable!). This method is likely to be removed at some point. Use qsize() == 0
as a direct substitute, but be aware that either approach risks a race
condition where a queue can grow before the result of empty() or
qsize() can be used. To create code that needs to wait for all queued tasks to be
completed, the preferred technique is to use the join() method.
'''
with self.mutex:
return not self._qsize() def full(self):
'''Return True if the queue is full, False otherwise (not reliable!). This method is likely to be removed at some point. Use qsize() >= n
as a direct substitute, but be aware that either approach risks a race
condition where a queue can shrink before the result of full() or
qsize() can be used.
'''
with self.mutex:
return 0 < self.maxsize <= self._qsize() def put(self, item, block=True, timeout=None):
'''Put an item into the queue. If optional args 'block' is true and 'timeout' is None (the default),
block if necessary until a free slot is available. If 'timeout' is
a non-negative number, it blocks at most 'timeout' seconds and raises
the Full exception if no free slot was available within that time.
Otherwise ('block' is false), put an item on the queue if a free slot
is immediately available, else raise the Full exception ('timeout'
is ignored in that case).
'''
with self.not_full:
if self.maxsize > 0:
if not block:
if self._qsize() >= self.maxsize:
raise Full
elif timeout is None:
while self._qsize() >= self.maxsize:
self.not_full.wait()
elif timeout < 0:
raise ValueError("'timeout' must be a non-negative number")
else:
endtime = time() + timeout
while self._qsize() >= self.maxsize:
remaining = endtime - time()
if remaining <= 0.0:
raise Full
self.not_full.wait(remaining)
self._put(item)
self.unfinished_tasks += 1
self.not_empty.notify() def get(self, block=True, timeout=None):
'''Remove and return an item from the queue. If optional args 'block' is true and 'timeout' is None (the default),
block if necessary until an item is available. If 'timeout' is
a non-negative number, it blocks at most 'timeout' seconds and raises
the Empty exception if no item was available within that time.
Otherwise ('block' is false), return an item if one is immediately
available, else raise the Empty exception ('timeout' is ignored
in that case).
'''
with self.not_empty:
if not block:
if not self._qsize():
raise Empty
elif timeout is None:
while not self._qsize():
self.not_empty.wait()
elif timeout < 0:
raise ValueError("'timeout' must be a non-negative number")
else:
endtime = time() + timeout
while not self._qsize():
remaining = endtime - time()
if remaining <= 0.0:
raise Empty
self.not_empty.wait(remaining)
item = self._get()
self.not_full.notify()
return item def put_nowait(self, item):
'''Put an item into the queue without blocking. Only enqueue the item if a free slot is immediately available.
Otherwise raise the Full exception.
'''
return self.put(item, block=False) def get_nowait(self):
'''Remove and return an item from the queue without blocking. Only get an item if one is immediately available. Otherwise
raise the Empty exception.
'''
return self.get(block=False) # Override these methods to implement other queue organizations
# (e.g. stack or priority queue).
# These will only be called with appropriate locks held # Initialize the queue representation
def _init(self, maxsize):
self.queue = deque() def _qsize(self):
return len(self.queue) # Put a new item in the queue
def _put(self, item):
self.queue.append(item) # Get an item from the queue
def _get(self):
return self.queue.popleft()

还有一个很重要的特性:deque是GIL来保护的,是线程安全的list是非线程安全的。

5、Counter功能详解

Counter是dict的一个子类。一般用来做统计

Counter部分源码:

class Counter(dict):
'''Dict subclass for counting hashable items. Sometimes called a bag
or multiset. Elements are stored as dictionary keys and their counts
are stored as dictionary values. >>> c = Counter('abcdeabcdabcaba') # count elements from a string >>> c.most_common(3) # three most common elements
[('a', 5), ('b', 4), ('c', 3)]
>>> sorted(c) # list all unique elements
['a', 'b', 'c', 'd', 'e']
>>> ''.join(sorted(c.elements())) # list elements with repetitions
'aaaaabbbbcccdde'
>>> sum(c.values()) # total of all counts
15 >>> c['a'] # count of letter 'a'
5
>>> for elem in 'shazam': # update counts from an iterable
... c[elem] += 1 # by adding 1 to each element's count
>>> c['a'] # now there are seven 'a'
7
>>> del c['b'] # remove all 'b'
>>> c['b'] # now there are zero 'b'
0 >>> d = Counter('simsalabim') # make another counter
>>> c.update(d) # add in the second counter
>>> c['a'] # now there are nine 'a'
9 >>> c.clear() # empty the counter
>>> c
Counter() Note: If a count is set to zero or reduced to zero, it will remain
in the counter until the entry is deleted or the counter is cleared: >>> c = Counter('aaabbc')
>>> c['b'] -= 2 # reduce the count of 'b' by two
>>> c.most_common() # 'b' is still in, but its count is zero
[('a', 3), ('c', 1), ('b', 0)] '''
# References:
# http://en.wikipedia.org/wiki/Multiset
# http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html
# http://www.demo2s.com/Tutorial/Cpp/0380__set-multiset/Catalog0380__set-multiset.htm
# http://code.activestate.com/recipes/259174/
# Knuth, TAOCP Vol. II section 4.6.3 def __init__(*args, **kwds):
'''Create a new, empty Counter object. And if given, count elements
from an input iterable. Or, initialize the count from another mapping
of elements to their counts. >>> c = Counter() # a new, empty counter
>>> c = Counter('gallahad') # a new counter from an iterable
>>> c = Counter({'a': 4, 'b': 2}) # a new counter from a mapping
>>> c = Counter(a=4, b=2) # a new counter from keyword args '''
if not args:
raise TypeError("descriptor '__init__' of 'Counter' object "
"needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
super(Counter, self).__init__()
self.update(*args, **kwds) def __missing__(self, key):
'The count of elements not in the Counter is zero.'
# Needed so that self[missing_item] does not raise KeyError
return 0 def most_common(self, n=None):
'''List the n most common elements and their counts from the most
common to the least. If n is None, then list all element counts. >>> Counter('abcdeabcdabcaba').most_common(3)
[('a', 5), ('b', 4), ('c', 3)] '''
# Emulate Bag.sortedByCount from Smalltalk
if n is None:
return sorted(self.items(), key=_itemgetter(1), reverse=True)
return _heapq.nlargest(n, self.items(), key=_itemgetter(1)) def elements(self):
'''Iterator over elements repeating each as many times as its count. >>> c = Counter('ABCABC')
>>> sorted(c.elements())
['A', 'A', 'B', 'B', 'C', 'C'] # Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
>>> prime_factors = Counter({2: 2, 3: 3, 17: 1})
>>> product = 1
>>> for factor in prime_factors.elements(): # loop over factors
... product *= factor # and multiply them
>>> product
1836 Note, if an element's count has been set to zero or is a negative
number, elements() will ignore it. '''
# Emulate Bag.do from Smalltalk and Multiset.begin from C++.
return _chain.from_iterable(_starmap(_repeat, self.items())) # Override dict methods where necessary @classmethod
def fromkeys(cls, iterable, v=None):
# There is no equivalent method for counters because setting v=1
# means that no element can have a count greater than one.
raise NotImplementedError(
'Counter.fromkeys() is undefined. Use Counter(iterable) instead.') def update(*args, **kwds):
'''Like dict.update() but add counts instead of replacing them. Source can be an iterable, a dictionary, or another Counter instance. >>> c = Counter('which')
>>> c.update('witch') # add elements from another iterable
>>> d = Counter('watch')
>>> c.update(d) # add elements from another counter
>>> c['h'] # four 'h' in which, witch, and watch
4 '''
# The regular dict.update() operation makes no sense here because the
# replace behavior results in the some of original untouched counts
# being mixed-in with all of the other counts for a mismash that
# doesn't have a straight-forward interpretation in most counting
# contexts. Instead, we implement straight-addition. Both the inputs
# and outputs are allowed to contain zero and negative counts. if not args:
raise TypeError("descriptor 'update' of 'Counter' object "
"needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
iterable = args[0] if args else None
if iterable is not None:
if isinstance(iterable, Mapping):
if self:
self_get = self.get
for elem, count in iterable.items():
self[elem] = count + self_get(elem, 0)
else:
super(Counter, self).update(iterable) # fast path when counter is empty
else:
_count_elements(self, iterable)
if kwds:
self.update(kwds) def subtract(*args, **kwds):
'''Like dict.update() but subtracts counts instead of replacing them.
Counts can be reduced below zero. Both the inputs and outputs are
allowed to contain zero and negative counts. Source can be an iterable, a dictionary, or another Counter instance. >>> c = Counter('which')
>>> c.subtract('witch') # subtract elements from another iterable
>>> c.subtract(Counter('watch')) # subtract elements from another counter
>>> c['h'] # 2 in which, minus 1 in witch, minus 1 in watch
0
>>> c['w'] # 1 in which, minus 1 in witch, minus 1 in watch
-1 '''
if not args:
raise TypeError("descriptor 'subtract' of 'Counter' object "
"needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
iterable = args[0] if args else None
if iterable is not None:
self_get = self.get
if isinstance(iterable, Mapping):
for elem, count in iterable.items():
self[elem] = self_get(elem, 0) - count
else:
for elem in iterable:
self[elem] = self_get(elem, 0) - 1
if kwds:
self.subtract(kwds) def copy(self):
'Return a shallow copy.'
return self.__class__(self) def __reduce__(self):
return self.__class__, (dict(self),) def __delitem__(self, elem):
'Like dict.__delitem__() but does not raise KeyError for missing values.'
if elem in self:
super().__delitem__(elem) def __repr__(self):
if not self:
return '%s()' % self.__class__.__name__
try:
items = ', '.join(map('%r: %r'.__mod__, self.most_common()))
return '%s({%s})' % (self.__class__.__name__, items)
except TypeError:
# handle case where values are not orderable
return '{0}({1!r})'.format(self.__class__.__name__, dict(self)) # Multiset-style mathematical operations discussed in:
# Knuth TAOCP Volume II section 4.6.3 exercise 19
# and at http://en.wikipedia.org/wiki/Multiset
#
# Outputs guaranteed to only include positive counts.
#
# To strip negative and zero counts, add-in an empty counter:
# c += Counter() def __add__(self, other):
'''Add counts from two counters. >>> Counter('abbb') + Counter('bcc')
Counter({'b': 4, 'c': 2, 'a': 1}) '''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
newcount = count + other[elem]
if newcount > 0:
result[elem] = newcount
for elem, count in other.items():
if elem not in self and count > 0:
result[elem] = count
return result def __sub__(self, other):
''' Subtract count, but keep only results with positive counts. >>> Counter('abbbc') - Counter('bccd')
Counter({'b': 2, 'a': 1}) '''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
newcount = count - other[elem]
if newcount > 0:
result[elem] = newcount
for elem, count in other.items():
if elem not in self and count < 0:
result[elem] = 0 - count
return result def __or__(self, other):
'''Union is the maximum of value in either of the input counters. >>> Counter('abbb') | Counter('bcc')
Counter({'b': 3, 'c': 2, 'a': 1}) '''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
other_count = other[elem]
newcount = other_count if count < other_count else count
if newcount > 0:
result[elem] = newcount
for elem, count in other.items():
if elem not in self and count > 0:
result[elem] = count
return result def __and__(self, other):
''' Intersection is the minimum of corresponding counts. >>> Counter('abbb') & Counter('bcc')
Counter({'b': 1}) '''
if not isinstance(other, Counter):
return NotImplemented
result = Counter()
for elem, count in self.items():
other_count = other[elem]
newcount = count if count < other_count else other_count
if newcount > 0:
result[elem] = newcount
return result def __pos__(self):
'Adds an empty counter, effectively stripping negative and zero counts'
result = Counter()
for elem, count in self.items():
if count > 0:
result[elem] = count
return result def __neg__(self):
'''Subtracts from an empty counter. Strips positive and zero counts,
and flips the sign on negative counts. '''
result = Counter()
for elem, count in self.items():
if count < 0:
result[elem] = 0 - count
return result def _keep_positive(self):
'''Internal method to strip elements with a negative or zero count'''
nonpositive = [elem for elem, count in self.items() if not count > 0]
for elem in nonpositive:
del self[elem]
return self def __iadd__(self, other):
'''Inplace add from another counter, keeping only positive counts. >>> c = Counter('abbb')
>>> c += Counter('bcc')
>>> c
Counter({'b': 4, 'c': 2, 'a': 1}) '''
for elem, count in other.items():
self[elem] += count
return self._keep_positive() def __isub__(self, other):
'''Inplace subtract counter, but keep only results with positive counts. >>> c = Counter('abbbc')
>>> c -= Counter('bccd')
>>> c
Counter({'b': 2, 'a': 1}) '''
for elem, count in other.items():
self[elem] -= count
return self._keep_positive() def __ior__(self, other):
'''Inplace union is the maximum of value from either counter. >>> c = Counter('abbb')
>>> c |= Counter('bcc')
>>> c
Counter({'b': 3, 'c': 2, 'a': 1}) '''
for elem, other_count in other.items():
count = self[elem]
if other_count > count:
self[elem] = other_count
return self._keep_positive() def __iand__(self, other):
'''Inplace intersection is the minimum of corresponding counts. >>> c = Counter('abbb')
>>> c &= Counter('bcc')
>>> c
Counter({'b': 1}) '''
for elem, count in self.items():
other_count = other[elem]
if other_count < count:
self[elem] = other_count
return self._keep_positive()

统计列表

from collections import Counter

li = ['a', 'a', 'b', 'c', 'c', 'c', 'd']
count = Counter(li)
print(count) # Counter({'c': 3, 'a': 2, 'b': 1, 'd': 1})

统计字符串

from collections import Counter

count = Counter('asdfgasddfghasdg')
print(count) # Counter({'d': 4, 'a': 3, 's': 3, 'g': 3, 'f': 2, 'h': 1})

统计英文文章的10个高频单词

import re
from collections import Counter
from pprint import pprint with open('test.txt') as f:
txt = f.read()
c = Counter(re.split('\W+', txt))
res = c.most_common(10)
pprint(res)

update合并统计,参数是一个可迭代对象,Counter是dict的子类,所有也可以传一个Counter对象

from collections import Counter

count = Counter('abcaab')
count.update('ddddeccc')
print(count) # Counter({'c': 4, 'd': 4, 'a': 3, 'b': 2, 'e': 1}) count2 = Counter('abcaab')
count3 = Counter('ddddeccc')
count2.update(count3)
print(count2) # Counter({'c': 4, 'd': 4, 'a': 3, 'b': 2, 'e': 1})

most_common(n)统计高频前n个元素,用来解决top n的问题

from collections import Counter

count = Counter('abcaab')
print(count.most_common(2)) # [('a', 3), ('b', 2)]

most_common部分源码

def most_common(self, n=None):
'''List the n most common elements and their counts from the most
common to the least. If n is None, then list all element counts. >>> Counter('abcdeabcdabcaba').most_common(3)
[('a', 5), ('b', 4), ('c', 3)] '''
# Emulate Bag.sortedByCount from Smalltalk
if n is None:
return sorted(self.items(), key=_itemgetter(1), reverse=True)
return _heapq.nlargest(n, self.items(), key=_itemgetter(1))
# _heapq是python中的一个数据结构:堆

6、OrderedDict功能详解

OrderedDict是dict的一个子类

OrderedDict部分源码:

class OrderedDict(dict):
'Dictionary that remembers insertion order'
# An inherited dict maps keys to values.
# The inherited dict provides __getitem__, __len__, __contains__, and get.
# The remaining methods are order-aware.
# Big-O running times for all methods are the same as regular dictionaries. # The internal self.__map dict maps keys to links in a doubly linked list.
# The circular doubly linked list starts and ends with a sentinel element.
# The sentinel element never gets deleted (this simplifies the algorithm).
# The sentinel is in self.__hardroot with a weakref proxy in self.__root.
# The prev links are weakref proxies (to prevent circular references).
# Individual links are kept alive by the hard reference in self.__map.
# Those hard references disappear when a key is deleted from an OrderedDict. def __init__(*args, **kwds):
'''Initialize an ordered dictionary. The signature is the same as
regular dictionaries. Keyword argument order is preserved.
'''
if not args:
raise TypeError("descriptor '__init__' of 'OrderedDict' object "
"needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__root
except AttributeError:
self.__hardroot = _Link()
self.__root = root = _proxy(self.__hardroot)
root.prev = root.next = root
self.__map = {}
self.__update(*args, **kwds) def __setitem__(self, key, value,
dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link at the end of the linked list,
# and the inherited dictionary is updated with the new key/value pair.
if key not in self:
self.__map[key] = link = Link()
root = self.__root
last = root.prev
link.prev, link.next, link.key = last, root, key
last.next = link
root.prev = proxy(link)
dict_setitem(self, key, value) def __delitem__(self, key, dict_delitem=dict.__delitem__):
'od.__delitem__(y) <==> del od[y]'
# Deleting an existing item uses self.__map to find the link which gets
# removed by updating the links in the predecessor and successor nodes.
dict_delitem(self, key)
link = self.__map.pop(key)
link_prev = link.prev
link_next = link.next
link_prev.next = link_next
link_next.prev = link_prev
link.prev = None
link.next = None def __iter__(self):
'od.__iter__() <==> iter(od)'
# Traverse the linked list in order.
root = self.__root
curr = root.next
while curr is not root:
yield curr.key
curr = curr.next def __reversed__(self):
'od.__reversed__() <==> reversed(od)'
# Traverse the linked list in reverse order.
root = self.__root
curr = root.prev
while curr is not root:
yield curr.key
curr = curr.prev def clear(self):
'od.clear() -> None. Remove all items from od.'
root = self.__root
root.prev = root.next = root
self.__map.clear()
dict.clear(self) def popitem(self, last=True):
'''Remove and return a (key, value) pair from the dictionary. Pairs are returned in LIFO order if last is true or FIFO order if false.
'''
if not self:
raise KeyError('dictionary is empty')
root = self.__root
if last:
link = root.prev
link_prev = link.prev
link_prev.next = root
root.prev = link_prev
else:
link = root.next
link_next = link.next
root.next = link_next
link_next.prev = root
key = link.key
del self.__map[key]
value = dict.pop(self, key)
return key, value def move_to_end(self, key, last=True):
'''Move an existing element to the end (or beginning if last==False). Raises KeyError if the element does not exist.
When last=True, acts like a fast version of self[key]=self.pop(key). '''
link = self.__map[key]
link_prev = link.prev
link_next = link.next
soft_link = link_next.prev
link_prev.next = link_next
link_next.prev = link_prev
root = self.__root
if last:
last = root.prev
link.prev = last
link.next = root
root.prev = soft_link
last.next = link
else:
first = root.next
link.prev = root
link.next = first
first.prev = soft_link
root.next = link def __sizeof__(self):
sizeof = _sys.getsizeof
n = len(self) + 1 # number of links including root
size = sizeof(self.__dict__) # instance dictionary
size += sizeof(self.__map) * 2 # internal dict and inherited dict
size += sizeof(self.__hardroot) * n # link objects
size += sizeof(self.__root) * n # proxy objects
return size update = __update = MutableMapping.update def keys(self):
"D.keys() -> a set-like object providing a view on D's keys"
return _OrderedDictKeysView(self) def items(self):
"D.items() -> a set-like object providing a view on D's items"
return _OrderedDictItemsView(self) def values(self):
"D.values() -> an object providing a view on D's values"
return _OrderedDictValuesView(self) __ne__ = MutableMapping.__ne__ __marker = object() def pop(self, key, default=__marker):
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding
value. If key is not found, d is returned if given, otherwise KeyError
is raised. '''
if key in self:
result = self[key]
del self[key]
return result
if default is self.__marker:
raise KeyError(key)
return default def setdefault(self, key, default=None):
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
if key in self:
return self[key]
self[key] = default
return default @_recursive_repr()
def __repr__(self):
'od.__repr__() <==> repr(od)'
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, list(self.items())) def __reduce__(self):
'Return state information for pickling'
inst_dict = vars(self).copy()
for k in vars(OrderedDict()):
inst_dict.pop(k, None)
return self.__class__, (), inst_dict or None, None, iter(self.items()) def copy(self):
'od.copy() -> a shallow copy of od'
return self.__class__(self) @classmethod
def fromkeys(cls, iterable, value=None):
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
If not specified, the value defaults to None. '''
self = cls()
for key in iterable:
self[key] = value
return self def __eq__(self, other):
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
while comparison to a regular mapping is order-insensitive. '''
if isinstance(other, OrderedDict):
return dict.__eq__(self, other) and all(map(_eq, self, other))
return dict.__eq__(self, other)

OrderedDict是有序的,有序体现在,先添加的排在前面,后添加的排在后面

from collections import OrderedDict

user_dict = OrderedDict()
user_dict['b'] = '0xbug'
user_dict['a'] = '0bug'
user_dict['c'] = '1bug'
print(user_dict) # OrderedDict([('b', '0xbug'), ('a', '0bug'), ('c', '1bug')])

popitem 弹出最后一个组

from collections import OrderedDict

user_dict = OrderedDict()
user_dict['b'] = '0xbug'
user_dict['a'] = '0bug'
user_dict['c'] = '1bug'
print(user_dict) # OrderedDict([('b', '0xbug'), ('a', '0bug'), ('c', '1bug')]) print(user_dict.popitem()) # ('c', '1bug')
print(user_dict) # OrderedDict([('b', '0xbug'), ('a', '0bug')])

pop弹出指定元素,必须写参数

from collections import OrderedDict

user_dict = OrderedDict()
user_dict['b'] = '0xbug'
user_dict['a'] = '0bug'
user_dict['c'] = '1bug'
print(user_dict) # OrderedDict([('b', '0xbug'), ('a', '0bug'), ('c', '1bug')]) print(user_dict.pop('a')) # 0bug
print(user_dict) # OrderedDict([('b', '0xbug'), ('c', '1bug')])

move_to_end将指定元素移至最后

from collections import OrderedDict

user_dict = OrderedDict()
user_dict['b'] = '0xbug'
user_dict['a'] = '0bug'
user_dict['c'] = '1bug'
print(user_dict) # OrderedDict([('b', '0xbug'), ('a', '0bug'), ('c', '1bug')]) print(user_dict.move_to_end('a')) # 0bug
print(user_dict) # OrderedDict([('b', '0xbug'), ('c', '1bug'), ('a', '0bug')])

7、ChainMap功能详解

ChainMap可以让我们访问多个字典像访问一个字典一样进行操作,实质是指向关系而不是复制。如果key值重复,循环遍历的时候只算第一个

from collections import ChainMap

user_dict1 = {'a': '0bug', 'b': '1bug'}
user_dict2 = {'b': '2bug', 'c': '3bug', 'd': '4bug'}
new_dict = ChainMap(user_dict1, user_dict2)
print(new_dict) # ChainMap({'a': '0bug', 'b': '1bug'}, {'b': '2bug', 'c': '3bug', 'd': '4bug'}) for key, value in new_dict.items():
print(key, value) """
c 3bug
d 4bug
b 1bug
a 0bug
"""

maps返回列表形式组合

from collections import ChainMap

user_dict1 = {'a': '0bug', 'b': '1bug'}
user_dict2 = {'b': '2bug', 'c': '3bug', 'd': '4bug'}
new_dict = ChainMap(user_dict1, user_dict2)
print(new_dict.maps) # [{'a': '0bug', 'b': '1bug'}, {'b': '2bug', 'c': '3bug', 'd': '4bug'}]
new_dict.maps[0]['a'] = 'aaaa'
print(new_dict) # ChainMap({'a': 'aaaa', 'b': '1bug'}, {'b': '2bug', 'c': '3bug', 'd': '4bug'})

  

上一篇:asp.net环境搭建


下一篇:D2.Reactjs 操作事件、状态改变、路由