流畅的python学习笔记-第3章

第3章 字典和集合

[toc]

字典推导式

可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的:

d = {key: value for (key, value) in iterable}

举个例子说明:

iterable = [(1, 'hello'), (2, 'world'), (3, 'happy')]
d = {key: value for (key, value) in iterable}

print(d)  # {1: 'hello', 2: 'world', 3: 'happy'}


setdefault用法

使用setdefault处理找不到的键

简单例子:

d = {'a': 1, 'b': 2}
d['c'] = 3

# 若原来没有,设置,否则原值不变
d.setdefault('c', 4)

print(d)  # {'a': 1, 'b': 2, 'c': 3}

字典也有get方法,如下所示:

d = {'a': 1, 'b': 2}
# d['c'] = 3


# 使用get 方法设置的默认值不会改变原字典
a = d.get('c', 4)
print(a) # 4

print(d)  # {'a': 1, 'b': 2}

可以看到两者的区别:

get 方法设置的默认值不会改变原字典, 而setdefault设置的默认值会改变原字典的值。

再看一个例子:

some_dict = {'abc': 'a', 'cdf': 'b', 'gh': 'a', 'fh': 'g', 'hfz': 'g'}
new_dict = {}

for k, v in some_dict.items():
    new_dict.setdefault(v, []).append(k)

print(new_dict)  # {'a': ['abc', 'gh'], 'b': ['cdf'], 'g': ['fh', 'hfz']}


defaultdict用法

所有的映射类型在处理找不到的键的时候,都会牵扯到__missing__方法。这也是这个方法称作“missing”的原因。虽然基类dict并没有定义这个方法,但是dict是知道有这么个东西存在的.

举个例子来说:

class Dict(dict):
    def __missing__(self, key):
        self[key] = []
        return self[key]


dct = Dict()
print(dct["foo"])  # []

dct["foo"].append(1)
dct["foo"].append(2)
print(dct["foo"])  # [1,2]

defaultdict是属于collections 模块下的一个工厂函数,用于构建字典对象,
接收一个函数(可调用)对象为作为参数。
参数返回的类型是什么,key对应value就是什么类型


from collections import defaultdict

# 参数为 list,它就会构建一个默认value为list的字典,
# 例如result['a']的值默认就是list对象。
dct = defaultdict(list)

print(dct)  # defaultdict(, {})

print(dct["a"])  # []
dct["a"].append("hello")
print(dct)  # defaultdict(, {'a': ['hello']})

看一个例子:

from collections import defaultdict

result = defaultdict(list)

data = [("p", 1), ("p", 2), ("p", 3), ("h", 1), ("h", 2), ("h", 3)]

for (key, value) in data:
    result[key].append(value)

print(result)  # defaultdict(, {'p': [1, 2, 3], 'h': [1, 2, 3]})
print(dict(result))  # {'p': [1, 2, 3], 'h': [1, 2, 3]}

再看一个例子:

# 字典的统计
names = ['leo', 'sam', 'jack', 'peter', 'joe', 'susan']
# 要变成这样:{3: ['leo', 'sam', 'joe'], 4: ['jack'], 5: ['peter', 'susan']}

# bad
nums = [len(n) for n in names]
contain = {}
for k1, k2 in zip(nums, names):
    print(k1, k2)
    if k1 not in contain:
        contain[k1] = [k2]
    else:
        contain[k1].append(k2)
print(contain)

print("#" * 10)

# better
from collections import defaultdict
d = defaultdict(list)
for name in names:
    key = len(name)
    d[key].append(name)

print(dict(d))



default性能效率检测

import timeit
from collections import defaultdict


def way1():
    d = {}
    chars = ['a', 'b', 'c', 'c', 'a', 'a'] * 10000
    for c in chars:
        if c in d:
            d[c] += 1
        else:
            d[c] = 1
    return d


def way2():
    d = defaultdict(int)
    chars = ['a', 'b', 'c', 'c', 'a', 'a'] * 10000
    for c in chars:
        d[c] += 1

    return d


if __name__ == "__main__":
    t1 = timeit.timeit("way1", setup="from __main__ import way1", number=10)
    print(t1)  # 6e-07(表示6x10^(-7),也就是6x0.0000001,如果写成6e07表示6x10^7)

    t2 = timeit.timeit("way2", setup="from __main__ import way2", number=10)
    print(t2)  # 4.000000000000097e-07
对于字典的使用,我们要学会用 defaultdict来代替,
一来是因为有缺省值非常安全,如果访问不存在的key,不会报错;
二来是Pyhon性能会大幅提高。
仅仅换了字典数据结构,性能就大幅的提高了很多。


字典的变种

python字典 dict 默认是无序的,要有序请用collection中的orderdict

python 2 代码:

import collections

d = {}
d['name'] = 'zhf'
d['age'] = 33
d['city'] = 'chengdu'



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

a = collections.OrderedDict()
a['name'] = 'zhf'
a['age'] = 33
a['city'] = 'chengdu'


print "*" * 10
for k, v in a.items():
    print k,v

返回结果:

city chengdu
age 33
name zhf
**********
name zhf
age 33
city chengdu

python3.6.7 代码:

import collections

d = {}
d['name'] = 'zhf'
d['age'] = 33
d['city'] = 'chengdu'

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

a = collections.OrderedDict()
a['name'] = 'zhf'
a['age'] = 33
a['city'] = 'chengdu'

print("*" * 10)
for k, v in a.items():
    print(k, v)

运行结果:

name zhf
age 33
city chengdu
**********
name zhf
age 33
city chengdu
可以看到3.6版本的Python已经使得dict变得紧凑以及关键字变得有序


collections.Counter 用法

简单例子:

import collections

string = "aaabbbc"

ct = collections.Counter(string)
print(ct)  # Counter({'a': 3, 'b': 3, 'c': 1})
print(dict(ct))  # {'a': 3, 'b': 3, 'c': 1}

ct.update('abcdef')
print(dict(ct))  # {'a': 4, 'b': 4, 'c': 2, 'd': 1, 'e': 1, 'f': 1}


集合

集合的本质是许多唯一对象的聚集。因此集合可以去掉重复的元素

d = ['abc', 'def', 'abc', 'def']

s = set(d)

print(s)  # {'def', 'abc'}

假设有2个集合a和b,需要统计集合a的哪些元素在集合b中也出现。
如果不使用集合,代码只能写成下面的形式:

a = ['abc', 'def', 'aaa']
b = ['abc', 'bbb', 'ccc', 'def']

for n in a:
    if n in b:
        print(n)

但是如果使用集合,则不用这么麻烦。
在集合中。a|b返回的是合集,a&b返回的是交集。
a-b返回的是差集。-差集是指属于A但不属于B的结合

a = ['abc', 'def', 'aaa']
b = ['abc', 'bbb', 'ccc', 'def']

print(set(a) & set(b))
print(set(a) | set(b))
print(set(a) - set(b)) # 相等于 print(set(a).difference(b))

结果返回:

{'def', 'abc'}
{'aaa', 'def', 'ccc', 'abc', 'bbb'}
{'aaa'}


关于字典,列表,集合运行效率的对比

列表交集代码:

from time import time

t = time()
lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 34, 53, 42, 44]
listb = [2, 4, 6, 9, 23]
intersection = []

for i in range(1000000):
    for a in lista:
        for b in listb:
            if a == b:
                intersection.append(a)

print("total run time:%s" % (time() - t))
# total run time:5.015027046203613

集合交集代码:

from time import time
t = time()

lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 34, 53, 42, 44]
listb = [2, 4, 6, 9, 23]
intersection = []

for i in range(1000000):
    list(set(lista) & set(listb))

print("total run time:%s" % (time() - t))
# total run time:1.0969467163085938

字典代码:

from time import time

t = time()
list = [
    'a', 'b', 'is', 'python', 'jason', 'hello', 'hill', 'with', 'phone',
    'test', 'dfdf', 'apple', 'pddf', 'ind', 'basic', 'none', 'baecr', 'var',
    'bana', 'dd', 'wrd'
]
# list = dict.fromkeys(list, True) # total run time:0.9107456207275391
print(list)
filter = []
for i in range(1000000):
    for find in ['is', 'hat', 'new', 'list', 'old', '.']:
        if find not in list:
            filter.append(find)
print("total run time:%s" % (time() - t))
# total run time:2.0197031497955322

Python 字典中使用了 hash table,因此查找操作的复杂度为 O(1),而 list 实际是个数组,在 list 中,查找需要遍历整个 list,其复杂度为 O(n),因此对成员的查找访问等操作字典要比 list 更快。

因此在需要多数据成员进行频繁的查找或者访问的时候,使用 dict 而不是 list 是一个较好的选择

一个良好的算法能够对性能起到关键作用,因此性能改进的首要点是对算法的改进。
在算法的时间复杂度排序上依次是:
O(1) -> O(lg n) -> O(n lg n) -> O(n^2) -> O(n^3) -> O(n^k) -> O(k^n) -> O(n!)

你可能感兴趣的:(python)