Patrick's blog

Posted Wed 15 May 2019

Please No: Things I Wish People Wouldn't Do In Python

If you feel you should dict.get('some_key', None), please don't instead. The None here is extraneous as it is already returned by default if the key is not found.

If you feel you should loop like this:

for i in range(len(my_iterable)):
    my_item = my_iterable[i]

Please no.

Instead, do:

for i, my_item in enumerate(my_iterable):
    # loop stuff

If you feel the urge to use def __my_method(), please do not.

Especially if you are going to make it a @staticmethod. This does not make sense at all.

According to Raymond Hettinger, the leading double-underscore convention is referred to as a "class local reference":

We are making sure that self actually refers to you. Most of the time self means you are your children but occasionally you need it to be you. People will mistakenly represent this as the way to make things private in Python. Is Python about privacy? (Crowd says 'No') No, it's a consenting adult language. We don't leave the locks on the doors.

It's actually the opposite of privacy. It's all about freedom. It makes your subclasses free to override any one method without breaking the others.

Source talk:

According to the documentation of Python:

"Private" instance variables that cannot be accessed except from inside an object don’t exist in Python.

It additionally states that even if you use the name mangling aspects of the leading double-underscore you are not actually preventing access to the variable.

Unless you have a use case that aligns with those explicitly named in the documentation, this is probably not what you want to do. You will confuse noobs, frustrate pros, and probably yourself too.

If you make an all-capsed constant at the top of your module, please do not leave it undocumented.


If you are using boto3 in any function or method and you hard-code in the region and don't allow an endpoint URL to be passed in, please no.

Always accept a region and endpoint-url parameters in the signature of that function. Hard-coding regions and accepting the default endpoint-urls is lazy.

If you are going to do a conditional assignment expression, please do not chain more than 1 condition.

my_var = my_dict['key_1']['key_2'] if 'key_1' in my_dict and 'key_2' in my_dict['key_1'] else 'default_val'

For the same example above, please ask for forgiveness instead of looking before you leap. This is known as EAFP vs. LBYL in Python.

this would be better:

    my_var = my_dict['key_1']['key_2']
except KeyError:
    my_var = 'default_val'

If you are explicitly using unicode strings like u'Something' and you are using Python 3, please no. You are probably doing it wrong. This one I don't have the patience to research but it definitely feels wrong.

If your functions and methods do not return anything, please no.

It doesn't hurt to return something from your functions. Even if you are changing the state of a reference that was passed in you can still always return True if the operation succeeded.

Here's a well-written blog on writing better functions.

If you are iterating over a dictionary like

for _, val in my_dict.items():
    # stuff

and the underscore represents a value you won't use, please no.

Instead do this:

for v in dict.values():
    # stuff

And vice versa if you are iterating over the keys (for k, _ in my_dict.items()) just iterate over the dictionary itself (for k in my_dict).

If you are writing a function and find you have the need to copy the function because you'd like a simple and complex version of it please do not do this.

Accept **kwargs in your function to allow callers to opt-in to the complex version while allowing for sane defaults for callers who want something simple.

If you feel the need to call exit(1) please do not.

Instead, raise a RuntimeError, or any other Exception, with some additional context in the message. If nothing handles your exception then the program will crash and you will have achieved the same thing as exit(1) but you will have given callers farther down the stack a chance to handle your problem.

If you feel you are having trouble expressing yourself with the standard library's datetime module please just use arrow.

For example, don't do this:

yesterday = datetime.fromordinal(datetime.utcfromtimestamp(time.time()).toordinal() - 1).strftime("%Y-%m-%d")

Please, do this instead:

import arrow

If you are using requests and you feel you should use json.loads(response.text), please no.

Instead use response.json().

The latter is fundamentally an alias for the former and both will work

If you feel the need to put a space between every line of your Python code, please do not do this.

Instead, adjust the line spacing in your editor which allows everyone to choose their preference when working on the project.

If you feel the need to try some calculation or get some data from the network and if it doesn't succeed instead return sys.float_info.max instead as a sentinel value, please do not do this. I can't think of an alternative.

If you feel the need to explicitly call my_var.__len__() please do not do this. Use len(my_var) instead.

You will confuse noobs and frustrate pros.

Category: python
Tags: python code-review