python dict keys,python dict key类型
在python3里面,我们经常会用如果k在钥匙()中来判断某个键是不是在某个词典里面,或者是用a_dict.keys() - b_dict.keys()来获取两个字典之间键的差集。那么这里就有一个问题,字典的按键()返回了什么数据类型呢?
列表?设置?两者都是错误答案不要说那么多,打印一下类型,发现是这么个数据类型:class dict_keys
字典键是什么东西?在大蟒字典数据结构定义中(dictobject.c),可以看到字典键的定义
//dict对象。cpytypeobject PyDictKeys _ Type={ PyVarObject _ HEAD _ INIT(PyType _ Type,0) dict_keys ,/* TP _ name */sizeof(_ pydictview对象),/* tp_basicsize */0,/* TP _ itemsize */* methods */(析构函数)dictview_dealloc,/* tp_dealloc */0,/* tp_vectorcall_offset */0,/* tp_getattr */0,/* TP _ sett/* TP _ as _ buffer */Py _ TP flags _ DEFAULT Py _ TP flags _ HAVE _ GC,/* tp_flags */0,/* TP _ doc */(traverse proc)dict view _ traverse,/* tp_traverse */0,/* TP _ clear */dict view _ rich compare,/* tp_richcompare */0,/* TP _ weaklistoffset */(getiter func)dict keys _ ITER,/* tp_iter */0,/* tp_iternext */dictkeys字典键数据结构的尺寸,是以_PyDictViewObject为准的。 PyDictViewObject从语义上看是词典的一个视图,从逻辑上看只包含了一个对应词典的指针
//dict对象。htypedef struct { py object _ HEAD PyDictObject * dv _ dict;} _ PyDictViewObject如何理解字典视图的设计?其实这种相当于一个词典实例的代理(代理人/代理人),用户(开发者)侧请求对应的操作(英寸,运算符等),代理侧来给出一个最有效率的方案。举一些例子:
在操作在涉及到对于钥匙()中的k跟has _ key=(d . keys()中的k)两种形式,对应迭代遍历跟包含两种操作。
对于钥匙()中的k操作对应的是PyDictKeys_Type里的dictkeys_iter函数,返回了这个字典视图视图对应的词典的键的迭代器,类型为PyDictIterKey_Type。在迭代遍历时候,会一直调用PyDictIterKey_Type里定义的字典_iternextkey执行迭代过程中的然后操作,从而一个个地获得词典里所有钥匙。
//dictobject.c//dict的键的迭代程序的定义,这里只节选一部分pytype对象PyDictIterKey _ Type={ dict _ key iterator ,/* TP _ name */sizeof(dictiter对象),/* TP _ basic size */py object _ self ITER,/* TP _ ITER */(iternextfunc)dictiter _ iternextkey,/* TP _ ITER next */} has _ key=(k in d . keys())对应的是包含操作,在PyDictKeys_Type里面,对应的是字典键序列的字典键_包含回调。在上一讲目录可变、元组不可变中已经提到,python里面实现对特定数据的多种操作,实际上会尝试将数据看成序列、映射等形式,执行对应数据形式中定义的回调函数,而这里便是将键看作是序列,执行sq _包含对应的回调,表示一个是否包含的判断。字典键_包含实质上调用的是词典自己的包含操作,也就是说钥匙中的k()和d中的k这两种写法,实质上是等价的
运算符操作字典键支持多种运算符操作。比如我们在对比作为计数器的词典时(不是内置的计数器类),会用键相减的方式来得到两次统计里新增/删除的钥匙。相减的操作,比如a.keys() - b.keys(),会执行将键看作为数字时的字典视图_sub函数。在函数内部的实现里,会首先将答。密钥()转化为一个设置,然后调用设置数据结构的差异_更新函数,逐步移动掉右侧钥匙()里面的元素。
//dict对象。c static py object * dict views _ sub(py object * self,py object * other){ py object * result=dict views _ to _ set(self);if (result==NULL) {返回NULL} _ Py _标识符(差异_更新);py object * tmp=_ py object _ callmethoddionearg(result,PyId_difference_update,other);if(tmp==NULL){ Py _ DECREF(result);返回NULL } Py _ DECREF(tmp);返回结果;} 值得注意的是,字典视图_sub内部指定了一个标识符PyId_difference_update通过_ PyObject _ CallMethodIdOneArg函数去调用结果实例里标识为PyId_difference_update的函数,入参为其他。这样调用接口的方式,在大蟒里称之为向量呼叫,是3.9完全应用的文件的特性,相对于以前的版本,显著优化了文件内部不同数据结构间函数调用的效率。有兴趣的同学可以深入了解。
如果是类似=、之类的操作,字典_关键字也是支持的,我们可以在dictview_richcompare函数中看到这些比较符对应的计算方式:
//dict对象。c static Py object * dict view _ rich compare(Py object * self,PyObject *other,int op){ Py_ssize_t len_self,len _ otherint okPyObject *结果;断言(自我!=NULL);assert(PyDictViewSet _ Check(self));断言(其他!=NULL);如果(!PyAnySet_Check(其他)!PyDictViewSet _ Check(other))Py _ RETURN _未实现;len _ self=py object _ Size(self);if (len_self 0)返回NULLlen _ other=py object _ Size(其他);if (len_other 0)返回NULLok=0;switch(op){ case Py _ NE:case Py _ EQ:if(len _ self==len _ other)ok=all _ contained _ in(self,other);if (op==Py_NE ok=0) ok=!ok;打破;case Py _ LT:if(len _ self len _ other)ok=all _ contained _ in(self,other);打破;case Py _ LE:if(len _ self=len _ other)ok=all _ contained _ in(self,other);打破;case Py _ GT:if(len _ self len _ other)ok=all _ contained _ in(other,self);打破;case Py _ GE:if(len _ self=len _ other)ok=all _ contained _ in(other,self);打破;}如果(ok 0)返回空结果=ok?Py _ True:Py _ False;Py_INCREF(结果);返回结果;} 如果两个词典的键相等,则这两组键需要长度一样,并且包含相同的元素(类似于设置相等)
如果大于size,比如a.keys()大于b.keys(),除了a.keys()的长度大于b.keys()之外,a.keys()必须包含b.keys()的所有元素。因此,大于或等于号主要反映包含/被包含关系。
视图概念的其他应用在字典中。除了keys(),dict的values()和items()其实都是返回DictView的视图结构,定义的方式基本相似,只是有一些区别。例如values(),因为未指定tp_richcompare,所以两组值不能在大小或==(两者都将返回false)上进行比较
在python中有很多地方应用了视图的概念/技术。如果你坚持用View这个词,还有一个地方可以应用,叫做memoryview。Memoryview在业务代码中不常用,它的主要功能是为一块内存提供“代理”,以便调用者可以安全地读取和管理数据实例的内存。例如,我们通过pickle.dumps序列化各种数据后,可以通过memoryview看到内存中序列化数据的组成:
def memory view _ test():s= 1234567890 mem _ view=memory view(pickle . dumps(s))print(len(mem _ view))print([chr(item)for item in mem _ view])s=[ 1234 , 56 , 7890 ]mem _ view=memory view(pickle . dumps(s))print(len(mem _ view))print([chr(item)for item for mem _ view])]20[ \x80 , \ x04 , \x95 , \t , \x00 , \x00 , \x00 , \x00 , \x00 , \x8c , \x05 , 5 , 4 , 3 , 2 , 1 , \x94 ,。]35[ \x80 , \x04 , \x95 , \x18 , \x00 , \x00 , \x00 , \x00 , \x00 ,], \x94 ,(, \x8c , \x04 , \ 1 , 2 , 3 , 4 , \x94 , \x02 , 5 ,]32[ \x80 , \x04 , \x95 , \x15 , \x00 , \x00 , \x00 , \x00 , \x00 ,], \x94 ,(, \x8c , \x05 , \ 1 , 2 , 3 , 4 , 5 , \x94 , \x8c , \x05 ,]
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。