字典(dict)

字典的实现原理

列表可以理解为数组,那么字典可以理解为关联数组。
字典的实现原理和查字典是类似的。但我们在字典中查找某个字时,一种办法是从字典的第一页开始往后翻,知道找到我们要查找的字为止。这种办法就是在列表中查找元素的办法,其缺点是:字典中的字数越多查找效率就越低。第二种办法是现在字典的索引表里(比如部首表)查找这个字对应的页码,然后直接翻到这个字对应的页,其优点是:查找效率不会随着字典中字数的增加而降低,无论查找那个字,查找速度都非常快。

字典的特点

  • 字典中的所有元素都是一个key-value对,通过指定的key总能映射到唯一确定的value。
  • 字典中不可以存在重复的key,但是可以存在重复的value。
  • 字典中的元素是无序的,顺序不重要,重要的是key和value的映射关系。
  • 字典中的key必须是不可变对象,存储字典中的key-value对时,系统会调用内置函数hash根据指定的key计算出value的存储位置,也就是哈希值。对于指定的key,为了保证每次计算出的hash值都是相同的,要求key必须是不可变对象,也就是说,只有不可变对象才能存在哈希值。
  • 字典可以根据需要动态的伸缩,系统会根据需要动态的分配和回收内存。因此在使用前无需预先声明字典的容量。
  • 字典会浪费比较大的内存,与列表相比,字典就是用空间换取了时间。

字典-创建

花括号

print({'name': 'Jack', 'age': 18})  # {'name': 'Jack', 'age': 18}
# 空字典
print({})   # {}

内置函数dict

print(dict(name= 'Jack', age= 18))
print(dict({'name': 'Jack', 'age': 18}))
print(dict([('name','Jack'),('age',18)]))
print(dict(zip(range(3), 'ABC')))

返回结果如下:

{'name': 'Jack', 'age': 18}
{'name': 'Jack', 'age': 18}
{'name': 'Jack', 'age': 18}
{0: 'A', 1: 'B', 2: 'C'}

dict的方法fromKeys

调用该方法时通过参数指定所有的key,所有的value的默认值都是None
调用该方法时可以通过参数指定所有value的值

print(dict.fromkeys(['name', 'age']))   #   {'name': None, 'age': None}
print(dict.fromkeys(('name', 'age')))   # {'name': None, 'age': None}
    
print(dict.fromkeys(['name', 'age'], 'N/A'))    # {'name': 'N/A', 'age': 'N/A'}

字典-查

在字典中根据指定的key查找对应的value,常见的方法如下

使用中括号

d = {'name': 'Jack', 'age': 18}
print(d['name'])	# Jack
# 如果字典中不存在指定的key,会抛出KeyError

调用方法get

d = {'name': 'Jack', 'age': 18}
print(d.get('name'))    # Jack
# 如果字典中不存在指定的key,并不会抛出KeyError,而是返回None
print(d.get('aaa')) # None
# 可以通过参数设置默认的value,以便在字典中不存在指定的key时将其返回
print(d.get('aaa','cvcc'))  # cvcc
# 此外,可以使用运算符in(not)检查字典中是否存在(不存在)指定的key
print('name' in d)      # True
print('aaa' in d)       # False
print('name' not in d)  # False
print('aaa' not in d)   # True

字典-改

如果想要修改字典中指定的key对应的value,常见方式有两种:

为已经存在的key赋予一个新的value值(一次只修改一个key对应的value0)

d = {'name': 'Jack', 'age': 18, 'gender': '男'}
d['age'] = 20
print(d)    # {'name': 'Jack', 'age': 20, 'gender': '男'}

调用方法update(一次至少修改一个key对应的value)

d = {'name': 'Jack', 'age': 18, 'gender': '男'}
d.update(name = 'Mike', age = 20)
print(d)    # {'name': 'Mike', 'age': 20, 'gender': '男'}

字典-增

如果想往字典中添加key-value时,常见的方式有两种:

为不存在的key赋予一个value值(一次只添加一个key-value对)

d = {'name': 'Jack', 'age': 18}
d['gender'] = '男'
print(d)    # {'name': 'Jack', 'age': 18, 'gender': '男'}

调用方法update(一次至少添加一个key-value对)

d = {'name': 'Jack', 'age': 18}
d.update(gender = '男', score = 90)
print(d)    # {'name': 'Jack', 'age': 18, 'gender': '男', 'score': 90}

字典-删

调用方法pop(一次只删除一个指定key的key-value对)

该方法返回指定的key对应的value

d = {'name': 'Jack', 'age': 18, 'gender': '男'}
print(d.pop('age'))     # 18
print(d)        # {'name': 'Jack', 'gender': '男'}
# 如果指定的key不存在,会抛出KeyError
# 为了防止抛出KeyError的情况,可以通过参数指定一个默认返回的value
print(d.pop('aaa', 90)) # 90

使用del语句(一次只删除一个指定key的key-value对)

d = {'name': 'Jack', 'age': 18, 'gender': '男'}
del d['age']
print(d)    # {'name': 'Jack', 'gender': '男'}

调用方法popitem(一次只删除一个任意的key-value对)

该方法返回被删除的key-value对

d = {'name': 'Jack', 'age': 18, 'gender': '男'}
print(d.popitem())  # ('gender', '男')
print(d)    # {'name': 'Jack', 'age': 18}

调用方法clear(清空字典)

d = {'name': 'Jack', 'age': 18, 'gender': '男'}
d.clear()
print(d)    # {}

字典-视图

得到字典相关视图有如下三个方法:

  • keys:返回字典所有key的视图
  • values:返回字典所有value的视图
  • items:返回字典所有key-value对的视图
d = {'name': 'Jack', 'age': 18, 'gender': '男'}
print(d.keys()) # dict_keys(['name', 'age', 'gender'])
print(list(d.keys()))   # ['name', 'age', 'gender']
print(d.values())       # dict_values(['Jack', 18, '男'])
print(list(d.values())) # ['Jack', 18, '男']
print(d.items())        # dict_items([('name', 'Jack'), ('age', 18), ('gender', '男')])
print(list(d.items()))  # [('name', 'Jack'), ('age', 18), ('gender', '男')]

为字典中指定的key设置默认值

为了确保字典中指定的key总是存在的,可以调用方法setdefault:

  • 如果字典中存在指定的key,该方法返回指定的key对应的value,字典不发生变化。
  • 如果字典中不存在指定的key,该方法返回指定的默认value值,字典中添加一个key-value对:"指定的key: 指定的默认value值",此时,调用方法setdefault相当于语句:if ... not in ...。
d = {'name': 'Jack'}
print(d.setdefault('name', 'defaultName'))  # Jack
print(d)    # {'name': 'Jack'}
d = {}
print(d.setdefault('name', 'defaultName'))  # defaultName
print(d)    # {'name': 'defaultName'}

借助字典创建格式化字符串

使用百分号作为占位符

phonebook = {'张三': '1333333333',
             '李四': '1444444444',
             '王五': '1555555555',
             '赵六': '1666666666'}
# 王五的号码:1555555555,张三的号码:1333333333
print('王五的号码:%s,张三的号码:%s' % (phonebook['王五'], phonebook['张三']))

当定义的格式化字符串中的占位符是百分号,并且占位符对应的实际值来自某个字典的value时,可以把所有的实际值改写为字典,同时根据字典的value对应的key在占位符%的后面添加:(字典的key).其中,字典的key会被添加一对引号,因此,如果字典的key是字符串,需要去掉字典的key自带的引号。

phonebook = {'张三': '1333333333',
             '李四': '1444444444',
             '王五': '1555555555',
             '赵六': '1666666666'}
# 王五的号码:1555555555,张三的号码:1333333333
print('王五的号码:%(王五)s,张三的号码:%(张三)s' % phonebook)

使用花括号作为占位符

phonebook = {'张三': '1333333333',
             '李四': '1444444444',
             '王五': '1555555555',
             '赵六': '1666666666'}
# 王五的号码:1555555555,张三的号码:1333333333
print('王五的号码:{},张三的号码:{}'.format(phonebook['王五'],phonebook['张三']))

当定义的格式化字符串中的占位符是花括号,并且占位符对应的实际值来自某个字典的value时,可以调用方法format_map并把该字典直接作为方法的参数,同时根据字典的value在花括号中指定对应的key:{字典的key}。其中,字典的key会被添加一对引号,因此,如果字典的key是字符串,需要去掉字典的key自带的引号。

phonebook = {'张三': '1333333333',
             '李四': '1444444444',
             '王五': '1555555555',
             '赵六': '1666666666'}
# 王五的号码:1555555555,张三的号码:1333333333
print('王五的号码:{王五},张三的号码:{张三}'.format_map(phonebook))

集合(set)

可以把集合看做是没有存储value的字典

集合的特点

  • 集合中不可以存储重复的数据
  • 集合中的数据是无序的
  • 集合中的数据可以是任何不可变的类型,多种类型的数据可以混合存储在一个集合中
  • 集合可以根据需要动态的伸缩,也就是说,系统会根据需要动态的分配和回收内存,因此,在使用前无需预先声明集合的容量
  • 集合会浪费比较大的内存,与列表相比,用空间换取时间

集合-创建

使用花括号

将创建的集合赋值给变量时,变量名不要取set,因为set是集合对应的类名。

s = {1, 6, 3, 4, 5}
print(type(s))  # <class 'set'>
# 可以看出,输出集合时,会进行排序
print(s)        # {1, 3, 4, 5, 6}
# 集合中重复的元素会被抹除
print({1, 3, 4, 3, 5, 5, 11, 7})    # {1, 3, 4, 5, 7, 11}
# 不能使用{}表示空集合,因为{}表示空字典
print(type({})) # <class 'dict'>

调用内置函数set

print(set(range(1,6)))      # {1, 2, 3, 4, 5}
print(set([3, 4, 5, 1]))    # {1, 3, 4, 5}
print(set((3, 4, 5, 1)))    # {1, 3, 4, 5}
print(set('3451'))          # {'5', '3', '1', '4'}
print(set({3, 4, 5, 1}))    # {1, 3, 4, 5}
# 空集合
print(set())    # set()

集合间的关系

两个集合是否相等

可以使用运算符==和!=进行判断

s1 = {1, 3, 5, 7, 9}
s2 = {3, 7, 9, 5, 1}
print(s1 == s2)     # True
print(s1 != s2)     # False

一个集合是否是另外一个集合的子集

可以调用方法issubset进行判断

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
s3 = {1, 3, 5, 6, 7, 9}
print(s1.issubset(s2))  # False
print(s1.issubset(s3))  # True

一个集合是否是另外一个集合的超集

可以调用方法issuperset进行判断

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
s3 = {1, 3, 5, 6, 7, 9}
print(s2.issuperset(s1))        # False
print(s3.issuperset(s1))        # True

两个集合是否没有交集

可以调用方法isdisjoint判断

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
s3 = {2, 4, 6, 8, 10}
print(s1.isdisjoint(s2))        # False
print(s1.isdisjoint(s3))        # True

集合-数学操作

两个集合的交集

调用方法intersection和使用运算符&是等价的。
做交集操作后生成给一个新集合,做交集操作的两个集合不变。

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
print(s1.intersection(s2))  # {3, 7}
print(s1 & s2)              # {3, 7}
print(s1)                   # {1, 3, 5, 7, 9}
print(s2)                   # {2, 3, 6, 7, 10}

# s1.intersection_update(s2) 将交集结果更新s1,s2不变,方法intersection_update的返回值为None
print(s1.intersection_update(s2))   # None
print(s1)                           # {3, 7}
print(s2)                           # {2, 3, 6, 7, 10}

两个集合的并集

调用方法union和使用运算符|是等价的。
做并集操作后生成一个新的集合,做并集操作的两个集合不变。

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
print(s1.union(s2))     # {1, 2, 3, 5, 6, 7, 9, 10}
print(s1 | s2)          # {1, 2, 3, 5, 6, 7, 9, 10}
print(s1)               # {1, 3, 5, 7, 9}
print(s2)               # {2, 3, 6, 7, 10}
# 不存在方法union_update()

两个集合的差集

调用方法difference和使用运算符-是等价的。
做差集操作后生成一个新集合,做差集操作的两个集合不变。

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
print(s1.difference(s2))     # {1, 5, 9}
print(s1 - s2)          # {1, 5, 9}
print(s1)               # {1, 3, 5, 7, 9}
print(s2)               # {2, 3, 6, 7, 10}
# s1.difference_update(s2)将差集结果更新s1,s2不变,此方法返回None
print(s1.difference_update(s2))     # None
print(s1)                           # {1, 5, 9}
print(s2)                           # {2, 3, 6, 7, 10}

两个集合的对称差集

调用方法symmetric_difference和使用运算符^是等价的。
做对称差集操作后生成一个新集合,做对称差集操作的两个集合不变。

s1 = {1, 3, 5, 7, 9}
s2 = {2, 3, 6, 7, 10}
print(s1.symmetric_difference(s2))     # {1, 2, 5, 6, 9, 10}
print(s1 ^ s2)          # {1, 2, 5, 6, 9, 10}
print(s1)               # {1, 3, 5, 7, 9}
print(s2)               # {2, 3, 6, 7, 10}
# s1.symmetric_difference_update(s2)将对称差集结果更新s1,s2不变,此方法返回None
print(s1.symmetric_difference_update(s2))     # None
print(s1)                           # {1, 2, 5, 6, 9, 10}
print(s2)                           # {2, 3, 6, 7, 10}

集合-查

可以使用运算符in(not in)检查集合中是否存在(不存在)指定的元素。

s1 = {1, 3, 5, 7, 9}

print(5 in s1)      # True
print(5 not in s1)  # False

集合-增

调用方法add(一次只添加一个元素)

s = {3, 4, 5, 6, 7}
s.add(8)
print(s)    # {3, 4, 5, 6, 7, 8}
# 集合中已经存在的元素不会被添加,切不会抛出异常
s.add(8)
print(s)    # {3, 4, 5, 6, 7, 8}

调用方法update(一次至少添加一个元素)

s = {3, 4, 5, 6, 7}
s.update({8, 9})
print(s)    # {3, 4, 5, 6, 7, 8, 9}
# 集合中已经存在的元素不会被添加,切不会抛出异常
s.update({8, 9})
print(s)    # {3, 4, 5, 6, 7, 8, 9}

集合-删

调用方法remove(一次只删除一个指定的元素)

s = {3, 4, 5, 6, 7}
s.remove(5)
print(s)    # {3, 4, 6, 7}
# 如果指定的元素在集合中不存在,抛出KeyError

调用方法discard(一次只删除一个指定的元素)

s = {3, 4, 5, 6, 7}
s.discard(5)
print(s)    # {3, 4, 6, 7}
# 如果指定的元素在集合中不存在,不会抛出异常
s.discard(1)
print(s)    # {3, 4, 6, 7}

调用方法pop(一次只删除一个任意的元素)

该方法会返回被删除的元素

s = {3, 4, 5, 6, 7}
print(s.pop())  # 3
print(s)        # {4, 5, 6, 7}

调用方法clear(清空集合)

s = {3, 4, 5, 6, 7}
s.clear()
print(s)        # set()

不可变集合frozenset

frozenset意思是被冻结的set,也就是不可变的set。
frozenset是不可变类型,所有forzenset类型的对象:

  • 存在哈希值
  • 可以作为字典的key
  • 可以作为set中的元素

可以调用内置函数frozenset来创建frozenset对象

print(frozenset())              # frozenset()
print(frozenset(range(1, 6)))   # frozenset({1, 2, 3, 4, 5})
print(frozenset([1, 3, 4, 5]))  # frozenset({1, 3, 4, 5})
print(frozenset([1, 3, 4, 5]))  # frozenset({1, 3, 4, 5})
print(frozenset('1345'))        # frozenset({'4', '1', '5', '3'})
print(frozenset({1, 3, 4, 5}))  # frozenset({1, 3, 4, 5})

星霜荏苒 居诸不息