When using dictionary (dict) keys in Python, there seem to be a few general approaches:
some_dict['key_name'] # string constants everywhere
some_dict[KeyConstants.key_name] # where class KeyConstants: key_name: 'key_name'
some_dict[KEY_NAME] # with from some_module import KEY_NAME # a module level constant
'key_name' has the disadvantage that you're repeating constants throughout your code. It's not DRY. Worse, if you ever go to publish an your API (in the broadest sense) you'll have consumers of your API repeating these constants everywhere, and if you ever want to change 'key_name' to 'better_key_name' it will be a breaking change.
This is the typed language, DRY approach, with constants consolidated in one place. Its only disadvantages are that it's ugly, a bit less readable, and more verbose. Pythonic principles primarily prohibit that. It lets you easily change the constant representing the key, since everyone's coding against the variable KeyConstants.key_name. It also works well with IDEs for refactoring.
Module level constants are recommended in the PEP 8 style guide. ALL_CAPS_ARE_LOUD and harder to type. This has some of the advantages of both options 1 and 2.
What are some other best practices for dict key constants? Which of the above approaches are preferred and when?
With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.
Using the Inbuilt method keys() Using if and in. Using has_key() method.
However, there are a couple restrictions that dictionary keys must abide by. First, a given key can appear in a dictionary only once. Duplicate keys are not allowed. A dictionary maps each key to a corresponding value, so it doesn't make sense to map a particular key more than once.
I don't really view #3 as requiring module-level imports; they just can be in the module namepsace, e.g. you could do something like How to programmatically set a global (module) variable?
The advantage of #2 over #1 is that typos and obsolete values will throw an attribute error "this key doesn't exist!" rather than an index error "could not be found!" -- which is always better. #2>#1. It's not more verbose either, because you just set K=Keys
(or something) if you're typing a lot, so you have d[K.key_name]
, just two characters more (). For example, depending how before I'm feeling, I may do either:
import subprocess as proc proc.Popen(..., stdout=proc.PIPE)
or
import subprocess as proc PIPE = proc.PIPE proc.Popen(..., stdout=PIPE)
or
from subprocess import * Popen(..., stdout=PIPE)
With regards to #3, ALL_CAPS_ARE_LOUD for a reason; it becomes confusing to distinguish between d[someVariable]
(which can be holding any keyword) and d[magicKeyword]
-- whereas d[MAGIC_KEYWORD]
is unambiguous that it is a constant, and not some variable which may be holding a constant, e.g. for someVariable in magicKeywords
. #3 basically is equivalent to #2, e.g. re.DOTALL
(the re
being equivalent to KeyConstants
, without having to remember the name of the KeyConstants
containers because it is the module). Thus #3 is superior to #2 unless you are in a strange situation where you have different types of keyspaces.
DRY / OAOO is very very important, but ultimately is not relevant to any of these, because you always need to repeat a variable name in order to refer to it; the best you can do is create an alias.
You could also consider #4, which is to endow your dictionary with attributes, e.g. d.key_name
-- this is only appropriate if it's some subscriptable object.
But to quote a comment by Jochen Ritzel: "Using constant keys should be a very rare occasion" (use attributes of an object, or as he suggests perhaps a named tuple, though I've always found them to be unwieldy)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With