Skip to content

Instantly share code, notes, and snippets.

@AvasDream
Last active September 4, 2019 12:39
Show Gist options
  • Save AvasDream/2b1f3ea3cbe6e25d8320044ae4bf43e4 to your computer and use it in GitHub Desktop.
Save AvasDream/2b1f3ea3cbe6e25d8320044ae4bf43e4 to your computer and use it in GitHub Desktop.
Cheatsheet for python best practices

Motivation

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.

How to write pythonic code

Official Documentation

The Hitchhiker`s guide to python

Principles for or OOP

  1. 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.
  2. Software entities ... should be open for extension, but closed for modification.
  3. Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
  4. Many client-specific interfaces are better than one general-purpose interface.
  5. One should "depend upon abstractions, [not] concretions.

General rules

  1. Shorter Code which is more complex to understand should be avoided.
  2. Write short functions which do one Thing.
  3. A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.

Variable naming

user_sessions_list = []
user_session = "session_1337"

# Constant
PI = 3.14

Functions

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

Classes

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

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

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).

Official Documentation

Metaclasses explained

What are metaclasses?

new

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.

Error Handling

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

Use Exceptions!

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.

Use the finally keyword.

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.

Write your own Exceptions!

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.

Comments / Docstrings

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

Datastructures

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}")

My personal process to write cleaner code in the future

  1. Think about what your function/method does.
  2. Think about which Datatypes has the Input/Output of your function.
  3. Write the docstring.
  4. Does your function really just one thing? If not split in multiple functions.
  5. Write the tests for the function and think about what could go wrong while runtime.
  6. Write the function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment