This post talks about some common pitfalls and gotchas I've ran into over the years that are tucked away in the Python programming language. In hopes that after reading this article, you won't find yourself saying:
Hash
In python 3, hash()
returns the same results within one process, but different results across invocations.
It is advised to use hashlib
instead for repeated consistent results.
Mutable objects as arguments
If a mutable object is used as default argument for a function, it retains its value across multiple function calls. This is because Python's default arguments are evaluated once when the function is defined, not each time the function is called.
Class variables and inheritance
Class variables are handled as dictionaries internally and follow Method Resolution Order. This can cause class variables to behave in unexpected ways if you don't understand what is happening under the hood.
Consider the following example:
>>> class A(object):
... x = 1
...
>>> class B(A):
... pass
...
>>> class C(A):
... pass
...
>>> print A.x, B.x, C.x
1 1 1
If I update the class variable x
of class B
:
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
This works as expected. However, if I update the value of x
for class A
:
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
We see that the value of x
for class C
also changed. Since class C
does not have its own variable x
, when called, Python would look for it in its parent (class A
in this example).
Thus, references to C.x
are in fact references to A.x
.
Scope and variable assignment
In Python, variables that are only referenced inside a function are implicitly global. However, if a variable is assigned a value anywhere within the function’s body, it's considered local unless explicitly declared as global.
You can read more about Python scoping and namespaces here.
assert
statement
(Note: This one is less of a gotcha, more like a necessary piece of information of which not everyone is aware.)
Python’s assert statement allows you to write sanity checks in your code. These checks are known as assertions, and you can use them to test if certain assumptions remain true
while you’re developing your code. Assertions are a convenient tool for documenting, debugging, and testing code during development. And you should only use them in this capacity.
You should not and can not use them in production code acting as guard clauses. This is because Python allows you to disable assert
statements for performance optimization.
You can do this by:
- Running Python with the -O or -OO options.
- Or Setting the
PYTHONOPTIMIZE
environment variable to an appropriate value.