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 timeself
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: https://youtu.be/HTLu2DFOdTg?t=2213
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.
What is a BAD_REPORT_CONSTANT
?
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:
try:
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.