您的位置:首页 > 理论基础 > 数据结构算法

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

2019-04-11 21:33 323 查看

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)
      合并其他元素到set集合中来
    1. 参数others必须是可迭代对象
    2. 就地修改

set删除

  • remove(elem)
      从set中移除一个元素
    1. 元素不存在,跑出KeyError异常
  • discard(elem)
      从set中移除一个元素
    1. 元素不存在,什么都不做
  • pop() -> item
      移除并返回任意的元素
    1. 空集返回KeyError异常
  • clear()
      移除所有元素

set修改、查询

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

set成员运算符的比较

pass

set和线性结构

  • 线性结构的查询时间复杂度是O(n),即随着数据规模的增大而增加耗时
  • set、dict等结构,内部使用hash值作为key,时间复杂度可以做到O(1),查询时间和数据规模无关
  • 可hash
      数据行int、float、complex
    1. 布尔型True、False
    2. 字符串string、bytes
    3. tuple
    4. None
    5. 以上都是不可变类型,是可哈希类型,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
    判断se 4000 t1是否是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 条件语句可选
    • 返回一个新的列表
  • 列表解析式是一种语法糖
      编译器会优化,不会因为简写而影响效率,反而因优化提高了效率
    • 29509 减少程序员工作量,减少出错
    • 简化了代码,但可读性增强

    举例

    • 获取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 循环遍历
  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: