【问题标题】:[] and {} vs list() and dict(), which is better?[] 和 {} 与 list() 和 dict(),哪个更好?
【发布时间】:2011-08-13 01:09:54
【问题描述】:

我知道它们本质上是一样的,但就样式而言,哪个更好(更 Pythonic)用于创建空列表或字典?

【问题讨论】:

    标签: python performance list dictionary


    【解决方案1】:

    在速度方面,空列表/字典没有竞争力:

    >>> from timeit import timeit
    >>> timeit("[]")
    0.040084982867934334
    >>> timeit("list()")
    0.17704233359267718
    >>> timeit("{}")
    0.033620194745424214
    >>> timeit("dict()")
    0.1821558326547077
    

    对于非空:

    >>> timeit("[1,2,3]")
    0.24316302770330367
    >>> timeit("list((1,2,3))")
    0.44744206316727286
    >>> timeit("list(foo)", setup="foo=(1,2,3)")
    0.446036018543964
    >>> timeit("{'a':1, 'b':2, 'c':3}")
    0.20868602015059423
    >>> timeit("dict(a=1, b=2, c=3)")
    0.47635635255323905
    >>> timeit("dict(bar)", setup="bar=[('a', 1), ('b', 2), ('c', 3)]")
    0.9028228448029267
    

    此外,使用方括号表示法可以让您使用列表和字典推导,这可能就足够了。

    【讨论】:

    • 字典和列表理解也可以使用英文名称来完成。示例:list(i for i in range(10) if i % 2)
    • {} 和 [] 快得多有什么原因吗?我以为它们只是别名。
    • 时间似乎没有给出准确的时间。根据基准,它似乎需要大约 200 毫秒,这比正常的 http 调用要慢得多。尝试在 shell 中正常运行 dict(),然后运行 ​​timeit("dict()"),你会看到执行上的明显差异。
    • @piyush 实际上,timeit() 函数报告执行指定迭代次数的总时间,默认为1000000。所以上面的例子是运行代码 sn -p 一百万次的秒数。例如timeit('dict()', number=1) // -> 4.0531158447265625e-06(一次迭代)而timeit('dict()') // -> 0.12412905693054199(一百万次迭代)
    • @GregHaskins 所以在这种情况下,我不认为人们应该担心使用 dict() 或 {},除非遍历一百万条记录并在循环中使用 dict()。
    【解决方案2】:

    在我看来,[]{} 是创建空列表/字典的最 Pythonic 和可读性最强的方法。

    请注意set(),例如:

    this_set = {5}
    some_other_set = {}
    

    可能会令人困惑。第一个创建一个包含一个元素的集合,第二个创建一个空 dict 并且 not 一个集合。

    【讨论】:

    • {} 总是创建一个空字典。 {1,2,3} 在 2.7+ 中创建了一个集合,但在 2.6 和旧版本中是一个语法错误。
    • 对不起?这是一个名为some_epic_set 的变量,它指向一个空的dict 对象……它不是一个空集。对于空集,您需要使用set()
    • @6502:确实,但{5} 创建一个包含一个元素的集合是一个常见的陷阱,5{} 是一个空字典。
    • 哇,这令人困惑。尽管如此,这并不是令人困惑的不良设计级别的分形。 :-)
    • @EnderLook:实际上,使用generalized unpacking,您可以使用{*()} 以文字语法创建一个空的set。我称它为独眼猴操作员。 :-)
    【解决方案3】:

    dict 字面量可能tiny 快一点,因为它的字节码更短:

    In [1]: import dis
    In [2]: a = lambda: {}
    In [3]: b = lambda: dict()
    
    In [4]: dis.dis(a)
      1           0 BUILD_MAP                0
                  3 RETURN_VALUE
    
    In [5]: dis.dis(b)
      1           0 LOAD_GLOBAL              0 (dict)
                  3 CALL_FUNCTION            0
                  6 RETURN_VALUE
    

    同样适用于list[]

    【讨论】:

    • 这假设 BUILD_MAP 和 LOAD_GLOBAL 是常数时间并且花费相同的时间。不大可能。 timeit 提供了更好的估计。
    • 更有可能的是,CALL_FUNCTION 花费的时间至少BUILD_MAP 一样多(被调用的函数本质上是BUILD_MAP),而LOAD_GLOBAL 花费的时间只是额外的开销。
    【解决方案4】:

    注意list()[] 的工作方式不同:

    >>> def a(p):
    ...     print(id(p))
    ... 
    >>> for r in range(3):
    ...     a([])
    ... 
    139969725291904
    139969725291904
    139969725291904
    >>> for r in range(3):
    ...     a(list())
    ... 
    139969725367296
    139969725367552
    139969725367616
    

    list() 总是在堆上创建一个新对象,但[] 在许多情况下可以重用内存单元。

    【讨论】:

      【解决方案5】:

      恕我直言,使用 list()dict() 会使您的 Python 看起来像 C。呃。

      【讨论】:

        【解决方案6】:

        在 [] 和 list() 之间的区别的情况下,有一个我没有看到其他人指出的陷阱。 如果你使用字典作为列表的成员,两者会给出完全不同的结果:

        In [1]: foo_dict = {"1":"foo", "2":"bar"}
        
        In [2]: [foo_dict]
        Out [2]: [{'1': 'foo', '2': 'bar'}]
        
        In [3]: list(foo_dict)
        Out [3]: ['1', '2'] 
        

        【讨论】:

        • 使用list((foo_dict,))可以得到与[foo_dict]相同的结果。 list() 方法接受一个可迭代对象作为它的唯一参数,并对其进行迭代以将元素添加到列表中。这将通过执行list(some_list) 导致类似的陷阱,这将使列表变平。
        【解决方案7】:

        list() 和 [] 之间没有这样的区别,但是如果将它与迭代器一起使用,它会给我们:

        nums = [1,2,3,4,5,6,7,8]

        在:打印([iter(nums)])

        输出:[]

        在:打印(list(iter(nums)))

        输出:[1、2、3、4、5、6、7、8]

        【讨论】:

          【解决方案8】:

          list()[] 之间没有任何人提及的区别在于,list() 会将例如元组转换为列表。而[] 会将所述元组放入列表中:

          a_tuple = (1, 2, 3, 4)
          test_list = list(a_tuple) # returns [1, 2, 3, 4]
          test_brackets = [a_tuple] # returns [(1, 2, 3, 4)]
          

          【讨论】:

            【解决方案9】:

            [] 和 list() 之间的行为有一个区别,如下例所示。如果我们想要返回数字列表,我们需要使用 list() ,否则我们会得到一个地图对象!不过不知道怎么解释。

            sth = [(1,2), (3,4),(5,6)]
            sth2 = map(lambda x: x[1], sth) 
            print(sth2) # print returns object <map object at 0x000001AB34C1D9B0>
            
            sth2 = [map(lambda x: x[1], sth)]
            print(sth2) # print returns object <map object at 0x000001AB34C1D9B0>
            type(sth2) # list 
            type(sth2[0]) # map
            
            sth2 = list(map(lambda x: x[1], sth))
            print(sth2) #[2, 4, 6]
            type(sth2) # list
            type(sth2[0]) # int
            

            【讨论】:

            • 这里似乎是对使用 range() 函数示例的行为的解释 >>> print(range(10)) # range(0, 10) range() 的行为就像一个列表,但它不是一个列表。它是一个对象,当您对其进行迭代时,它会从一个序列中返回连续的项目,它并没有真正创建列表,从而节省空间。这样的对象是可迭代的,也就是说,适合作为函数和构造的目标,这些函数和构造期望它们可以从中获得连续的项目,直到供应耗尽。函数 list() 从可迭代对象创建列表: >>> list(range(5)) # [0, 1, 2, 3, 4]
            • 结果是 [] 存储了可迭代对象; list() 从同一个可迭代对象中创建列表
            【解决方案10】:

            方括号对表示列表对象或索引下标之一,如my_List[x]

            花括号对表示字典对象。

            a_list = ['on', 'off', 1, 2]
            
            a_dict = { on: 1, off: 2 }
            

            【讨论】:

              【解决方案11】:

              大多数时候这主要是一个选择问题。这是一个偏好问题。

              但是请注意,例如,如果您有数字键,则不能这样做:

              mydict = dict(1="foo", 2="bar")
              

              你必须这样做:

              mydict = {"1":"foo", "2":"bar"}
              

              【讨论】:

              • 这是错误的......你需要做mydict = {1:"foo", 2:"bar"}(不带引号的键)。
              • 这不仅仅是“错误”。键是字符串/整数,具体取决于您是否引用它们。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-11-14
              • 2019-02-01
              • 2010-09-16
              • 2017-03-29
              • 2019-09-20
              相关资源
              最近更新 更多