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'})
Python 模块collections的更多相关文章
- python模块--collections
python的内建模块collections有几个关键的数据结构,平常在使用的时候,开发者可以直接调用,不需要自己重复制造轮子,这样可以提高开发效率. 1. deque双端队列 平常我们使用的pyth ...
- python模块collections中namedtuple()的理解
Python中存储系列数据,比较常见的数据类型有list,除此之外,还有tuple数据类型.相比与list,tuple中的元素不可修改,在映射中可以当键使用.tuple元组的item只能通过index ...
- 不可不知的Python模块: collections
原文:http://www.zlovezl.cn/articles/collections-in-python/ Python作为一个“内置电池”的编程语言,标准库里面拥有非常多好用的模块.比如今天想 ...
- python模块--collections(容器数据类型)
Counter类(dict的子类, 计数器) 方法 返回值类型 说明 __init__ Counter 传入可迭代对象, 会对对象中的值进行计数, 值为键, 计数为值 .elements() 迭代器 ...
- Python中collections模块
目录 Python中collections模块 Counter defaultdict OrderedDict namedtuple deque ChainMap Python中collections ...
- Python之常用模块--collections模块
认识模块 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编写的 ...
- 【转】python模块分析之collections(六)
[转]python模块分析之collections(六) collections是Python内建的一个集合模块,提供了许多有用的集合类. 系列文章 python模块分析之random(一) pyth ...
- Python的collections模块中namedtuple结构使用示例
namedtuple顾名思义,就是名字+元组的数据结构,下面就来看一下Python的collections模块中namedtuple结构使用示例 namedtuple 就是命名的 tuple,比较 ...
- python常用模块collections os random sys
Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句. 模块让你能够有逻辑地组织你的 Python 代码段. 把相关的代码 ...
随机推荐
- SQL SERVER 事务的使用(tran)
sql server事务的使用是为了确保数据的一致性. 通常写法 begin tran --sql 语句1 --sql 语句2 --sql 语句3 commit tran 上面写法存在隐患,当操作(增 ...
- MySQL三层循环
begindeclare i int; #定义i变量declare j int; #定义j变量declare k int; #定义k变量set i=1;set j=1;set k=1;while ...
- Excel中针对IP地址的排序方法
新建一个辅助排序列,用辅助列来扩展,辅助列公式如下: =TRIM(TEXT(LEFT(SUBSTITUTE(A1,".",REPT(" ",99)),100), ...
- python全栈开发笔记---------数据类型---字典方法
def clear(self) 清空字典里所有元素 # info = { # "k1":18, # "k2":True, # "k3":[ ...
- vuex-Mutation(同步)
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation.Vuex 中的 mutation 非常类似于事件: 每个 mutation 都有一个字符串的 事件类型 (type) 和 一 ...
- mariadb安装和一些sql基础
MariaDB安装 yum -y install mariadb mariadb-server 启动 systemctl start mariadb systemctl enabl ...
- svn提示文件 is already locked
有时候在提交代码或者更新代码的时候svn会报错误,提示请执行"clean up",但是有时候执行"clean up"也没有什么用,不过当执行"clea ...
- kafka 分区和副本以及kafaka 执行流程,以及消息的高可用
1.Kafka概览 Apache下的项目Kafka(卡夫卡)是一个分布式流处理平台,它的流行是因为卡夫卡系统的设计和操作简单,能充分利用磁盘的顺序读写特性.kafka每秒钟能有百万条消息的吞吐量,因此 ...
- windows 重装系统
转载:https://blog.csdn.net/qq_41137650/article/details/80035921 老毛桃 大白菜都可以 电脑系统安装步骤我选的是用U盘做的系统 如果本计系 ...
- I/O简介
用户空间是常规进程所在区域.JVM就是常规进程,驻守于用户空间.用户空间是非特权区域,在该区域执行的代码不能直接访问硬件设备. 内核空间是操作系统所在区域.内核代码有特别的权利:它能与设备控制器通讯, ...