My answer will largely talk about the design of APIs to use dicts vs. keyword args.
But it's also applicable the individual use of {...}
vs. dict(...)
.
The bottom line: be consistent. If most of your code will refer to 'bar'
as a string - keep it a string in {...}
; if you normally refer to it the identifier bar
- use dict(bar=...)
.
Constraints
Before talking about style, note that the keyword bar=42
syntax works only for strings and only if they are valid identifiers. If you need arbitrary punctuation, spaces, unicode - or even non-string keys - the question is over => only the {'bar': 42}
syntax will work.
This also means that when designing an API, you must allow full dicts, and not only keyword arguments - unless you are sure that only strings, and only valid identifiers are allowed.
(Technically, update(**{'spaces & punctuation': 42})
works. But it's ugly. And numbers/tuples/unicode won't work.)
Note that dict()
and dict.update()
combine both APIs: you can pass a single dict, you can pass keyword args, and you can even pass both (the later I think is undocumented). So if you want to be nice, allow both:
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`...
This is especially recommended for a method named .update()
, to follow the least-surprise rule.
Style
I find it nice to distinguish internal from external strings. By internal I mean arbitrary identifiers denoting something only inside the program (variable names, object attributes) or possibly between several programs (DB columns, XML attribute names). They are normally only visible to developers. External strings are intended for human consumption.
[Some Python coders (me included) observe the convention of using 'single_quotes'
for internal strings vs. "Double quotes"
for external strings. This is definitely not universal, though.]
Your question is about the proper uses of barewords (Perl term) - syntax sugars allowing to omit the quotes quotes altogether on internal strings. Some languages (notably LISP) allow them widely; the Pythonic opportunities to employ barewords are attribute access - foo.bar
and keyword arguments - update(bar=...)
.
The stylistic dilemma here is "Are your strings internal enough to look like identifiers?"
If the keys are external strings, the answer is definitely NO:
foo.update({"The answer to the big question": 42})
# which you later might access as:
foo["The answer to the big question"]
If the keys refer to Python identifiers (e.g. object attributes), then I'd say 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
If the keys refer to identifiers outside your Python program, such as XML attr names, or DB column names, using barewords may be good or bad choice - but you it's best to choose one style and be consistent.
Consistency is good because there is a psychological barrier between identifiers and strings. It exists because strings rarely cross it - only when using introspection to do meta-programming. And syntax highlighting only reinforces it. So if you read the code and see a green 'bar'
in one place and a black foo.bar
in a second place, you won't immediately make a connection.
Another important rule of thumb is: Barewords are good iff they are (mostly) fixed. E.g. if you refer to fixed DB columns mostly in your code, than using barewords to refer to them might be nice; but if half the time the column is a parameter, then it's better to use strings.
This is because parameter/constant is the most important difference people associate with the identifiers/strings barrier. The difference between column
(variable) and "person"
(constant) is the most readable way to convey this difference. Making them both identifiers would blur the distinction, as well as backfiring syntactically - you'd need to use **{column: value}
and getattr(obj, column)
etc. a lot.