Differences between keywords and constants in Python
Yesterday I wrote about the challenges of having true constants
in Python and said that there are other
mechanisms that achieve basically the same results if all we care
about are a few builtin values like
The most straightforward way is what was done with
None in Python
2 and eventually done with
False in Python 3, which
is to make them into keywords. This raises the obvious question,
namely why the Python people waited until Python 3 to make this
change. One way of starting to answer this is to ask what the
difference is (or would be) between Python keywords and hypothetical
true constants (or just the ordinary 'constants' Python 2 has today
If you look in Python's language documentation in the keywords section, you sort of get an answer:
The following identifiers are used as reserved words, or keywords of the language, and cannot be used as ordinary identifiers. [...]
A keyword cannot be used as an identifier in any context, not merely as a variable (whether global to a module or even local to a function). If you try to define the following class in Python 3, you'll get a syntax error:
class example: def __init__(self): self.True = 10
If you try harder, you can nominally create the instance attribute
(either by directly setting it in
self.__dict__ or by naming
__slots__), but then you have no way of getting access
to it as an attribute, since writing
obj.True in any context
gets you a syntax error.
(By extension, you can't have a method called
Our hypothetical true constants would not be so restricted. A constant would be unchangeable in its namespace, but it certainly wouldn't block the use of its name as an identifier in general in other contexts. You probably shouldn't give user-created names that much power (and the idea is a bad fit for Python's semantics anyway, with no obvious way to implement it).
Given this, we can look at some issues with making
into keywords in Python 2.
To start with, it's unlikely that someone was using either
False as the name of a field in a class but it's not impossible.
If they were and if some version of Python 2 made
into keywords, that code would immediately fail to even start
running. Although I don't know for sure, I suspect that Python 2
had no infrastructure that would have let it report deprecation
warnings in advance for this, so it probably would have been an
However, this is a pretty esoteric reason and there's a much more
pragmatic one, illustrated by the the example that Giedrius
Statkevičius reported at the end of his article.
The pymodbus module defined
__init__.py, not because it was worried about other people
overriding them, but because at one point
it wanted to support older Python versions while still using them:
# Define True and False if we don't have them (2.3.2) try: True, False except NameError: True, False = 1, 0
(A later version changed the values to be the result of boolean comparisons.)
False had been created as keywords, there would be
no way to use them and be backwards compatible with versions of
Python 2 before they were defined. If they're keywords, merely
writing a line that says '
True = (1 == 1)' is a syntax error when
the module is imported or otherwise used, even if the line is never
executed. You have no good way to define your own versions of them
in Python versions where they're not supported (technically there
is one way, but let's not go
there), which means that you can't use them at all until you're
willing to completely abandon support for those older Python versions.
Forcing people to make this choice right up front is not a good way
to get new features used; in fact, it's a great way for a story to
spread through the community of 'oh, you can't use
because ...'. This is counterproductive, to put it one way.
Python 3 can make this sort of change because Python 3 was already
making incompatible changes; in fact, making incompatible changes
is its entire point. Python 2 was not in a good position to do it.
Thus, I suspect that this is the major reason that Python 2 didn't
False into keywords but instead just put them
into the builtins namespace as values.