• 字典对象的 Pythonic 用法(上篇:转载)


      转载:https://mp.weixin.qq.com/s?timestamp=1498528588&src=3&ver=1&signature=DfFeOFPXy44ObCMO3yMBLLgt5iFzbFAuShvog4m*JYf1w-gLIDZd7VoPWJN5f36DbVcaBJi33DkfXw6i-h*F7t0-D5-AJsAX*NeUCQAvfR4AYY6g5HMEUFyKFH31O65ulsSDpRyQf4roj-ZQnOZhPBol9POO67N9YO4x29OvrzY=

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

     1、 使用 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

      2、使用 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 对应的值。

      3、 使用 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()函数接收的参数一样

      4、使用 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))

      5、高效合并字典

      普通方法

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

    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次之,用字典推导式相对来说是最慢的。

  • 相关阅读:
    cube.js 基于http 通道的数据实时更新bug 解决
    cube.js 基于http 通道的数据实时更新
    cube.js websocket 实时数据更新说明
    phpspy 进行php 项目性能分析
    使用groovy 下载maven依赖包
    java 几个不错的代码生成工具
    语言性能分析工具总结
    使用rbspy 分析ruby应该性能
    dremio v18.0 软件包可以使用了
    nginx rewrite查询字符串重写问题
  • 原文地址:https://www.cnblogs.com/shaosks/p/7083837.html
Copyright © 2020-2023  润新知