11 nuggets to keep you safe when coding Python
- Closures in Python are late binding. When defining a function
Bwithin another function
A, the value of a variable defined in function
Aused in function
Bis looked up at the time function
Bis called. Implication: if the value of the variable changes/ or is changing, function
Bwhen called is oblivious of this change and only accesses the current value of the variable. See what to do instead.
**operator binds more than the unary
-, so when using a numerical literal that is the preceded by
—with exponentiation, remember to use grouping parentheses e.g.
(-2)**2gives the correct result,
forloop target variables are leaky. The leak extends to list comprehensions in Python 2. After the loop is completed, the target variable is bound to the last iteratee value from the loop not the value previously assigned to the variable before the start of the loop e.g.
[x for x in [1, 2, 3]]leaves
xwith a value of
3at the end of the list comprehension in Python 2.
- Calling dunders (double underscore methods) is usually not a good idea unless you’re totally sure of what you’re doing or you’re GvR e.g. calling an object’s
__hash__directly would give a different result from calling
hashon the object when the returned hash value is larger than
hasattrshadows errors in properties in Python 2 (See https://hynek.me/articles/hasattr/). Stay safe by using
getattrto test for object attributes, which also allows to provide an appropriate sentinel as default if you don’t want an
- Be careful when using
isto test the identity of immutable objects as certain implementation specific optimisations may result in inconsistent results across implementations e.g. the interning of small strings and integers (-5 to 256), and the caching of the empty tuple in CPython.
- Name mangling is evoked for class members with leading double underscore and no corresponding trailing double underscores.
__slots__in a subclass whose superclass does not use
__slots__(i.e. has a
__dict__attribute) defeats the goal of using
__slots__in the first place since the subclass still has access to the
__dict__attribute of the superclass and can be assigned new attributes not specified in its
__slots__; no memory savings.
namedtuplereturns a class (more precisely, a subclass of
tuple) not a tuple instance. Eh, doesn’t sound so spooky until
isinstance(namedtuple('Slam', 'a b'), tuple)rightly slams a
Falsein your face.
- Function default arguments are evaluated at creation time of the function. Therefore, using mutable objects as defaults could create unintended results. Avoid using mutable arguments as default, except you’re doing some kind of recursion or again you’re GvR.
&is a bitwise operator not a boolean operator. Most times you’ll be needing
andinstead, the boolean operator. Although, with numpy, you may find
&to be more appropriate.