加速下班之collections模块

吉林白癜风医院 http://pf.39.net/bdfyy/bjzkbdfyy/160318/4792683.html

collections模块实现了特定目标的容器,这些容器能够帮助开发者快速简洁的实现想法

namedtuple

namedtuple又被称为具名元组,官方解释为“创建命名元组子类的工厂函数”,也就是说namedtuple是一个工厂函数,那么namedtuple是如何将传入的参数构建出一个类并返回的呢?其实这上面又回到了Python中一切皆对象上

namedtuple源码

defnamedtuple(typename,field_names,*,rename=False,defaults=None,module=None):"""Returnsanewsubclassoftuplewithnamedfields.Point=namedtuple(Point,[x,y])Point.__doc__#docstringforthenewclassPoint(x,y)p=Point(11,y=22)#instantiatewithpositionalargsorkeywordsp[0]+p[1]#indexablelikeaplaintuple33x,y=p#unpacklikearegulartuplex,y(11,22)p.x+p.y#fieldsalsoaccessiblebyname33d=p._asdict()#converttoadictionaryd[x]11Point(**d)#convertfromadictionaryPoint(x=11,y=22)p._replace(x=)#_replace()islikestr.replace()buttargetsnamedfieldsPoint(x=,y=22)"""#Validatethefieldnames.Attheusersoption,eithergenerateanerror#messageorautomaticallyreplacethefieldnamewithavalidname.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(Typenamesandfieldnamesmustbestrings)ifnotname.isidentifier():raiseValueError(Typenamesandfieldnamesmustbevalidfidentifiers:{name!r})if_iskeyword(name):raiseValueError(Typenamesandfieldnamescannotbeafkeyword:{name!r})seen=set()fornameinfield_names:ifname.startswith(_)andnotrename:raiseValueError(Fieldnamescannotstartwithanunderscore:f{name!r})ifnameinseen:raiseValueError(fEncounteredduplicatefieldname:{name!r})seen.add(name)field_defaults={}ifdefaultsisnotNone:defaults=tuple(defaults)iflen(defaults)len(field_names):raiseTypeError(Gotmoredefaultvaluesthanfieldnames)field_defaults=dict(reversed(list(zip(reversed(field_names),reversed(defaults)))))#Variablesusedinthemethodsanddocstringsfield_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}=%rfornameinfield_names)+)tuple_new=tuple.__new___dict,_tuple,_len,_map,_zip=dict,tuple,len,map,zip#Createallthenamedtuplemethodstobeaddedtotheclassnamespacenamespace={_tuple_new:tuple_new,__builtins__:{},__name__:fnamedtuple_{typename},}code=flambda_cls,{arg_list}:_tuple_new(_cls,({arg_list}))__new__=eval(code,namespace)__new__.__name__=__new____new__.__doc__=fCreatenewinstanceof{typename}({arg_list})ifdefaultsisnotNone:__new__.__defaults__=defaults

classmethoddef_make(cls,iterable):result=tuple_new(cls,iterable)if_len(result)!=num_fields:raiseTypeError(fExpected{num_fields}arguments,got{len(result)})returnresult_make.__func__.__doc__=(fMakeanew{typename}objectfromasequenceoriterable)def_replace(self,/,**kwds):result=self._make(_map(kwds.pop,field_names,self))ifkwds:raiseValueError(fGotunexpectedfieldnames:{list(kwds)!r})returnresult_replace.__doc__=(fReturnanew{typename}objectreplacingspecifiedfieldswithnewvalues)def__repr__(self):Returnanicelyformattedrepresentationstringreturnself.__class__.__name__+repr_fmt%selfdef_asdict(self):Returnanewdictwhichmapsfieldnamestotheirvalues.return_dict(_zip(self._fields,self))def__getnewargs__(self):Returnselfasaplaintuple.Usedbycopyandpickle.return_tuple(self)#Modifyfunctionmetadatatohelpwithintrospectionanddebuggingformethodin(__new__,_make.__func__,_replace,__repr__,_asdict,__getnewargs__,):method.__qualname__=f{typename}.{method.__name__}#Build-uptheclassnamespacedictionary#andusetype()tobuildtheresultclassclass_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__,}forindex,nameinenumerate(field_names):doc=_sys.intern(fAliasforfieldnumber{index})class_namespace[name]=_tuplegetter(index,doc)result=type(typename,(tuple,),class_namespace)#Forpicklingtowork,the__module__variableneedstobesettotheframe#wherethenamedtupleiscreated.Bypassthisstepinenvironmentswhere#sys._getframeisnotdefined(Jythonforexample)orsys._getframeisnot#definedforargumentsgreaterthan0(IronPython),orwheretheuserhas#specifiedaparticularmodule.ifmoduleisNone:try:module=_sys._getframe(1).f_globals.get(__name__,__main__)except(AttributeError,ValueError):passifmoduleisnotNone:result.__module__=modulereturnresult

前面的都看不懂也不要紧,在namedtuple源码的最后return了一个resultreturnresult,往上看几行,会发现有这么一段代码result=type(typename,(tuple,),class_namespace)

这个类是通过type来构建的。在Python一切皆对象中说过,object是type的实例,可以点开type的源码看到,type的__init__方法会初始化一个新的类型

def__init__(cls,what,bases=None,dict=None):#knownspecialcaseoftype.__init__"""type(object_or_name,bases,dict)type(object)-theobjectstypetype(name,bases,dict)-anewtype#(copiedfromclassdoc)"""pass

那么一切问题都解决了,前面的那一部分源码都是在定义按照type的参数签名去构建协议和方法。

知道了是怎么实现的我们只需要关心用namedtuple的用法就可以了,通常情况下使用namedtuple会用作数据模板,当然namedtuple也能构建出具有复杂方法的类型,但是最好别这样做

示例

fromcollectionsimportnamedtupleclassTeacher(object):def__init__(self,name,age,sex,course):self.name=nameself.age=ageself.sex=sexself.course=coursedef__repr__(self):returnf"Teacher(name={self.name},age={self.age},sex={self.sex},"\f"course={self.course})"Person=namedtuple(Person,[name,age,sex])p=Person("张三","18","男")#Person(name=张三,age=18,sex=男)print(p)t=Teacher("李四","20","女","美术")print(t)#Teacher(name=李四,age=20,sex=女,course=美术)

千万因为具名元组这个名字而将namedtuple想成了返回一个元组了,返回的是类型

其实看到这里会发现namedtuple的功能和dataclass装饰器有一点像,后面会单独讲解dataclass装饰器

Counter

Counter是一种容器类型,源码很长,这里就不展示了,Counter继承自dict,所以Counter的大部分功能和dict相同,但是Counter的主要功能就和名字一样,计数!也就是统计序列中的元素出现的次数

defupdate(self,iterable=None,/,**kwds):Likedict.update()butaddcountsinsteadofreplacingthem.Sourcecanbeaniterable,adictionary,oranotherCounterinstance.c=Counter(which)c.update(witch)#addelementsfromanotheriterabled=Counter(watch)c.update(d)#addelementsfromanothercounterc[h]#fourhinwhich,witch,andwatch4#Theregulardict.update()operationmakesnosenseherebecausethe#replacebehaviorresultsinthesomeoforiginaluntouchedcounts#beingmixed-inwithalloftheothercountsforamismashthat#doesnthaveastraight-forwardinterpretationinmostcounting#contexts.Instead,weimplementstraight-addition.Boththeinputs#andoutputsareallowedtocontainzeroandnegativecounts.ifiterableisnotNone:ifisinstance(iterable,_collections_abc.Mapping):ifself:self_get=self.getforelem,countiniterable.items():self[elem]=count+self_get(elem,0)else:#fastpathwhencounterisemptysuper().update(iterable)else:_count_elements(self,iterable)ifkwds:self.update(kwds)

Counter在实例化的的过程中执行__init__方法时会调用update方法,在update方法中如果传入的可迭代对象是映射类型,那么将会遍历添加对应的映射关系,因为Counter本身就继承自dict所以它自己就是一个字典,它内部也没有维护字典,所以for循环遍历添加键的计数时是self[slem],因此这也要求如果传入的是映射类型那么需要是key(Any):value(int)形式的,如果不是映射类型,那么将会执行_count_elements函数

def_count_elements(mapping,iterable):Tallyelementsfromtheiterable.mapping_get=mapping.getforeleminiterable:mapping[elem]=mapping_get(elem,0)+1

这个函数很简单,就干了一件事情,遍历序列中的元素,然后将该元素的计数加一

示例

fromcollectionsimportCounterc=Counter([1,2,3,4,5,10,,12,4,2])print(c)#Counter({2:2,4:2,1:1,3:1,5:1,10:1,:1,12:1})c.update({7:10})#将键值对按值排序后,返回指定数量的键值对,如果参数是2,那么将返回前2个print(c.most_



转载请注明地址:http://www.sanbaicaoasb.com/sctx/8479.html
  • 上一篇文章:
  • 下一篇文章: 没有了
  • 热点文章

    • 没有热点文章

    推荐文章

    • 没有推荐文章