
Exploring Other Constants in Python
Exploring Other Constants in Python 관련
Apart from user-defined constants, Python also defines several internal names that can be considered as constants. Some of these names are strict constants, meaning that you can’t change them once the interpreter is running. This the case for the __debug__ constant, for example.
In the following sections, you’ll learn about some internal Python names that you can consider and should treat as constants in your code. To kick things off, you’ll review some built-in constants and constant values.
Built-in Constants
According to the Python documentation, “A small number of constants live in the built-in namespace”. The first two constants listed in the docs are True and False, which are the Python Boolean values. These two values are also instances of int. True has a value of 1, and False has a value of 0:
True
#
# True
False
#
# False
isinstance(True, int)
#
# True
isinstance(False, int)
#
# True
int(True)
#
# 1
int(False)
#
# 0
True = 42
#
# ...
# SyntaxError: cannot assign to True
True is True
#
# True
False is False
#
# True
Note that the True and False names are strict constants. In other words, they can’t be reassigned. If you try to reassign them, then you get a SyntaxError. These two values are also singleton objects in Python, meaning that there’s only one instance of each. That’s why the identity operator (is) returns True in the final examples above.
Another important and commonplace constant value is None, which is the null value in Python. This constant value comes in handy when you want to express the idea of nullability. Just like True and False, None is also a singleton and strict constant object that can’t be reassigned:
None is None
#
# True
None = 42
#
# ...
# SyntaxError: cannot assign to None
None is quite useful as a default argument value in functions, methods, and class constructors. It’s typically used to communicate that a variable is empty. Internally, Python uses None as the implicit return value of functions that don’t have an explicit return statement.
The ellipsis literal (...) is another constant value in Python. This special value is the same as Ellipsis and is the only instance of the types.EllipsisType type:
Ellipsis
#
# Ellipsis
...
#
# Ellipsis
... is Ellipsis
#
# True
You can use Ellipsis as a placeholder for unwritten code. You can also use it to replace the pass statement. In type hints, the ... literal communicates the idea of an unknown-length collection of data with a uniform type:
def do_something():
... # TODO: Implement this function later
class CustomException(Exception): ...
raise CustomException("some error message")
#
# Traceback (most recent call last):
# ...
# CustomException: some error message
# A tuple of integer values
numbers: tuple[int, ...]
The Ellipsis constant value can come in handy in many situations and help you make your code more readable because of its semantic equivalence to the English ellipsis punctuation sign (…).
Another interesting and potentially useful built-in constant is __debug__, as you already learned at the beginning of this section. Python’s __debug__ is a Boolean constant that defaults to True. It’s a strict constant because you can’t change its value once your interpreter is running:
__debug__
#
# True
__debug__ = False
#
# ...
# SyntaxError: cannot assign to __debug__
The __debug__ constant is closely related to the assert statement. In short, if __debug__ is True, then all your assert statements will run. If __debug__ is False, then your assert statements will be disabled and won’t run at all. This feature can slightly improve the performance of your production code.
Note
Even though __debug__ also has a dunder name, it’s a strict constant because you can’t change its value once the interpreter is running. In contrast, the internal dunder names in the section below should be treated as constants but aren’t strict constants. You can change their values during your code’s execution. However, this practice can be tricky and would require advanced knowledge.
To change the value of __debug__ to False, you must run Python in optimized mode by using the -O or -OO command-line options, which provide two levels of bytecode optimization. Both levels generate an optimized Python bytecode that doesn’t contain assertions.
Internal Dunder Names
Python also has a broad set of internal dunder names that you can consider as constants. Because there are several of these special names, you’ll just learn about __name__ and __file__ in this tutorial.
Note
To dive deeper into other dunder names in Python and what they mean to the language, check out the official documentation about Python’s data model.
The __name__ attribute is closely related to how you run a given piece of code. When importing a module, Python internally sets __name__ to a string containing the name of the module that you’re importing.
Fire up your code editor and create the following sample module:
print(f"The type of __name__ is: {type(__name__)}")
print(f"The value of __name__ is: {__name__}")
Once you have this file in place, get back to your command-line window and run the following command:
python -c "import sample_name"
#
# The type of __name__ is: <class 'str'>
# The value of __name__ is: sample_name
With the -c switch, you can execute a small piece of Python code at the command line. In this example, you import the sample_name module, which prints some messages to the screen. The first message tells you that __name__ is of type str, or string. The second message shows that __name__ was set to sample_name, which is the name of the module you just imported.
Alternatively, if you take sample_name.py and run it as a script, then Python will set __name__ to the "__main__" string . To confirm this fact, go ahead and run the following command:
python sample_name.py
#
# The type of __name__ is: <class 'str'>
# The value of __name__ is: __main__
Note that now __name__ holds the "__main__" string. This behavior indicates that you’ve run the file directly as an executable Python program.
The __file__ attribute will contain the path to the file that Python is currently importing or executing. You can use __file__ from inside a given module when you need to get the path to the module itself.
As an example of how __file__ works, go ahead and create the following module:
print(f"The type of __file__ is: {type(__file__)}")
print(f"The value of __file__ is: {__file__}")
If you import the sample_file module in your Python code, then __file__ will store the path to its containing module on your file system. Check this out by running the following command:
python -c "import sample_file"
#
# The type of __file__ is: <class 'str'>
# The value of __file__ is: /path/to/sample_file.py
Likewise, if you run sample_file.py as a Python executable program, then you get the same output as before:
python sample_file.py
#
# The type of __file__ is: <class 'str'>
# The value of __file__ is: /path/to/sample_file.py
In short, Python sets __file__ to contain the path to the module from which you’re using or accessing this attribute.
Useful String and Math Constants
You’ll find many useful constants in the standard library. Some of them are tightly connected to some specific modules, functions, and classes. Others are more generic, and you can use them in various scenarios. That’s the case with some math and string-related constants that you can find in the math and string modules, respectively.
The math module provides the following constants:
import math
# Euler's number (e)
math.e
#
# 2.718281828459045
# Pi (π)
math.pi
#
# 3.141592653589793
# Infinite (∞)
math.inf
#
# inf
# Not a number (NaN)
math.nan
#
# nan
# Tau (τ)
math.tau
#
# 6.283185307179586
These constants will come in handy whenever you’re writing math-related code or even code that just uses them to perform specific computations, like your Circle class back in the Reusing Objects for Maintainability section.
Here’s an updated implementation of Circle using math.pi instead of your custom PI constant:
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius**2
def perimeter(self):
return 2 * math.pi * self.radius
def projected_volume(self):
return 4/3 * math.pi * self.radius**3
def __repr__(self):
return f"{self.__class__.__name__}(radius={self.radius})"
This updated version of Circle is more readable than your original version because it provides more context on where the Pi constant comes from, making it clear that it’s a math-related constant.
The math.pi constant also has the advantage that if you’re using an older version of Python, then you’ll get a 32-bit version of Pi. In contrast, if you use Circle in a modern version of Python, then you’ll get a 64-bit version of Pi. So, your program will self-adapt to its concrete execution environment.
The string module also defines several useful string constants. The table below shows the name and value of each constant:
| Name | Value |
|---|---|
ascii_lowercase | abcdefghijklmnopqrstuvwxyz |
ascii_uppercase | ABCDEFGHIJKLMNOPQRSTUVWXYZ |
ascii_letters | ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz |
digits | 0123456789 |
hexdigits | 0123456789abcdefABCDEF |
octdigits | 01234567 |
punctuation | !"#$%&'()*+,-./:;<=>?@[\]^_`{ |
whitespace | The combination of the space character, horizontal and vertical tab, linefeed, carriage return, and form feed |
printable | The combination of digits, ascii_letters, punctuation, and whitespace |
These string-related constants come in handy in many situations. You can use them when you’re doing a lot of string processing, working with regular expressions, processing natural language, and more.