Weeks of programming can save you hours of planning.
Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.
Clean code is not written by following a set of rules. You don’t become a software craftsman by learning a list of heuristics. Professionalism and craftsmanship come from values that drive disciplines.
And last but not least sometimes i am reading my old code and get the feeling my past-self hates my future-self.
The Hitchhiker`s guide to python
Principles for or OOP
- A class should only have a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class.
- Software entities ... should be open for extension, but closed for modification.
- Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
- Many client-specific interfaces are better than one general-purpose interface.
- One should "depend upon abstractions, [not] concretions.
- Shorter Code which is more complex to understand should be avoided.
- Write short functions which do one Thing.
- A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.
user_sessions_list = []
user_session = "session_1337"
# Constant
PI = 3.14
Naming conventions:
def get_user_data():
return user_data
Private Methods:
def _create_session_key():
return session_key
Best practices for Functions:
- Functions should be small and only do one job.
- Return generators instead of datastructures. (E.g. yield for file reading when you dont know the filesize. This would return an iterator over all the lines.)
- Raise Exceptions and dont return None. Looking at Exceptions tells you why your code failed.
- Use Keyword arguments for better readability.
- Use logging instead of print.
- Write Unittests to reflect
class PostCreator:
def _json_to_xml(json_data):
return xml_data
def sort_posts_by(post_creation_date, order="descending"):
return sorted_posts
Best practices for Classes:
- Every class should have a single respnsibility.
- Same order for Class variables, constructor etc. in all classes.
- Use @property decorator for setter/getter methods.
- Use @staticmethod decorator when there is no dependency to the class state.
- Use the ABCMeta Package for abstract classes.
- Use @classmethod decorator when there is a reference to a class object.
- Use underscores for private methods.
Modules are just python files which separate the functionality of your programm. The filename is also the module name.
- Use the init.py file to split up modules in different files.
Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).
This method is called when an instance is created, before the init method. Can be used to set probertys for each subclass and the validation of supplied Input.
Functions or Methods should return expected values or raise an Exception. You should not return None.
def get_website_name(urls):
if not isinstance(urls, dict):
raise ValueError("Not a dictionary")
if not len(urls):
raise ValueError("Empty dictionary")
website_names = _crawl_websites(urls)
return website_names
When someone is using your code, exceptions will show the limitations of your API. Generally raise an exception when a assumption of the current block is false.
Code in the finally block is always executed even when there is an exception. When working with IO like files or sockets finally can be used to release the file descriptor or socket.
Own exceptions will help you while debugging your code. Think about what scope your Exception has, DockerDaemonNotFound is a smaller scope than DockerDaemonError.
class DockerDaemonNotFoundError(Exception):
def __init__(self, message=None, errors=None):
# Call base class Constructor
super().__init__(message)
self.errors=errors
def get_docker_daemon(url):
docker_daemon = docker.APIClient(base_url=url)
if not docker_daemon:
raise DockerDaemonNotFoundError(f"Error whille connecting to docker daemon at {url}")
return docker_daemon
Prefer handling specific exceptions over the use of except. Using except is internally interpreted as except BaseError.
Write a docstring for every Function/Method, Class and Module.
VScode Extension to auto generate Docstrings
Python Sphinx Tool for documenting your code
"""
I am a module level docstring
This module contains all of the docker daemon API related functions.
This module contains all functions to build and run docker files/images.
"""
import docker
def run_alpine(client):
"""Run docker alpine Container on remote docker daemon
Arguments:
client {str} -- The Docker API client
Returns:
Stdout -- Output string of Container
"""
stdout = client.containers.run('alpine', 'echo docker is awesome')
return stdout
Use sets whenever possible.
Sets can access elements in O(1), dont allow duplicates and sort at insertion time.
user_list = set(users)
Use named tuples
container_info = ("container_info", ["container_name","container_size"])
print(f"Container name: {container_info.container_name}")
- Think about what your function/method does.
- Think about which Datatypes has the Input/Output of your function.
- Write the docstring.
- Does your function really just one thing? If not split in multiple functions.
- Write the tests for the function and think about what could go wrong while runtime.
- Write the function.