Break long functions or methods into shorter ones. A function should do just one thing and one thing only. Once you notice your function is getting too long then it’s time to refactor.
Move magic numbers that are sprinkled in your code to constants at the top of your module. Assigning numbers to constant variables will give them context and make your code more readable.
Watch out for anything you put in the global scope and localize variables as much as possible and you will have fewer unexpected side consequences. As the Zen of Python says: “Namespaces are one honking great idea. Let’s do more of those!”. If you have a function that modifies global variables, then the function has infringed on rule 1 i.e. A function doesn't do more than one thing. A function modifying global variables can lead to unintended side effects and hard to resolve bugs.
Use Flake 8 or Black for formatting. The sooner we catch bugs, the better, and setting up your environment with linting goes a long way.
Keep try - except blocks narrow. The more code appears between try and except blocks, the higher the possibility that the block won’t raise the exception. Also, try to avoid bare exceptions i.e. always make your exceptions explicit.
Leverage the Python language. Always lean on the language built-in code and tools than writing yours. Example: Opening a file with a context manager which implicitly closes the file at the end of the operation, instead of writing your own code and having to manually close the file. Another example is relying on Python’s concept of “truthiness” instead of finding out the length of a collection.
Use the right data structures. Use sets and dictionaries instead of lists of tuples if you want to find membership. This is because sets and dictionaries are O(1), thus very fast.
Leverage the standard library. Always leverage the standard library instead of having to reinvent the wheel.
Avoid long sequences of if-elif-else clauses. It gets pretty ugly quickly and unmaintainable. Instead, use a dictionary or mapping to have a set of conditions. A mapping is also way cleaner and easier to extend.
Flat is better than nested. When code gets very nested, see if you can return early from the function or see if you can break it into smaller multiple functions. This can reduce the amount of nesting or indenting and makes for better maintainable and easier to test and reuse code.
I hope these few tips help to improve your code.