How to correctly comply with Python coding standards

Foreword

There is no rule without rules, and the same is true for code. This article will introduce some of the more commonly used Python code specifications that you follow when doing your own projects.

Naming

Case

  • Module name writing: module_name
  • Package name writing: package_name
  • Class name: ClassName
  • Method name: method_name
  • Exception name: ExceptionName
  • Function name: function_name
  • Global constant name: GLOBAL_CONSTANT_NAME
  • Global variable name: global_var_name
  • Instance name: instance_var_name
  • Function parameter name: function_parameter_name
  • Local variable name: local_var_name

Naming convention

  1. Function names, variable names and file names should be descriptive and try to avoid abbreviations, except for counters and iterators, as e in exception declarations in try/except and as f of the file handle in the >with statement.
  2. Starting with a single underscore (_) indicates that the variable or function is protected and should not be accessed from outside (except by subclasses).

Comments

Functions and methods

A function must have a docstring unless it meets the following conditions:

  1. Not visible from the outside
  2. very short
  3. easy to understand

The docstring should contain a detailed description of what the function does, as well as its inputs and outputs. Normally, one shouldn’t describe “how” unless it’s some complex algorithm. The docstring should provide enough information that when someone else writes code to call the function, they don’t need to look at a line of code, just the docstring.

There are many styles of docstrings, such as Google style and Numpy style. The Numpy style docstring is more recommended here. The basic writing method is as follows:

def download_song(song_info: SongInfo, save_dir: str, quality=SongQuality.STANDARD) -> str:
    """ download online music to local

    Parameters
    ----------
    song_info: SongInfo
        song information

    save_dir: str
        directory to save the downloaded audio file

    quality: SongQuality
        song sound quality

    Returns
    -------
    song_path: str
        save path of audio file, empty string when the download fails

    Raises
    ------
    AudioQualityError:
        thrown when the sound quality is illegal
    """
    pass

Block comments and line comments

The most important areas to write comments are the technical parts of the code. For complex operations, several lines of comments should be written before the operation begins. Code that is not self-explanatory should have comments added at the end of its lines. To improve readability, comments should leave the code at least 2 spaces.

# We use a weighted dictionary search to find out where i is in
# the array. We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.
if i & amp; (i-1) == 0: # True if i is 0 or a power of 2.

On the other hand, never describe the code, the person reading the code may know Python better than you do, they just don’t know what your code is doing.

# BAD COMMENT: increase i
i+=1

Indent

Use 4 spaces to indent code, never use tabs, and don’t mix tabs and spaces. In the case of line concatenation, the wrapped element should be vertically aligned, or a hanging indent of 4 spaces should be used (in which case there should be no parameters on the first line).

that’s amazing:

# Vertical alignment parameters
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# Vertical alignment within dictionary
foo = {
    "long_dictionary_key": value1 +
                           value2,
    ...
}

# 4 spaces hanging indent, no parameters after the left bracket
foo = long_function_name(
    var_one,
    var_two,
    var_three,
    var_four
)

# 4 spaces hanging indent within dictionary
foo = {
    "long_dictionary_key":
        long_dictionary_value,
    ...
}

You don’t want that kind of thing:

# Parameters cannot be carried after the left bracket
foo = long_function_name(var_one, var_two,
    var_three, var_four
)


# Disable hanging indentation of 2 spaces
foo = long_function_name(
  var_one,
  var_two,
  var_three,
  var_four
)

# Hanging indentation is required within the dictionary
foo = {
    "long_dictionary_key":
    long_dictionary_value,
    ...
}

When writing code, do not indent too many levels, generally no more than 4 levels, otherwise it will cause difficulty in reading. Indentation in Python generally comes from statement blocks such as if-else, for and while. One way to reduce indentation is to write if does not write else, for example:

def update_by_ids(self, entities: List[Entity]) -> bool:
    """ update multi records

    Parameters
    ----------
    entities: List[Entity]
        entity instances

    Returns
    -------
    success: bool
        whether the update is successful
    """
    # If the conditions are not met, return to the default value directly
    if not entities:
        return True

    # Assume this is a bunch of complex business code
    db = self.get_database()
    db.transaction()

    #Create sql statement
    id_ = self.fields[0]
    values = ','.join([f'{i} = :{i}' for i in self.fields[1:]])
    sql = f"UPDATE {self.table} SET {values} WHERE {id_} = :{id_}"
    self.query.prepare(sql)

    return db.commit()

You can also use continue to reduce unnecessary indentation in for and while:

bbboxes = []
for contour in contours:
    if cv.contourArea(contour) < 500:
        continue

    x, y, bw, bh = cv.boundingRect(contour)
    bboxes.append([x, y, bw, bh])

Line break

Two empty lines between top-level definitions, such as function or class definitions. There should be a blank line between method definitions, class definitions and the first method. In functions or methods, if you think it is appropriate, leave a blank line in some places, such as after if-else, for and while One line, and add blank lines between different logic:

def download_cover(url: str, singer: str, album: str) -> str:
    """ download online album cover

    Parameters
    ----------
    url:str
        the url of online album cover

    singer:str
        singer name

    album:str
        album name

    Returns
    -------
    save_path: str
        save path of album cover, empty string when the download fails
    """
    if not url.startswith('http'):
        return ''

    # request data
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
    }
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    pic_data = response.content

    # save album cover
    return Cover(singer, album).save(pic_data)

Guide package

Imports should always be placed at the top of the file, after module comments and docstrings, and before module global variables and constants. The import should be in the order of standard library -> third-party packages -> project code:

# coding:utf-8
import base64
import json
from pathlib import Path
from typing import Union

import requests
from common.database.entity import SongInfo
from common.url import FakeUrl

from .crawler_base import CrawlerBase, MvQuality

Function length

There is no hard limit on function length. But if a function exceeds 40 lines, you should consider whether it can be broken down without damaging the structure of the program. Because even if a long function works well, someone may modify it and add some new behavior a few months later, which can easily lead to hard-to-find bugs. Keep functions concise to make them easier to read and modify. When encountering some very long functions, if you find it difficult to debug or want to use part of the function elsewhere, you may wish to consider splitting the long function.

Type annotations

  1. Public APIs require annotations

  2. Annotate code that is prone to type-related errors. For example, the image of the following code may be a numpy array, or it may be of Image type. Which type needs to be specified:

    def draw_bboxes(image: np.ndarray, bboxes: Union[np.ndarray, list], labels: List[str]) -> Image:
        pass
    
  3. You can use the end-of-line comment # type::

    persons = [] # type: List[Person]
    

Parameter and return value types

When a function needs to pass in or return multiple values, you can consider encapsulating these related values into a class as data members. As shown in the following code, you can easily create an entity class using the dataclass decorator provided by python3.7, and then you can pass in and out instances of the entity class:

from dataclasses import dataclass

@dataclass
classSongInfo:
    """ Song information """
    file: str = None
    title: str = None
    singer: str = None
    album: str = None
    year: int = None
    genre: str = None
    duration: int = None
    track: int = None
    track_total: int = None
    disc: int = None
    disc_total: int = None
    create_time: int = None
    modified_time: int = None


class SongInfoReader(SongInfoReaderBase):
    """ Song information reader """

    def read(self, file: Union[str, Path]):
        if not isinstance(file, Path):
            file = Path(file)

        tag = TinyTag.get(file)

        file_ = str(file).replace('\', '/')
        title = tag.title or file.stem
        singer = tag.artist or self.singer
        album = tag.album or self.album
        year = self.__get_year(tag, file)
        genre = tag.genre or self.genre
        duration = int(tag.duration)
        track = self._parseTrack(tag.track or self.track)
        track_total = int(tag.track_total or self.track_total)
        disc = int(tag.disc or self.disc)
        disc_total = int(tag.disc_total or self.disc_total)
        create_time = int(file.stat().st_ctime)
        modified_time = int(file.stat().st_mtime)

        return SongInfo(
            file=file_,
            title=title,
            singer=singer,
            album=album,
            year=year,
            genre=genre,
            duration=duration,
            track=track,
            track_total=track_total,
            disc=disc,
            disc_total=disc_total,
            createTime=create_time,
            modifiedTime=modified_time
        )


class KuWoMusicCrawler:
    """ Kuwo music crawler """

    def get_song_url(self, song_info: SongInfo) -> str:
        if not FakeUrl.isFake(song_info.file):
            return song_info.file

        # send request for play url
        rid = KuWoFakeSongUrl.getId(song_info.file)
        url = f'http://www.kuwo.cn/api/v1/www/music/playUrl?mid={rid} & amp;type=convert_url3 & amp;br=128kmp3'
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        play_url = json.loads(response.text)['data']['url']

        return play_url

Never put these fields in a dictionary or list for temporary convenience, and then use them as parameters or return values of functions. The dictionary is momentarily cool and reconstructs the crematorium.

Write at the end

If the above specifications can be followed, I believe the code will not emit bad smells so easily. For more coding standards, you can see the Google Open Source Project Style Guide, and this article also refers to part of the guide. If you want to write more elegant code, you can read books and blogs related to design patterns. Here we recommend “Mastering Python Design Patterns” and the tutorial Refactoring GURU. We also highly recommend the following YouTube video tutorials:

  • 7 Python Code Smells: Olfactory Offenses To Avoid At All Costs
  • More Python Code Smells: Avoid These 7 Smelly Snags
  • Uncle Bob’s SOLID principles made easy – in Python!

The rapid rise of Python is extremely beneficial to the entire industry, but “There are many popular people and not many people“, which has led to a lot of criticism, but it still cannot stop its popularity. development momentum.

If you are interested in Python and want to learn Python, here I would like to share with you a Complete set of Python learning materials, which I compiled during my own study. I hope it can help you, let’s work together!

Friends in need can click the link below to get it for free or Scan the QR code below to get it for free
Python complete set of learning materials

1Getting started with zero basics

① Learning route

For students who have never been exposed to Python, we have prepared a detailed Learning and Growth Roadmap for you. It can be said to be the most scientific and systematic learning route. You can follow the above knowledge points to find corresponding learning resources to ensure that you learn more comprehensively.

② Route corresponding learning video

There are also many learning videos suitable for beginners. With these videos, you can easily get started with Python~

③Exercise questions

After each video lesson, there are corresponding exercises to test your learning results haha!

2Domestic and foreign Python books and documents

① Documents and books

3Python toolkit + project source code collection

①Python toolkit

The commonly used development software for learning Python is here! Each one has a detailed installation tutorial to ensure you can install it successfully!

②Python practical case

Optical theory is useless. You must learn to type code along with it and practice it in order to apply what you have learned to practice. At this time, you can learn from some practical cases. 100+ practical case source codes are waiting for you!

③Python mini game source code

If you feel that the practical cases above are a bit boring, you can try writing your own mini-game in Python to add a little fun to your learning process!

4Python interview questions

After we learn Python, we can go out and find a job if we have the skills! The following interview questions are all from first-tier Internet companies such as Alibaba, Tencent, Byte, etc., and Alibaba bosses have given authoritative answers. I believe everyone can find a satisfactory job after reviewing this set of interview materials.

5Python part-time channel

Moreover, after learning Python, you can also take orders and make money on major part-time platforms. I have compiled various part-time channels + part-time precautions + how to communicate with customers into documents.

All the above information , if friends need it, you can scan the QR code below to get it for free