11 nuggets to keep you safe when coding Python

Snake training (source: http://www.simpsonsworld.com/)
  1. Closures in Python are late binding. When defining a function B within another function A, the value of a variable defined in function A used in function B is looked up at the time function B is called. Implication: if the value of the variable changes/ or is changing, function B when called is oblivious of this change and only accesses the current value of the variable. See what to do instead.
  2. The ** 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**2 gives -4, while (-2)**2 gives the correct result, 4.
  3. for loop 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 x with a value of 3 at the end of the list comprehension in Python 2.
  4. 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 hash on the object when the returned hash value is larger than sys.maxsize.
  5. hasattr shadows errors in properties in Python 2 (See https://hynek.me/articles/hasattr/). Stay safe by using getattr to test for object attributes, which also allows to provide an appropriate sentinel as default if you don’t want an AttributeError.
  6. Be careful when using is to 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.
  7. Name mangling is evoked for class members with leading double underscore and no corresponding trailing double underscores.
  8. Using __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.
  9. namedtuple returns 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 False in your face.
  10. 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.
  11. & is a bitwise operator not a boolean operator. Most times you’ll be needing and instead, the boolean operator. Although, with numpy, you may find& to be more appropriate.

I’m pretty sure there are so many other nuggets to keep in mind when coding Python. Feel free to leave them in the comments.