在Python中,使用“dict”与关键字或匿名词典?

时间:2009-12-18 16:26:56

标签: python

假设您要将值的字典传递给函数,或者希望使用不会被重用的短期字典。有两种简单的方法可以做到这一点:

使用dict()功能创建字典:

foo.update(dict(bar=42, baz='qux'))

使用匿名词典:

foo.update({'bar': 42, 'baz': 'qux'})
你更喜欢哪个?除了个人风格之外还有其他理由选择其中一种吗?

9 个答案:

答案 0 :(得分:15)

我更喜欢匿名字典选项。

由于我不喜欢的原因,我不喜欢dict()选项:

 i = int("1")

使用dict()选项,你不必要地调用一个增加你不需要的开销的函数:

>>> from timeit import Timer
>>> Timer("mydict = {'a' : 1, 'b' : 2, 'c' : 'three'}").timeit()
0.91826782454194589
>>> Timer("mydict = dict(a=1, b=2, c='three')").timeit()
1.9494664824719337

答案 1 :(得分:6)

我认为在这个具体案例中我可能更喜欢这个:

foo.update(bar=42, baz='qux')

在更一般的情况下,我经常更喜欢文字语法(你称之为匿名字典,尽管使用{}就像使用dict()一样匿名)。我认为这对维护程序员(通常是我)来说更清楚,部分原因是它在语法高亮的文本编辑器中脱颖而出。它还确保当我必须添加一个不能表示为Python名称的键时,就像带有空格的键一样,那么我就不必去重写整行。

答案 2 :(得分:4)

我的回答将主要讨论使用dicts与关键字args的API设计。 但它也适用于{...}dict(...)的个人使用。

底线:保持一致。如果您的大多数代码都将'bar'称为字符串,请在{...}中将其保留为字符串;如果您通常引用标识符bar - 请使用dict(bar=...)

约束

在讨论样式之前,请注意关键字bar=42语法仅适用于字符串,并且只有在它们是有效标识符时才有效。如果您需要任意标点符号,空格,unicode - 甚至非字符串键 - 问题就在于=>>只有{'bar': 42}语法才有效。

这也意味着在设计API时,必须允许完整的dicts,而不仅仅是关键字参数 - 除非您确定只允许字符串,并且只允许有效的标识符。 (从技术上讲,update(**{'spaces & punctuation': 42})有效。但它很难看。数字/元组/ unicode不起作用。)

请注意,dict()dict.update()结合了两种API:您可以传递一个dict,可以传递关键字args,甚至可以传递两者(后者我认为是未记录的)。所以,如果你想变得更好,请同时允许:

def update(self, *args, **kwargs):
    """Callable as dict() - with either a mapping or keyword args:

    .update(mapping)
    .update(**kwargs)
    """
    mapping = dict(*args, **kwargs)
    # do something with `mapping`...

特别推荐使用名为.update()的方法,以遵循最不出意的规则。

风格

我觉得很好区分内部外部字符串。在内部,我指的是任意标识符,仅表示程序内部的某些内容(变量名,对象属性)或可能在多个程序之间(DB列,XML属性名称)。它们通常只对开发人员可见。外部字符串供人类使用。

[一些Python编码器(包括我)观察了使用'single_quotes'用于内部字符串而使用"Double quotes"用于外部字符串的约定。但这绝对不是普遍的。]

您的问题是关于正确使用裸字(Perl术语) - 语法糖允许在内部字符串上完全省略引号。有些语言(特别是LISP)允许它们广泛使用;使用裸字的Pythonic机会是属性访问 - foo.bar和关键字参数 - update(bar=...)

这里的风格困境是“你的字符串内部是否足以看起来像标识符?

如果键是外部字符串,答案肯定是NO:

foo.update({"The answer to the big question": 42})

# which you later might access as:
foo["The answer to the big question"]

如果键引用了Python标识符(例如对象属性),那么我会说YES:

foo.update(dict(bar=42))
# As others mentioned, in that case the cleaner API (if possible)
# would be to receive them as **kwargs directly:
foo.update(bar=42)

# which you later might access as:
foo.bar

如果键引用Python程序之外的标识符,例如XML attr名称或DB列名称,则使用裸字可能是好的或坏的选择 - 但是您最好选择一种样式并且一致

一致性很好,因为标识符和字符串之间存在心理障碍。它存在是因为字符串很少跨越它 - 只有在使用内省进行元编程时才会存在。语法高亮只会强化它。因此,如果您阅读代码并在一个位置看到绿色'bar'而在另一个位置看到黑色foo.bar,则不会立即建立连接。

另一个重要的经验法则是:如果它们(大部分)是固定的,那么它们是好的。例如。如果您在代码中主要引用固定数据库列,那么使用裸字来引用它们可能会很好;但如果列的一半是参数,那么最好使用字符串。

这是因为参数/常量是人们与标识符/字符串障碍相关联的最重要的差异。 column(变量)和"person"(常量)之间的差异是传达这种差异的最可读方式。将它们作为标识符会使区别变得模糊,并且在语法上会发生逆火 - 您需要经常使用**{column: value}getattr(obj, column)等。

答案 3 :(得分:2)

我更喜欢你的“匿名词典”方法,我认为这纯粹是个人风格的东西。我发现后一版本更具可读性,但它也是我以前看到的。

答案 4 :(得分:2)

dict()方法增加了函数调用的开销。

>>>import timeit,dis
>>> timeit.Timer("{'bar': 42, 'baz': 'qux'}").repeat()
[0.59602910425766709, 0.60173793037941437, 0.59139834811408321]
>>> timeit.Timer("dict(bar=42, baz='qux')").repeat()
[0.98166498814792646, 0.97745355904172015, 0.99231773870701545]

>>> dis.dis(compile("{'bar': 42, 'baz': 'qux'}","","exec"))
  1           0 BUILD_MAP                0
              3 DUP_TOP
              4 LOAD_CONST               0 (42)
              7 ROT_TWO
              8 LOAD_CONST               1 ('bar')
             11 STORE_SUBSCR
             12 DUP_TOP
             13 LOAD_CONST               2 ('qux')
             16 ROT_TWO
             17 LOAD_CONST               3 ('baz')
             20 STORE_SUBSCR
             21 POP_TOP
             22 LOAD_CONST               4 (None)
             25 RETURN_VALUE

>>> dis.dis(compile("dict(bar=42, baz='qux')","","exec"))
  1           0 LOAD_NAME                0 (dict)
              3 LOAD_CONST               0 ('bar')
              6 LOAD_CONST               1 (42)
              9 LOAD_CONST               2 ('baz')
             12 LOAD_CONST               3 ('qux')
             15 CALL_FUNCTION          512
             18 POP_TOP
             19 LOAD_CONST               4 (None)
             22 RETURN_VALUE

答案 5 :(得分:1)

我也喜欢匿名词典,只是出于个人风格。

答案 6 :(得分:1)

如果我有很多参数,有时可以省略键上的引号:

DoSomething(dict(
   Name = 'Joe',
   Age = 20,
   Gender = 'Male',
   ))

这是一个非常主观的问题,BTW。 :)

答案 7 :(得分:1)

我认为当你从别的东西创建一个dict时,dict()函数真的存在,也许是很容易产生必要的关键字args的东西。匿名方法最适合'dict literals',就像你对字符串使用“”一样,而不是str()。

答案 8 :(得分:0)

实际上,如果接收函数只接收没有预定义关键字的字典,我通常会使用**传递约定。

在这个例子中,那将是:

class Foo(object):
   def update(self, **param_dict):
       for key in param_dict:
          ....
foo = Foo()
....
foo.update(bar=42, baz='qux')