Python内置数据结构(集合与字典)及Python解析式、生成器

Python内置数据结构

#####此篇文章来源于我的老师Wayne,仅作记录以备复习

集set

  • 约定
    set翻译为集合
    collection翻译为集合类型,是一个大概念
  • set
    可变的、无序的、不重复 的元素的集合

set定义 初始化

  • set() -> new empty set object
  • set(iterable) -> new set object

注意

  • s1={}为字典,而不是集合
  • s2={[1],(1),1} 错误,set的元素要求必须可以hash
  • set 的元素不可以使用索引
  • set可以迭代

set增加

  • add(elem)
    增加一个元素到set中
    如果元素存在,什么都不做

  • update(*others)
    1. 合并其他元素到set集合中来
    2. 参数others必须是可迭代对象
    3. 就地修改

set删除

  • remove(elem)
    1. 从set中移除一个元素
    2. 元素不存在,跑出KeyError异常

  • discard(elem)
    1. 从set中移除一个元素
    2. 元素不存在,什么都不做

  • pop() -> item
    1. 移除并返回任意的元素
    2. 空集返回KeyError异常

  • clear()
    1. 移除所有元素

set修改、查询

  • 修改
    1. 要么删除,要么加入新的元素
  • 查询
    1. 非线性结构,无法索引
  • 遍历
    1. 可以迭代所有元素
  • 成员运算符
    1. in 和 not in 判断元素是否在set中
    2. 效率是O(1) ,set用元素的hash值进行查询

set成员运算符的比较

pass

set和线性结构

  • 线性结构的查询时间复杂度是O(n),即随着数据规模的增大而增加耗时

  • set、dict等结构,内部使用hash值作为key,时间复杂度可以做到O(1),查询时间和数据规模无关

  • 可hash
    1. 数据行int、float、complex
    2. 布尔型True、False
    3. 字符串string、bytes
    4. tuple
    5. None
    6. 以上都是不可变类型,是可哈希类型,hashable

  • set的元素必须是可hash的

集合

  • 基本概念

  • 全集
    所有元素的集合。例如实数集,所有实数组成的集合就是全集

  • 子集subset和超集superset
    一个集合A所有元素都在另一个集合B内,A是B的子集,B是A的超集

  • 真子集和真超集
    A是B的子集,且A不等于B,A就是B的真子集,B是A的真超集

  • 并集:多个集合合并的结果
  • 交集:多个集合的公共部分
  • 差集:集合中出去和其他集合公共部分

集合运算

并集

在这里插入图片描述

  1. 将两个集合A和B的所有的元素合并到一起,组成的集合称作集合A与集合B的并集
  2. union(*others)
    返回和多个集合合并后的新的集合
  3. | 运算符重载
    等同于union
  4. update(*others)
    和多个集合合并,就地修改
  5. |=
    等同 update

交集

在这里插入图片描述

  1. 集合A和B,由所有属于A属于B的元素组成的集合
  2. intersection(*others)
    返回和多个集合的交集
  3. &
    等同于 intersection
  4. intersection_update(*others)
    获取和多个集合的交集,并就地修改
  5. &=
    等同于 intersection_update

差集

在这里插入图片描述

  1. 集合A和B,由所有属于A且不属于B的元素组成的集合
  2. difference(*others)
    返回和多个集合的差集

  3. 等同于difference
  4. difference_update(*others)
    获取和多个集合的差集并就地修改
  5. –=
    等同difference_update

对称差集

在这里插入图片描述

  1. 集合A和B,由所有属于A且不属于B的交集元素组成的集合,记做(A-B)U(B-A)
  2. symmetric_difference(other)
    返回和另一个集合的差集
  3. ^
    等同于symmetric_differece
  4. symmetric_difference_update(other)
    获取和另一个集合的差集并就地修改
  5. ^=
    等同symmetric_difference_update

  1. issubset(other)、<=
    判断当前集合是否是另一个集合的子集
  2. set1 < set2
    判断set1是否是set2的真子集
  3. issuperset(other)、>=
    判断当前集合是否是other的超集
  4. set1 > set2
    判断set1是否是set2的真超集
  5. isdisjoint(other)
    当前集合和另一个集合有没有交集
    没有交集,返回True

字典dict

  • key-value 键值对的数据集合
  • 可变的、无序的、key不重复

字典dict定义 初始化

  • d = dict() 或者 d = {}
  • dict(**kwargs) 使用 name=value对 初始化一个字典
  • dict(iterable,**kwarg)使用可迭代对象和 name=value对 构造字典,不过可迭代对象的元素必须是一个二元 结构
    • d = dict(((1,‘a’),(2,‘b’))) 或者 d = dict(([1,‘a’],[2,‘b’]))
  • dict(mapping,**kwarg) 使用一个字典构建另一个字典
  • d = {‘a’:10,‘b’:20,‘c’:None,‘d’:[1,2,3]}
  • 类方法 dict.fromkeys(iterable,value)
    • d = dict.fromkeys(range(5))
    • d = dict.fromkeys(range(5),0)

字典元素的访问

  • d[key]
    • 返回key对应的值value
    • key不存在抛出KeyError异常
  • get(key[,default])
    • 返回key对应的值value
    • key不存在返回缺省值,如果没有设置缺省值就返回None
  • setdefault(key[,default])
    • 返回key对应的值value
    • key不存在,添加kv对,value设置为default,并返回default,如果default没有设置,缺省为None

字典增加和修改

  • d[key] = value
    • 将key对应的值修改为 value
    • key不存在添加新的kv对
  • update([other]) --> None
    • 使用另一个字典的kv对更新本字典
    • key不存在,就添加
    • key存在,覆盖已经存在的key对应的值
    • 就地修改
      例:
d.update(red=1)
d.update((('red‘,2),))
d.update({'red':3})

字典删除

  • pop(ley[,default])
    • key 存在,移除它,并返回它的value
    • key 不存在,返回给定的default
    • default未设置,key不存在,则抛出KeyError异常
  • popitem()
    • 移除并返回一个任意的键值对
    • 字典为empty,抛出KeyError异常
  • clear()
    • 清空字典 (一般情况不建议使用)
  • del 语句
a = True
b = [6]
d = {'a':1,'b':b,'c':[1,3,5]}
del a
del d['c']
del b[0]
c=b
del c
del b
b = d['b']

del a[‘c’] 看着像删除了一个对象,本质上减少了一个对象的引用,del 实际上删除的是名称,而不是对象(引用计数减一)


字典遍历(方法列举)

  • for … in dict
    • 遍历key
for k in d:
	print(k)

for k in d.keys():
	print(k)
  • for … in dict
    • 遍历value
for k in d:
	print(d[k])

for k in d.keys():
	print(d.get(k))

for v in d.value():
	print(v)
  • for … in dict
    • 遍历 item in d.items():
for item in d.items():
	print(item)

for item in d.itmes():
	print(tiem[0],item[1])

for k,v in d.items():
	print(k,v)

for k,_ in d.items():
	print(k)

for _,v in d.itmes():
	print(v)
字典遍历总结
  • Python3 中,keys、values、items 方法返回一个类似一个生成器的可迭代对象,不会把函数的返回结果复制到内存中
    • 返回Dictionary view 对象,可以使用 len()、iter()、in 操作
    • 字典的 entry 的动态的视图,字典变化,视图将反映出这些变化
    • keys返回一个类 set对象,也就是可以看做一个set集合
    • 如果values都可以hash,那么iems也可以看做是类set对象
  • Python2 中,上面的方法会返回一个新列表,占据新的内存空间。所以Python2 建议使用iterkeys、itervalues、iteritems版本,返回一个迭代器,而不是返回一个copy

字典遍历和移除

  • 如何在遍历的时候移除元素
!错误的做法

d = dict(a=1,b=2,c='abc')
for k,v in d.items():
	d.pop(k)    会抛异常

while len(d):   相当于清空,不如直接clear()
	print(d.popitem())

while d:
	print(d.popitem())
正确的做法

d = dict(a=1,b=2,c='abc')
keys = []
for k,v in d.items():
	if isinstance(v,str): 
		keys.append(k)
for k in keys:
	d.pop(k)
print(d)

字典的key

  • key 的要求和 set 的元素要求一致
    • set 的元素可以就是看做key,set 可以看做dict的简化版
    • hashable 可哈希才可以作为 key,可以使用 hash()测试
    • d = {1:0,2.0:3,‘abc’:None,(‘hello’,‘world’,‘python’):“string”,b’abc’:‘135’}

defaultdict

  • collections.defaultdict([default_factory[,…]])
    • 第一个参数是default_factory,缺省是None,它提供了一个初始化函数。当key不存在的时候,会调用这个工厂函数来生成key对应的value
    • 构造一个字典,value是列表,为其添加随机个元素
import random

d1={}
for k in 'abcdef':
	for v in range(random.randint(1,5)):
		if k not in d1.keys():
			d1[k] = []
		d1[k].append(v)
	print(d1)
	
-------------

from collections import defaultdict
import random

d1 = defaultdict(list)
for k in 'abcdef':
	for v in range(random.randint(1,5)):
		d1[k].append(v)
print(d1)

OrderedDict

  • collections.OrderedDict
    • key 并不是按照加入的顺序排列,可以使用OrderedDict记录顺序
from collections import OrderedDict
import random

d = {'banana:3,'apple':4,'pear':1,'orange':2}
print(d)
keys = list(d.keys())
random.shuffle(keys)
print(leys)
od = OrderedDict()
for key in keys:
	od[key] = d[key]
print(od)
print(od.keys())
  • 有序字典可以记录元素插入的顺序,打印的时候也是按照这个顺序输出打印
  • 3.6版本的Python的字典就是记录key插入的顺序(IPython不一定有效果)
  • 应用场景
    • 假如使用字典记录了N个产品,这些产品使用ID由小到大加入到字典中
    • 除了使用字典检索的遍历,有时候需要取出ID,但是希望是按照输入的顺序,因为输入顺序是有序的
    • 否则还需要重新把遍历到的值排序

Python解析式、生成器

列表解析List Comprehension

  • 语法
    • [返回值 for 元素 in 可迭代对象 if 条件]
    • 使用中括号 [] , 内部是 for 循环,if 条件语句可选
    • 返回一个新的列表
  • 列表解析式是一种语法糖
    • 编译器会优化,不会因为简写而影响效率,反而因优化提高了效率
    • 减少程序员工作量,减少出错
    • 简化了代码,但可读性增强

举例

  • 获取10以内的偶数,比较执行效率
  even = []
  for x in range(10):
  if x % 2 == 0:
  even.append(x)
  --------------------------
  even = [x for x in range(10) if x % 2 == 0]

FAQ

  1. 有这样的赋值语句 newlist = [print(i) for i in range(10)] ,请问 newlist 的元素打印出来是什么?
    这样的元素打印出来是None。由于print的返回值为None,所以列表中会生成10个None
  2. 获取20以内的偶数,如果同时将3的倍数也打印 [i for i in range(20) if i % 2 ==0 elif i % 3 ==0] 行么?
    这样做是不行的,会抛出语法错误。因为列表解析式中不允许出现else语句,想要的到问题的结果,有下面两种做法
[ i for i in range(20) if i % 2 == 0 if i % 3 == 0]
[ i for i in range(20) if i % 2 == 0 and i % 3 == 0] 

列表解析式进阶

[ expr for i in iterabe1 for j in iterable2 ]
等价于
ret = []
for i in iterable1:
for j in iterable2:
ret.append(expr)

  • 举例
[(x,y) for x in 'abcde' for y in range(3)]
[[x,y] for x in 'abcde' for y in range(3)]
[{x:y} for x in 'abcde' for y in range(3)]

习题举例
  • 生成一个新列表,要求新列表元素是lst相邻2项的和
numlist = [1,4,9,16,2,5,10,15]
length = len(numlist)
newnumlist = [ numlist[i]+numlist[i+1] for i in range(length-1)]
print(newlist)
  • 用列表解析式打印九九乘法表
[print('{}*{}={:>2} {}'.format(x,y,x*y, '\n' if x == y else''),end='') for x in range(1,10) for y in range(1,x+1)]

生成器表达式

  • 语法
    • (返回值 for 元素 in 可迭代对象 if 条件)
    • 列表解析式的中括号换成小括号就行了
    • 返回一个生成器
  • 和列表解析式的区别
    • 生成器表达式是按需计算(或称惰性求值、延迟计算),需要的时候才计算值
    • 列表解析式是立即返回值
  • 生成器
    • 可迭代对象
    • 迭代器

举例

g = ("{:04}".format(i) for i in range(1,11))
next(g)
for x in g:
	print(x)
print('~~~~~~~~~~‘)
for x in g:
	print(x)
  • 总结

    • 延迟计算
    • 返回迭代器
    • 从前到后从头到尾走完一遍以后,不能回头
  • 对比列表

g = ["{:04}".format(i) for i in range(1,11)]
for x in g:
	print(x)
print(`~~~~~~~~~~~~~~~`)
for x in g:
	print(x)	
  • 总结
    • 立即计算
    • 返回的不是迭代器,返回的是可迭代对象列表
    • 从前到后走完一遍后,可以重新回头迭代

和列表解析式的对比
  • 计算方式
    • 生成器表达式延迟计算,列表解析式立即计算
  • 内存占用
    • 单从返回值本身来说,生成器表达式占用当前内存较小,省内存,列表解析式返回新的列表
    • 生成器没有数据,内存占用极少,它是使用时一个个返回数据。如果将这些返回值的数据合起来占用的内存也和列表解析式差不多。但是,它不需要立即占用这么多内存
    • 列表解析式构造新的列表需要立即占用内存,不管你是否立即使用这么多数据
  • 计算速度
    • 单从计算时间看,生成器表达式耗时非常短,列表解析式耗时长
    • 但是生成器本身并没有返回任何值,只返回了一个生成器对象
    • 列表解析式构造并返回了一个新的列表,所以看起来耗时了

集合解析式
  • 语法
    • {返回值 for 元素 in 可迭代对象 if 条件}
    • 列表解析式的中括号换成大括号{}就行了
    • 立即返回一个集合
  • 用法
    • {(x,x+1) for x in range(10)}
    • {[x] for x in range(10)} # 注意,这种方法是错误的,因为集合中的元素必须可以hash,列表不可hash,所以语法错误

字典解析式
  • 语法
    • {返回值 for 元素 in 可迭代对象 if 条件}
    • 列表解析式的中括号换成大括号{}就行了
    • 使用key:value 形式
    • 立即返回一个字典
  • 用法
    • {x:(x,x+1) for x in range(10)}
    • {x:[x,x+1] for x in range(10)}
    • {(x,):[x,x+1] for x in range(10)}
    • {[x]:[x,x+1] for x in range(10)} #注意,这种方法是错误的,因为字典的key 要求必须是可hash值。列表不可hash,所以语法错误
    • {chr(0x41+x):x**2 for x in range(10)}
    • {str(x):y for x in range(3) for y in range(4)} # 注意,这里只会输出三个元素,因为作为 key 的 x 只能取到三次

总结

在这里插入图片描述

  • Python 2 引入列表解析式
  • Python 2.4 引入生成器表达式
  • Python 3 引入集合、字典解析式,并迁移到了 2.7
  • 一般来说,应该多应用解析式,简短、高效
  • 如果一个解析式非常复杂,难以读懂,可以考虑拆解成for循环
  • 生成器和迭代器是不同的对象,但都是可迭代对象
  • 可迭代对象范围更大,都可以使用 for 循环遍历
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值