zoukankan      html  css  js  c++  java
  • 字典对象的 Pythonic 用法(上篇)

    字典对象在Python中作为最常用的数据结构之一,和数字、字符串、列表、元组并列为5大基本数据结构,字典中的元素通过键来存取,而非像列表一样通过偏移存取。笔者总结了字典的一些常用Pyhonic用法,这是字典的Pythonic用法的上篇

    0. 使用 in/not in 检查 key 是否存在于字典

    判断某个 key 是否存在于字典中时,一般初学者想到的方法是,先以列表的形式把字典所有键返回,再判断该key是否存在于键列表中:

    dictionary = {}

    keys = dictionary.keys()

    for k in keys:

        if key == k:

            print True

            break

    更具Pythonic的用法是使用in关键字来判断 key 是否 存在于字典中:

    if key in dictionary:

        print True

    else:

        print False

    1. 使用 setdefault() 初始化字典键值

    使用字典的时候经常会遇到这样一种应用场景:动态更新字典,像如下代码,如果 key 不在 dictionary 中那么就添加它并把它对应的值初始为空列表 [] ,然后把元素 append 到空列表中:

    dictionary = {}

    if "key" not in dictionary:

        dictionary["key"] = []

    dictionary["key"].append("list_item")

    尽管这段代码没有任何逻辑错误,但是我们可以使用setdefault来实现更Pyhonic的写法:

    dictionary = {}

    dictionary.setdefault("key", []).append("list_item")

    字典调用 setdefault 方法时,首先检查 key 是否存在,如果存在该方法什么也不做,如果不存在 setdefault 就会创建该 key,并把第二个参数[]作为 key 对应的值。

    2. 使用 defaultdict() 初始化字典

    初始化一个字典时,如果初始情况下希望所有 key 对应的值都是某个默认的初始值,比如有一批用户的信用积分都初始为100,现在想给 a 用户增加10分

    d = {}

    if 'a' not in d:

        d['a'] = 100

    d['a'] += 10

    同样这段代码也没任何问题,换成更pyhtonic的写法是:

    from collections import defaultdict

    d = defaultdict(lambda100)

    d['a'] += 10

    defaultdict 是位于 collections 模块下,它是 dict 类的子类,语法结构是:

    class collections.defaultdict([default_factory[, ...]])

    第一个参数default_factory是一个工厂方法,每次初始化某个键的时候将会被调用,value就是default_factory返回的值,剩下的参数和dict()函数接收的参数一样

    3. 使用 iteritems() 迭代大数据

    迭代大数据字典时,如果是使用 items() 方法,那么在迭代之前,迭代器迭代前需要把数据完整地加载到内存,这种方式不仅处理非常慢而且浪费内存,下面代码约占1.6G内存(为什么是1.6G?可以参考:

    http://stackoverflow.com/questions/4279358/pythons-underlying-hash-data-structure-for-dictionaries)

    d = {ii * 2 for i in xrange(10000000)}

    for key, value in d.items():

        print("{0} = {1}".format(key, value))

    而使用 iteritem() 方法替换 items() ,最终实现的效果一样,但是消耗的内存降低50%,为什么差距那么大呢?因为 items() 返回的是一个 list,list 在迭代的时候会预先把所有的元素加载到内存,而 iteritem() 返回的一个迭代器(iterators),迭代器在迭代的时候,迭代元素逐个的生成。

    d = {ii * 2 for i in xrange(10000000)}

    for key, value in d.iteritem():

        print("{0} = {1}".format(key, value))

    4. 高效合并字典

    普通方法

    合并多个字典的时候可以用一行代码实现:

    x = {'a'1, 'b'2}

    y = {'b'3, 'c'4}

    z = dict(x.items() + y.items())

    这种写法看起来很Pythonic,但仔细分析的话,它的执行效率并不高,items()方法在python2.7中返回的是列表对象,两个列表相加得到一个新的列表,这样内存中存在3个列表对象,如果两个列表的大小都是1G,那么执行这段代码需要占用4G的空间来创建这个字典。此外这段代码在Python3中会报错,因为python3中items()返回的是dict_items对象,而不是列表。

    >>> c = dict(a.items() + b.items())

    Traceback (most recent call last):

      File "<stdin>", line 1, in <module>

    TypeErrorunsupported operand type(s) for +: 'dict_items' and 'dict_items'

    在python3中,你需要明确地强制转换成list对象:

    z = dict(list(x.items()) + list(y.items()))

    Pythonic方法

    在Python3.5中提供了一种全新的Pythonic方法:

    z = {**x, **y}

    不过考虑到大部分系统还是基于Python2,所以一种更兼容的pythonic方法是:

    z = x.copy()

    z.update(y)

    当然,你可以把它封装成一个函数:

    def merge_dicts(*dict_args):

        '''

       可以接收1个或多个字典参数

        '''

        result = {}

        for dictionary in dict_args:

            result.update(dictionary)

        return result

    z = merge_dicts(a, b, c, d, e, f, g)

    其他方法

    还有其他方式来合并字典,但是性能不一定是最优的,比如: python2.7可以支持字典推导式

    {k: v for d in dicts for k, v in d.items()}

    python2.6及以下版本使用

    {k: v for d in dicts for k, v in d.items()}

    性能对比

    import timeit

    >>> min(timeit.repeat(lambda{**x, **y}))  # python3.5

    0.4094954460160807

    >>> min(timeit.repeat(lambdamerge_two_dicts(x, y)))

    0.5726828575134277

    >>> min(timeit.repeat(lambda{kv for d in (x, y) for k, v in d.items()} ))

    1.163769006729126

    >>> min(timeit.repeat(lambdadict((k, v) for d in (x, y) for k, v in d.items())))

    2.2345519065856934

    直接使用python3.5中的{**x, **y}是最快的,使用update次之,用字典推导式相对来说是最慢的。

  • 相关阅读:
    洛谷 1850 NOIP2016提高组 换教室
    2018牛客多校第三场 C.Shuffle Cards
    2018牛客多校第一场 B.Symmetric Matrix
    2018牛客多校第一场 A.Monotonic Matrix
    2018牛客多校第一场 D.Two Graphs
    2018宁夏邀请赛L Continuous Intervals
    2018宁夏邀请赛K Vertex Covers
    BZOJ
    HDU
    ACM International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (ECPC 2015)
  • 原文地址:https://www.cnblogs.com/MrFiona/p/7042653.html
Copyright © 2011-2022 走看看