'''This module implements specialized container datatypes providingalternatives to Python's general purpose built-in containers, dict,list, set, and tuple.* namedtuple factory function for creating tuple subclasses with named fields* deque list-like container with fast appends and pops on either end* ChainMap dict-like class for creating a single view of multiple mappings* Counter dict subclass for counting hashable objects* OrderedDict dict subclass that remembers the order entries were added* defaultdict dict subclass that calls a factory function to supply missing values* UserDict wrapper around dictionary objects for easier dict subclassing* UserList wrapper around list objects for easier list subclassing* UserString wrapper around string objects for easier string subclassing'''__all__=['ChainMap','Counter','OrderedDict','UserDict','UserList','UserString','defaultdict','deque','namedtuple',]import_collections_abcimportsysas_sysfromitertoolsimportchainas_chainfromitertoolsimportrepeatas_repeatfromitertoolsimportstarmapas_starmapfromkeywordimportiskeywordas_iskeywordfromoperatorimporteqas_eqfromoperatorimportitemgetteras_itemgetterfromreprlibimportrecursive_repras_recursive_reprfrom_weakrefimportproxyas_proxytry:from_collectionsimportdequeexceptImportError:passelse:_collections_abc.MutableSequence.register(deque)try:from_collectionsimport_deque_iteratorexceptImportError:passtry:from_collectionsimportdefaultdictexceptImportError:pass################################################################################### OrderedDict################################################################################class_OrderedDictKeysView(_collections_abc.KeysView):def__reversed__(self):yield fromreversed(self._mapping)class_OrderedDictItemsView(_collections_abc.ItemsView):def__reversed__(self):forkeyinreversed(self._mapping):yield(key,self._mapping[key])class_OrderedDictValuesView(_collections_abc.ValuesView):def__reversed__(self):forkeyinreversed(self._mapping):yieldself._mapping[key]class_Link(object):__slots__='prev','next','key','__weakref__'classOrderedDict(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__new__(cls,/,*args,**kwds):"Create the ordered dict object and set up the underlying structures."self=dict.__new__(cls)self.__hardroot=_Link()self.__root=root=_proxy(self.__hardroot)root.prev=root.next=rootself.__map={}returnselfdef__init__(self,other=(),/,**kwds):'''Initialize an ordered dictionary. The signature is the same as regular dictionaries. Keyword argument order is preserved. '''self.__update(other,**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.ifkeynotinself:self.__map[key]=link=Link()root=self.__rootlast=root.prevlink.prev,link.next,link.key=last,root,keylast.next=linkroot.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.prevlink_next=link.nextlink_prev.next=link_nextlink_next.prev=link_prevlink.prev=Nonelink.next=Nonedef__iter__(self):'od.__iter__() <==> iter(od)'# Traverse the linked list in order.root=self.__rootcurr=root.nextwhilecurrisnotroot:yieldcurr.keycurr=curr.nextdef__reversed__(self):'od.__reversed__() <==> reversed(od)'# Traverse the linked list in reverse order.root=self.__rootcurr=root.prevwhilecurrisnotroot:yieldcurr.keycurr=curr.prevdefclear(self):'od.clear() -> None. Remove all items from od.'root=self.__rootroot.prev=root.next=rootself.__map.clear()dict.clear(self)defpopitem(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. '''ifnotself:raiseKeyError('dictionary is empty')root=self.__rootiflast:link=root.prevlink_prev=link.prevlink_prev.next=rootroot.prev=link_prevelse:link=root.nextlink_next=link.nextroot.next=link_nextlink_next.prev=rootkey=link.keydelself.__map[key]value=dict.pop(self,key)returnkey,valuedefmove_to_end(self,key,last=True):'''Move an existing element to the end (or beginning if last is false). Raise KeyError if the element does not exist. '''link=self.__map[key]link_prev=link.prevlink_next=link.nextsoft_link=link_next.prevlink_prev.next=link_nextlink_next.prev=link_prevroot=self.__rootiflast:last=root.prevlink.prev=lastlink.next=rootroot.prev=soft_linklast.next=linkelse:first=root.nextlink.prev=rootlink.next=firstfirst.prev=soft_linkroot.next=linkdef__sizeof__(self):sizeof=_sys.getsizeofn=len(self)+1# number of links including rootsize=sizeof(self.__dict__)# instance dictionarysize+=sizeof(self.__map)*2# internal dict and inherited dictsize+=sizeof(self.__hardroot)*n# link objectssize+=sizeof(self.__root)*n# proxy objectsreturnsizeupdate=__update=_collections_abc.MutableMapping.updatedefkeys(self):"D.keys() -> a set-like object providing a view on D's keys"return_OrderedDictKeysView(self)defitems(self):"D.items() -> a set-like object providing a view on D's items"return_OrderedDictItemsView(self)defvalues(self):"D.values() -> an object providing a view on D's values"return_OrderedDictValuesView(self)__ne__=_collections_abc.MutableMapping.__ne____marker=object()defpop(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. '''marker=self.__markerresult=dict.pop(self,key,marker)ifresultisnotmarker:# The same as in __delitem__().link=self.__map.pop(key)link_prev=link.prevlink_next=link.nextlink_prev.next=link_nextlink_next.prev=link_prevlink.prev=Nonelink.next=Nonereturnresultifdefaultismarker:raiseKeyError(key)returndefaultdefsetdefault(self,key,default=None):'''Insert key with a value of default if key is not in the dictionary. Return the value for key if key is in the dictionary, else default. '''ifkeyinself:returnself[key]self[key]=defaultreturndefault@_recursive_repr()def__repr__(self):'od.__repr__() <==> repr(od)'ifnotself:return'%s()'%(self.__class__.__name__,)return'%s(%r)'%(self.__class__.__name__,dict(self.items()))def__reduce__(self):'Return state information for pickling'state=self.__getstate__()ifstate:ifisinstance(state,tuple):state,slots=stateelse:slots={}state=state.copy()slots=slots.copy()forkinvars(OrderedDict()):state.pop(k,None)slots.pop(k,None)ifslots:state=state,slotselse:state=stateorNonereturnself.__class__,(),state,None,iter(self.items())defcopy(self):'od.copy() -> a shallow copy of od'returnself.__class__(self)@classmethoddeffromkeys(cls,iterable,value=None):'''Create a new ordered dictionary with keys from iterable and values set to value. '''self=cls()forkeyiniterable:self[key]=valuereturnselfdef__eq__(self,other):'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive while comparison to a regular mapping is order-insensitive. '''ifisinstance(other,OrderedDict):returndict.__eq__(self,other)andall(map(_eq,self,other))returndict.__eq__(self,other)def__ior__(self,other):self.update(other)returnselfdef__or__(self,other):ifnotisinstance(other,dict):returnNotImplementednew=self.__class__(self)new.update(other)returnnewdef__ror__(self,other):ifnotisinstance(other,dict):returnNotImplementednew=self.__class__(other)new.update(self)returnnewtry:from_collectionsimportOrderedDictexceptImportError:# Leave the pure Python version in place.pass################################################################################### namedtuple################################################################################try:from_collectionsimport_tuplegetterexceptImportError:_tuplegetter=lambdaindex,doc:property(_itemgetter(index),doc=doc)
[文档]defnamedtuple(typename,field_names,*,rename=False,defaults=None,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.ifisinstance(field_names,str):field_names=field_names.replace(',',' ').split()field_names=list(map(str,field_names))typename=_sys.intern(str(typename))ifrename:seen=set()forindex,nameinenumerate(field_names):if(notname.isidentifier()or_iskeyword(name)orname.startswith('_')ornameinseen):field_names[index]=f'_{index}'seen.add(name)fornamein[typename]+field_names:iftype(name)isnotstr:raiseTypeError('Type names and field names must be strings')ifnotname.isidentifier():raiseValueError('Type names and field names must be valid 'f'identifiers: {name!r}')if_iskeyword(name):raiseValueError('Type names and field names cannot be a 'f'keyword: {name!r}')seen=set()fornameinfield_names:ifname.startswith('_')andnotrename:raiseValueError('Field names cannot start with an underscore: 'f'{name!r}')ifnameinseen:raiseValueError(f'Encountered duplicate field name: {name!r}')seen.add(name)field_defaults={}ifdefaultsisnotNone:defaults=tuple(defaults)iflen(defaults)>len(field_names):raiseTypeError('Got more default values than field names')field_defaults=dict(reversed(list(zip(reversed(field_names),reversed(defaults)))))# Variables used in the methods and docstringsfield_names=tuple(map(_sys.intern,field_names))num_fields=len(field_names)arg_list=', '.join(field_names)ifnum_fields==1:arg_list+=','repr_fmt='('+', '.join(f'{name}=%r'fornameinfield_names)+')'tuple_new=tuple.__new___dict,_tuple,_len,_map,_zip=dict,tuple,len,map,zip# Create all the named tuple methods to be added to the class namespacenamespace={'_tuple_new':tuple_new,'__builtins__':{},'__name__':f'namedtuple_{typename}',}code=f'lambda _cls, {arg_list}: _tuple_new(_cls, ({arg_list}))'__new__=eval(code,namespace)__new__.__name__='__new__'__new__.__doc__=f'Create new instance of {typename}({arg_list})'ifdefaultsisnotNone:__new__.__defaults__=defaults@classmethoddef_make(cls,iterable):result=tuple_new(cls,iterable)if_len(result)!=num_fields:raiseTypeError(f'Expected {num_fields} arguments, got {len(result)}')returnresult_make.__func__.__doc__=(f'Make a new {typename} object from a sequence ''or iterable')def_replace(self,/,**kwds):result=self._make(_map(kwds.pop,field_names,self))ifkwds:raiseValueError(f'Got unexpected field names: {list(kwds)!r}')returnresult_replace.__doc__=(f'Return a new {typename} object replacing specified ''fields with new values')def__repr__(self):'Return a nicely formatted representation string'returnself.__class__.__name__+repr_fmt%selfdef_asdict(self):'Return a new dict which maps field names to their values.'return_dict(_zip(self._fields,self))def__getnewargs__(self):'Return self as a plain tuple. Used by copy and pickle.'return_tuple(self)# Modify function metadata to help with introspection and debuggingformethodin(__new__,_make.__func__,_replace,__repr__,_asdict,__getnewargs__,):method.__qualname__=f'{typename}.{method.__name__}'# Build-up the class namespace dictionary# and use type() to build the result classclass_namespace={'__doc__':f'{typename}({arg_list})','__slots__':(),'_fields':field_names,'_field_defaults':field_defaults,'__new__':__new__,'_make':_make,'_replace':_replace,'__repr__':__repr__,'_asdict':_asdict,'__getnewargs__':__getnewargs__,'__match_args__':field_names,}forindex,nameinenumerate(field_names):doc=_sys.intern(f'Alias for field number {index}')class_namespace[name]=_tuplegetter(index,doc)result=type(typename,(tuple,),class_namespace)# 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.ifmoduleisNone:try:module=_sys._getframemodulename(1)or'__main__'exceptAttributeError:try:module=_sys._getframe(1).f_globals.get('__name__','__main__')except(AttributeError,ValueError):passifmoduleisnotNone:result.__module__=modulereturnresult
########################################################################### Counter########################################################################def_count_elements(mapping,iterable):'Tally elements from the iterable.'mapping_get=mapping.getforeleminiterable:mapping[elem]=mapping_get(elem,0)+1try:# Load C helper function if availablefrom_collectionsimport_count_elementsexceptImportError:passclassCounter(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.3def__init__(self,iterable=None,/,**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 '''super().__init__()self.update(iterable,**kwds)def__missing__(self,key):'The count of elements not in the Counter is zero.'# Needed so that self[missing_item] does not raise KeyErrorreturn0deftotal(self):'Sum of the counts'returnsum(self.values())defmost_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('abracadabra').most_common(3) [('a', 5), ('b', 2), ('r', 2)] '''# Emulate Bag.sortedByCount from SmalltalkifnisNone:returnsorted(self.items(),key=_itemgetter(1),reverse=True)# Lazy import to speedup Python startup timeimportheapqreturnheapq.nlargest(n,self.items(),key=_itemgetter(1))defelements(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 >>> import math >>> prime_factors = Counter({2: 2, 3: 3, 17: 1}) >>> math.prod(prime_factors.elements()) 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@classmethoddeffromkeys(cls,iterable,v=None):# There is no equivalent method for counters because the semantics# would be ambiguous in cases such as Counter.fromkeys('aaabbc', v=2).# Initializing counters to zero values isn't necessary because zero# is already the default value for counter lookups. Initializing# to one is easily accomplished with Counter(set(iterable)). For# more exotic cases, create a dictionary first using a dictionary# comprehension or dict.fromkeys().raiseNotImplementedError('Counter.fromkeys() is undefined. Use Counter(iterable) instead.')defupdate(self,iterable=None,/,**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 some of the 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.ifiterableisnotNone:ifisinstance(iterable,_collections_abc.Mapping):ifself:self_get=self.getforelem,countiniterable.items():self[elem]=count+self_get(elem,0)else:# fast path when counter is emptysuper().update(iterable)else:_count_elements(self,iterable)ifkwds:self.update(kwds)defsubtract(self,iterable=None,/,**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 '''ifiterableisnotNone:self_get=self.getifisinstance(iterable,_collections_abc.Mapping):forelem,countiniterable.items():self[elem]=self_get(elem,0)-countelse:foreleminiterable:self[elem]=self_get(elem,0)-1ifkwds:self.subtract(kwds)defcopy(self):'Return a shallow copy.'returnself.__class__(self)def__reduce__(self):returnself.__class__,(dict(self),)def__delitem__(self,elem):'Like dict.__delitem__() but does not raise KeyError for missing values.'ifeleminself:super().__delitem__(elem)def__repr__(self):ifnotself:returnf'{self.__class__.__name__}()'try:# dict() preserves the ordering returned by most_common()d=dict(self.most_common())exceptTypeError:# handle case where values are not orderabled=dict(self)returnf'{self.__class__.__name__}({d!r})'# 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()## Results are ordered according to when an element is first# encountered in the left operand and then by the order# encountered in the right operand.## When the multiplicities are all zero or one, multiset operations# are guaranteed to be equivalent to the corresponding operations# for regular sets.# Given counter multisets such as:# cp = Counter(a=1, b=0, c=1)# cq = Counter(c=1, d=0, e=1)# The corresponding regular sets would be:# sp = {'a', 'c'}# sq = {'c', 'e'}# All of the following relations would hold:# set(cp + cq) == sp | sq# set(cp - cq) == sp - sq# set(cp | cq) == sp | sq# set(cp & cq) == sp & sq# (cp == cq) == (sp == sq)# (cp != cq) == (sp != sq)# (cp <= cq) == (sp <= sq)# (cp < cq) == (sp < sq)# (cp >= cq) == (sp >= sq)# (cp > cq) == (sp > sq)def__eq__(self,other):'True if all counts agree. Missing counts are treated as zero.'ifnotisinstance(other,Counter):returnNotImplementedreturnall(self[e]==other[e]forcin(self,other)foreinc)def__ne__(self,other):'True if any counts disagree. Missing counts are treated as zero.'ifnotisinstance(other,Counter):returnNotImplementedreturnnotself==otherdef__le__(self,other):'True if all counts in self are a subset of those in other.'ifnotisinstance(other,Counter):returnNotImplementedreturnall(self[e]<=other[e]forcin(self,other)foreinc)def__lt__(self,other):'True if all counts in self are a proper subset of those in other.'ifnotisinstance(other,Counter):returnNotImplementedreturnself<=otherandself!=otherdef__ge__(self,other):'True if all counts in self are a superset of those in other.'ifnotisinstance(other,Counter):returnNotImplementedreturnall(self[e]>=other[e]forcin(self,other)foreinc)def__gt__(self,other):'True if all counts in self are a proper superset of those in other.'ifnotisinstance(other,Counter):returnNotImplementedreturnself>=otherandself!=otherdef__add__(self,other):'''Add counts from two counters. >>> Counter('abbb') + Counter('bcc') Counter({'b': 4, 'c': 2, 'a': 1}) '''ifnotisinstance(other,Counter):returnNotImplementedresult=Counter()forelem,countinself.items():newcount=count+other[elem]ifnewcount>0:result[elem]=newcountforelem,countinother.items():ifelemnotinselfandcount>0:result[elem]=countreturnresultdef__sub__(self,other):''' Subtract count, but keep only results with positive counts. >>> Counter('abbbc') - Counter('bccd') Counter({'b': 2, 'a': 1}) '''ifnotisinstance(other,Counter):returnNotImplementedresult=Counter()forelem,countinself.items():newcount=count-other[elem]ifnewcount>0:result[elem]=newcountforelem,countinother.items():ifelemnotinselfandcount<0:result[elem]=0-countreturnresultdef__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}) '''ifnotisinstance(other,Counter):returnNotImplementedresult=Counter()forelem,countinself.items():other_count=other[elem]newcount=other_countifcount<other_countelsecountifnewcount>0:result[elem]=newcountforelem,countinother.items():ifelemnotinselfandcount>0:result[elem]=countreturnresultdef__and__(self,other):''' Intersection is the minimum of corresponding counts. >>> Counter('abbb') & Counter('bcc') Counter({'b': 1}) '''ifnotisinstance(other,Counter):returnNotImplementedresult=Counter()forelem,countinself.items():other_count=other[elem]newcount=countifcount<other_countelseother_countifnewcount>0:result[elem]=newcountreturnresultdef__pos__(self):'Adds an empty counter, effectively stripping negative and zero counts'result=Counter()forelem,countinself.items():ifcount>0:result[elem]=countreturnresultdef__neg__(self):'''Subtracts from an empty counter. Strips positive and zero counts, and flips the sign on negative counts. '''result=Counter()forelem,countinself.items():ifcount<0:result[elem]=0-countreturnresultdef_keep_positive(self):'''Internal method to strip elements with a negative or zero count'''nonpositive=[elemforelem,countinself.items()ifnotcount>0]foreleminnonpositive:delself[elem]returnselfdef__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}) '''forelem,countinother.items():self[elem]+=countreturnself._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}) '''forelem,countinother.items():self[elem]-=countreturnself._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}) '''forelem,other_countinother.items():count=self[elem]ifother_count>count:self[elem]=other_countreturnself._keep_positive()def__iand__(self,other):'''Inplace intersection is the minimum of corresponding counts. >>> c = Counter('abbb') >>> c &= Counter('bcc') >>> c Counter({'b': 1}) '''forelem,countinself.items():other_count=other[elem]ifother_count<count:self[elem]=other_countreturnself._keep_positive()########################################################################### ChainMap########################################################################classChainMap(_collections_abc.MutableMapping):''' A ChainMap groups multiple dicts (or other mappings) together to create a single, updateable view. The underlying mappings are stored in a list. That list is public and can be accessed or updated using the *maps* attribute. There is no other state. Lookups search the underlying mappings successively until a key is found. In contrast, writes, updates, and deletions only operate on the first mapping. '''def__init__(self,*maps):'''Initialize a ChainMap by setting *maps* to the given mappings. If no mappings are provided, a single empty dictionary is used. '''self.maps=list(maps)or[{}]# always at least one mapdef__missing__(self,key):raiseKeyError(key)def__getitem__(self,key):formappinginself.maps:try:returnmapping[key]# can't use 'key in mapping' with defaultdictexceptKeyError:passreturnself.__missing__(key)# support subclasses that define __missing__defget(self,key,default=None):returnself[key]ifkeyinselfelsedefaultdef__len__(self):returnlen(set().union(*self.maps))# reuses stored hash values if possibledef__iter__(self):d={}formappinginmap(dict.fromkeys,reversed(self.maps)):d|=mapping# reuses stored hash values if possiblereturniter(d)def__contains__(self,key):returnany(keyinmforminself.maps)def__bool__(self):returnany(self.maps)@_recursive_repr()def__repr__(self):returnf'{self.__class__.__name__}({", ".join(map(repr,self.maps))})'@classmethoddeffromkeys(cls,iterable,*args):'Create a ChainMap with a single dict created from the iterable.'returncls(dict.fromkeys(iterable,*args))defcopy(self):'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'returnself.__class__(self.maps[0].copy(),*self.maps[1:])__copy__=copydefnew_child(self,m=None,**kwargs):# like Django's Context.push()'''New ChainMap with a new map followed by all previous maps. If no map is provided, an empty dict is used. Keyword arguments update the map or new empty dict. '''ifmisNone:m=kwargselifkwargs:m.update(kwargs)returnself.__class__(m,*self.maps)@propertydefparents(self):# like Django's Context.pop()'New ChainMap from maps[1:].'returnself.__class__(*self.maps[1:])def__setitem__(self,key,value):self.maps[0][key]=valuedef__delitem__(self,key):try:delself.maps[0][key]exceptKeyError:raiseKeyError(f'Key not found in the first mapping: {key!r}')defpopitem(self):'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.'try:returnself.maps[0].popitem()exceptKeyError:raiseKeyError('No keys found in the first mapping.')defpop(self,key,*args):'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].'try:returnself.maps[0].pop(key,*args)exceptKeyError:raiseKeyError(f'Key not found in the first mapping: {key!r}')defclear(self):'Clear maps[0], leaving maps[1:] intact.'self.maps[0].clear()def__ior__(self,other):self.maps[0].update(other)returnselfdef__or__(self,other):ifnotisinstance(other,_collections_abc.Mapping):returnNotImplementedm=self.copy()m.maps[0].update(other)returnmdef__ror__(self,other):ifnotisinstance(other,_collections_abc.Mapping):returnNotImplementedm=dict(other)forchildinreversed(self.maps):m.update(child)returnself.__class__(m)################################################################################### UserDict################################################################################classUserDict(_collections_abc.MutableMapping):# Start by filling-out the abstract methodsdef__init__(self,dict=None,/,**kwargs):self.data={}ifdictisnotNone:self.update(dict)ifkwargs:self.update(kwargs)def__len__(self):returnlen(self.data)def__getitem__(self,key):ifkeyinself.data:returnself.data[key]ifhasattr(self.__class__,"__missing__"):returnself.__class__.__missing__(self,key)raiseKeyError(key)def__setitem__(self,key,item):self.data[key]=itemdef__delitem__(self,key):delself.data[key]def__iter__(self):returniter(self.data)# Modify __contains__ and get() to work like dict# does when __missing__ is present.def__contains__(self,key):returnkeyinself.datadefget(self,key,default=None):ifkeyinself:returnself[key]returndefault# Now, add the methods in dicts but not in MutableMappingdef__repr__(self):returnrepr(self.data)def__or__(self,other):ifisinstance(other,UserDict):returnself.__class__(self.data|other.data)ifisinstance(other,dict):returnself.__class__(self.data|other)returnNotImplementeddef__ror__(self,other):ifisinstance(other,UserDict):returnself.__class__(other.data|self.data)ifisinstance(other,dict):returnself.__class__(other|self.data)returnNotImplementeddef__ior__(self,other):ifisinstance(other,UserDict):self.data|=other.dataelse:self.data|=otherreturnselfdef__copy__(self):inst=self.__class__.__new__(self.__class__)inst.__dict__.update(self.__dict__)# Create a copy and avoid triggering descriptorsinst.__dict__["data"]=self.__dict__["data"].copy()returninstdefcopy(self):ifself.__class__isUserDict:returnUserDict(self.data.copy())importcopydata=self.datatry:self.data={}c=copy.copy(self)finally:self.data=datac.update(self)returnc@classmethoddeffromkeys(cls,iterable,value=None):d=cls()forkeyiniterable:d[key]=valuereturnd################################################################################### UserList################################################################################classUserList(_collections_abc.MutableSequence):"""A more or less complete user-defined wrapper around list objects."""def__init__(self,initlist=None):self.data=[]ifinitlistisnotNone:# XXX should this accept an arbitrary sequence?iftype(initlist)==type(self.data):self.data[:]=initlistelifisinstance(initlist,UserList):self.data[:]=initlist.data[:]else:self.data=list(initlist)def__repr__(self):returnrepr(self.data)def__lt__(self,other):returnself.data<self.__cast(other)def__le__(self,other):returnself.data<=self.__cast(other)def__eq__(self,other):returnself.data==self.__cast(other)def__gt__(self,other):returnself.data>self.__cast(other)def__ge__(self,other):returnself.data>=self.__cast(other)def__cast(self,other):returnother.dataifisinstance(other,UserList)elseotherdef__contains__(self,item):returniteminself.datadef__len__(self):returnlen(self.data)def__getitem__(self,i):ifisinstance(i,slice):returnself.__class__(self.data[i])else:returnself.data[i]def__setitem__(self,i,item):self.data[i]=itemdef__delitem__(self,i):delself.data[i]def__add__(self,other):ifisinstance(other,UserList):returnself.__class__(self.data+other.data)elifisinstance(other,type(self.data)):returnself.__class__(self.data+other)returnself.__class__(self.data+list(other))def__radd__(self,other):ifisinstance(other,UserList):returnself.__class__(other.data+self.data)elifisinstance(other,type(self.data)):returnself.__class__(other+self.data)returnself.__class__(list(other)+self.data)def__iadd__(self,other):ifisinstance(other,UserList):self.data+=other.dataelifisinstance(other,type(self.data)):self.data+=otherelse:self.data+=list(other)returnselfdef__mul__(self,n):returnself.__class__(self.data*n)__rmul__=__mul__def__imul__(self,n):self.data*=nreturnselfdef__copy__(self):inst=self.__class__.__new__(self.__class__)inst.__dict__.update(self.__dict__)# Create a copy and avoid triggering descriptorsinst.__dict__["data"]=self.__dict__["data"][:]returninstdefappend(self,item):self.data.append(item)definsert(self,i,item):self.data.insert(i,item)defpop(self,i=-1):returnself.data.pop(i)defremove(self,item):self.data.remove(item)defclear(self):self.data.clear()defcopy(self):returnself.__class__(self)defcount(self,item):returnself.data.count(item)defindex(self,item,*args):returnself.data.index(item,*args)defreverse(self):self.data.reverse()defsort(self,/,*args,**kwds):self.data.sort(*args,**kwds)defextend(self,other):ifisinstance(other,UserList):self.data.extend(other.data)else:self.data.extend(other)################################################################################### UserString################################################################################classUserString(_collections_abc.Sequence):def__init__(self,seq):ifisinstance(seq,str):self.data=seqelifisinstance(seq,UserString):self.data=seq.data[:]else:self.data=str(seq)def__str__(self):returnstr(self.data)def__repr__(self):returnrepr(self.data)def__int__(self):returnint(self.data)def__float__(self):returnfloat(self.data)def__complex__(self):returncomplex(self.data)def__hash__(self):returnhash(self.data)def__getnewargs__(self):return(self.data[:],)def__eq__(self,string):ifisinstance(string,UserString):returnself.data==string.datareturnself.data==stringdef__lt__(self,string):ifisinstance(string,UserString):returnself.data<string.datareturnself.data<stringdef__le__(self,string):ifisinstance(string,UserString):returnself.data<=string.datareturnself.data<=stringdef__gt__(self,string):ifisinstance(string,UserString):returnself.data>string.datareturnself.data>stringdef__ge__(self,string):ifisinstance(string,UserString):returnself.data>=string.datareturnself.data>=stringdef__contains__(self,char):ifisinstance(char,UserString):char=char.datareturncharinself.datadef__len__(self):returnlen(self.data)def__getitem__(self,index):returnself.__class__(self.data[index])def__add__(self,other):ifisinstance(other,UserString):returnself.__class__(self.data+other.data)elifisinstance(other,str):returnself.__class__(self.data+other)returnself.__class__(self.data+str(other))def__radd__(self,other):ifisinstance(other,str):returnself.__class__(other+self.data)returnself.__class__(str(other)+self.data)def__mul__(self,n):returnself.__class__(self.data*n)__rmul__=__mul__def__mod__(self,args):returnself.__class__(self.data%args)def__rmod__(self,template):returnself.__class__(str(template)%self)# the following methods are defined in alphabetical order:defcapitalize(self):returnself.__class__(self.data.capitalize())defcasefold(self):returnself.__class__(self.data.casefold())defcenter(self,width,*args):returnself.__class__(self.data.center(width,*args))defcount(self,sub,start=0,end=_sys.maxsize):ifisinstance(sub,UserString):sub=sub.datareturnself.data.count(sub,start,end)defremoveprefix(self,prefix,/):ifisinstance(prefix,UserString):prefix=prefix.datareturnself.__class__(self.data.removeprefix(prefix))defremovesuffix(self,suffix,/):ifisinstance(suffix,UserString):suffix=suffix.datareturnself.__class__(self.data.removesuffix(suffix))defencode(self,encoding='utf-8',errors='strict'):encoding='utf-8'ifencodingisNoneelseencodingerrors='strict'iferrorsisNoneelseerrorsreturnself.data.encode(encoding,errors)defendswith(self,suffix,start=0,end=_sys.maxsize):returnself.data.endswith(suffix,start,end)defexpandtabs(self,tabsize=8):returnself.__class__(self.data.expandtabs(tabsize))deffind(self,sub,start=0,end=_sys.maxsize):ifisinstance(sub,UserString):sub=sub.datareturnself.data.find(sub,start,end)defformat(self,/,*args,**kwds):returnself.data.format(*args,**kwds)defformat_map(self,mapping):returnself.data.format_map(mapping)defindex(self,sub,start=0,end=_sys.maxsize):returnself.data.index(sub,start,end)defisalpha(self):returnself.data.isalpha()defisalnum(self):returnself.data.isalnum()defisascii(self):returnself.data.isascii()defisdecimal(self):returnself.data.isdecimal()defisdigit(self):returnself.data.isdigit()defisidentifier(self):returnself.data.isidentifier()defislower(self):returnself.data.islower()defisnumeric(self):returnself.data.isnumeric()defisprintable(self):returnself.data.isprintable()defisspace(self):returnself.data.isspace()defistitle(self):returnself.data.istitle()defisupper(self):returnself.data.isupper()defjoin(self,seq):returnself.data.join(seq)defljust(self,width,*args):returnself.__class__(self.data.ljust(width,*args))deflower(self):returnself.__class__(self.data.lower())deflstrip(self,chars=None):returnself.__class__(self.data.lstrip(chars))maketrans=str.maketransdefpartition(self,sep):returnself.data.partition(sep)defreplace(self,old,new,maxsplit=-1):ifisinstance(old,UserString):old=old.dataifisinstance(new,UserString):new=new.datareturnself.__class__(self.data.replace(old,new,maxsplit))defrfind(self,sub,start=0,end=_sys.maxsize):ifisinstance(sub,UserString):sub=sub.datareturnself.data.rfind(sub,start,end)defrindex(self,sub,start=0,end=_sys.maxsize):returnself.data.rindex(sub,start,end)defrjust(self,width,*args):returnself.__class__(self.data.rjust(width,*args))defrpartition(self,sep):returnself.data.rpartition(sep)defrstrip(self,chars=None):returnself.__class__(self.data.rstrip(chars))defsplit(self,sep=None,maxsplit=-1):returnself.data.split(sep,maxsplit)defrsplit(self,sep=None,maxsplit=-1):returnself.data.rsplit(sep,maxsplit)defsplitlines(self,keepends=False):returnself.data.splitlines(keepends)defstartswith(self,prefix,start=0,end=_sys.maxsize):returnself.data.startswith(prefix,start,end)defstrip(self,chars=None):returnself.__class__(self.data.strip(chars))defswapcase(self):returnself.__class__(self.data.swapcase())deftitle(self):returnself.__class__(self.data.title())deftranslate(self,*args):returnself.__class__(self.data.translate(*args))defupper(self):returnself.__class__(self.data.upper())defzfill(self,width):returnself.__class__(self.data.zfill(width))