1、map类型
dict
就是一种map
类型。先看个例子
a={}
print(isinstance(a,MutableMapping)) #True
# print(isinstance(dict,MutableMapping)) # False 因为isinstance判断的是实例的类型
补充:字典类型a
实际上并不是去继承MutableMapping
类,而是实现了MutableMapping
类中的一些方法(魔法函数),源码中最后通过MutableMapping.register(dict)
语句实现,即注册之后就可以判断dict
是MutableMapping
类型
具体来说
from abc import ABCMeta, abstractmethod
import sys
__all__ = ["Awaitable", "Coroutine",
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
]
在源码的抽象基类collections
中的abc
模块,可以看到有Mapping
类型和MutableMapping
类型(可修改),而dict
就是属于MutableMapping
。
在MutableMapping
类中定义了很多方法,如__setitem__
、__delitem__
、pop
等等,MutableMapping
类继承的正是Mapping
类。
list
和dict
有很多操作方法是一样的,这是因为它们都继承了Collection
类。
2、dict中的常用方法(只说几个蛮重要的)
clear
方法 (清空字典)
a = {"bobby1":{"company":"imooc"},
"bobby2": {"company": "imooc2"}
}
a.clear()
print(a) # {}
copy
方法(浅拷贝和深拷贝)
什么是浅拷贝,看如下例子。
a = {"bobby1": {"company": "im"},
"bobby2": {"company": "im2"}
}
print(a)
new_dict = a.copy()
new_dict["bobby1"]["company"] = "im3"
print(new_dict)
print(a)
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im2'}}
# {'bobby1': {'company': 'im3'}, 'bobby2': {'company': 'im2'}}
# {'bobby1': {'company': 'im3'}, 'bobby2': {'company': 'im2'}}
将字典a
拷贝到new_dict
之后,因为a
中的bobby1
指向的也是一个字典{'company': 'im'}
,这个字典不会再拷贝了,所以修改new_dict
之后,bobby1
指向的还是那个字典,只不过这个字典内容已经修改了。所以再打印出来的字典a
发生了变化。这就是浅拷贝的机制。
深拷贝就需要用到python
中的copy
包。请看如下示例:
import copy
a = {"bobby1": {"company": "im"},
"bobby2": {"company": "im2"}
}
print(a)
new_dict = copy.deepcopy(a)
new_dict["bobby1"]["company"] = "im3"
print(new_dict)
print(a)
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im2'}}
# {'bobby1': {'company': 'im3'}, 'bobby2': {'company': 'im2'}}
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im2'}}
可以看到修改复制得到的的new_dict
,a
并没有受到影响。
- 静态方法
fromkeys
(接收任意iterable
对象)
直接看例子
new_list = ["bobby1", "bobby2"]
new_dict = dict.fromkeys(new_list, {"company": "im"})
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}}
将列表中的元素作为keys,value自己添加。
get
方法
第一个参数要得到的键keys,第二个参数默认的是在字典里如果没有要找的keys,则返回指定的输出。
# new_dict : {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}}
value = new_dict.get("bobby", {"jj"})
print(value) # {"jj"}
# new_dict : {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}}
value = new_dict.get("bobby1")
print(value)
# {'company': 'im'}
item
方法
来看一个例子
# new_dict : {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}}
for key, value in new_dict.items():
print(key, value)
# bobby1 {'company': 'im'}
# bobby2 {'company': 'im'}
setfeault
方法
源码
def setdefault(self, *args, **kwargs): # real signature unknown
"""
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.
"""
pass
看例子(其实就是添加键值对)
# new_dict : {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}}
defult = new_dict.setdefault("bobby", "im2")
print(defult)
print(new_dict)
# im2
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}, 'bobby': 'im2'}
update
方法
源码
def update(self, E=None, **F): # known special case of dict.update
"""
D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]
If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
"""
pass
来看一个例子
# new_dict : {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}}
new_dict.update({"bobby": "im2"})
print(new_dict)
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}, 'bobby': 'im2'}
当然也可以传任意的iterable
对象。
new_dict.update(bobby = "im4",bobby4="im5")
# new_dict.update([("bobby" , "im4"),("bobby4","im5")]) #列表也可以
# new_dict.update((("bobby" , "im4"),("bobby4","im5"))) #元组也可以
print(new_dict)
# {'bobby1': {'company': 'im'}, 'bobby2': {'company': 'im'}, 'bobby': 'im4', 'bobby4': 'im5'}
3、dict的子类(UserDict和Defaultdict)
- 不建议继承
List
和Dict
。
举个栗子
定义一个Mydict
类,让它继承dict
类,然后覆盖(重写)dict
类中的__setitem__
方法,让它去调用dict
父类的__setitem__
方法,变成2倍value
。
class Mydict(dict):
def __setitem__(self,key,value):
super().__setitem__(key,value*2)
my_dict = Mydict(one=1)
print(my_dict) # {'one':1}
实例化后输出是{'one':1}
,发现并没有调用__setitem__
魔法函数。
但是如果我们使用my_dict["one"]=1
这种方法,就发现会去调用__setitem__
方法,结果变为{'one':2}。
所以说某些情况下,c语言实现的dict
(内置类型)不会去调用覆盖的方法,如果实在想要继承可使用collection
模块中的UserDict
。也就是去继承UserDict
。
from collections import UserDict
class Mydict(UserDict):
def __setitem__(self,key,value):
super().__setitem__(key,value*2)
my_dict = Mydict(one=1)
print(my_dict) # {'one':2}
defaultdict
前言
源码的UserDict
类中有一个__missing__
方法(如下面的代码),而在我们的dict
中可以重写__missing__
方法。
这个方法是在我们找不到某个key
时被调用,逻辑大概就是判断key
是否在data
中,在的话直接返回data
里面的值,不在的话,就会去判断这个class
中有没有__missing__
魔法函数,有的话就去调用。
def __getitem__(self, key):
if key in self.data:
return self.data[key]
if hasattr(self.__class__, "__missing__"):
return self.__class__.__missing__(self, key)
raise KeyError(key)
而defaultdict
类实际上就是去重写了__missing__
方法,从该方法中的if self.default_factory is None: raise KeyError((key,))
和self[key] = value = self.default_factory()
语句看出,如果当某个键值找不到的时候,就会将该值设置进来,也就是去调用default_factory()
。
def __missing__(self, key): # real signature unknown; restored from __doc__
"""
__missing__(key) # Called by __getitem__ for missing key; pseudo-code:
if self.default_factory is None: raise KeyError((key,))
self[key] = value = self.default_factory()
return value
"""
pass
而default_factory
实际上就是一个属性,设置一个值,将值赋到key上(self[key] = value = self.default_factory()
)
default_factory = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
"""Factory for default value called by __missing__()."""
举个栗子
from collections import defaultdict
my_dict = defaultdict(dict) # 若为int 返回0;若为list 返回[]
my_value = my_dict["bobby"]
print(my_value)
# {}
setdefault
和defaultdict
也可以参照这篇不错的文章来理解——文章地址
4、set和frozenset
set 集合 fronzenset 不可变集合
无序, 不重复
set
# b = set(['a', 'b', 'c', 'd', 'e'])
# print(b) # {'e', 'b', 'd', 'c', 'a'} 每次结果不一样,无序
s = set('abcdee')
# {'a', 'b', 'd', 'e', 'c'}
一种初始化方法
set={'a','b'}
print(type(set)) # <class 'set'>
fronezset
s = {'a', 'b'}
s.add('c')
print(s) #{'b','a','c'}
s = frozenset("abcde")
print(s) # frozenset({'e', 'c', 'd', 'a', 'b'})
# s.add('b') # AttributeError: 'frozenset' object has no attribute 'add'
注意: 不可变类型frozenset
可以作为字典的key
- 向set添加数据
s = {'a', 'b'}
print(s)
another_set = set("cef")
s.update(another_set)
print(s)
# {'b', 'a'}
# {'b', 'e', 'f', 'a', 'c'}
difference
差集
s = {'a', 'b', 'c'}
print(s)
another_set = set("cef")
re_set = s.difference(another_set) # re_set = s - another_set 这样也是一样的效果,s中有,anot中没有
print(re_set)
# {'a', 'b'}
- 查找集合中的元素(in)
if 'c' in re_set:
print('yes')
补充:只要对象实现了__contains__
魔法函数,就可以使用in
。
- 判断是否为子集
print(s.issubset(re_set)) #True
5、补充知识
dict查找的性能远远大于list
在dict中查找元素不会随着dict的增大而增大
dict的key或者set的值都必须是可以hash的,不可变对象都是可hash的,比如str,fronzenset,tuple等或者自己实现的类 `__hash__`
dict的内存花销大,但是查询速度快,自定义的对象或者python内部的对象都是用dict包装的
dict的存储顺序和元素添加顺序有关