假设您要将值的字典传递给函数,或者希望使用不会被重用的短期字典。有两种简单的方法可以做到这一点:
使用dict()
功能创建字典:
foo.update(dict(bar=42, baz='qux'))
使用匿名词典:
foo.update({'bar': 42, 'baz': 'qux'})
你更喜欢哪个?除了个人风格之外还有其他理由选择其中一种吗?
答案 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')