Total 113 articles, this article is about 8000 words, about 20 minutes to read

Original text: github.com/zedr/clean-…

The Python version of code cleanliness. The directory is as follows:

  1. introduce
  2. variable
  3. function

1. Introduction

The principles of software engineering come from Robert C. Martin’s book Clean Code, and this article is Clean Code for the Python version. This is not a style guide, but a guide on how to write Readable, usable, and reconfigurable Pyhton code.

Not every principle outlined here must be strictly adhered to, and only a few will be universally agreed upon. What follows is just a guide, but it comes from the author of Clean Code, who has years of programming experience.

The Python version here is 3.7+


2. The variable

2.1 Use meaningful and interpretable variable names

Bad writing

ymdstr = datetime.date.today().strftime("%y-%m-%d")
Copy the code

Good way to write it

current_date: str = datetime.date.today().strftime("%y-%m-%d")
Copy the code

2.2 Use the same vocabulary for variables of the same type

Bad writing: There are three different names for entities with the same underscore

get_user_info()
get_client_data()
get_customer_record()
Copy the code

Good writing: If the entities are the same, the functions used should be consistent

get_user_info()
get_user_data()
get_user_record()
Copy the code

Better: Python is an object-oriented programming language, so you can put functions of the same entity in classes, either as instance properties or as methods

class User:
    info : str


    @property
    def data(self) - >dict:
        #...


    def get_record(self) - >Union[Record, None] :
        #...
Copy the code

2.3 Use searchable names

We often see more code than we write, so it is important that we write code that is readable and searchable. If we do not declare some meaningful variables, our programs will become unintelligible, as shown in the following example.

Bad writing

What does # 86400 mean?
time.sleep(86400)
Copy the code

Good way to write it

A global variable is declared
SECONDS_IN_A_DAY = 60 * 60 * 24


time.sleep(SECONDS_IN_A_DAY)
Copy the code

2.4 Variables with explanations are used

Bad writing

address = 'One Infinite Loop, Cupertino 95014'
city_zip_code_regex = r'^[^,\\]+[,\\\s]+(.+?) \s*(\d{5})? $'
matches = re.match(city_zip_code_regex, address)


save_city_zip_code(matches[1], matches[2])
Copy the code

That’s a good way to write it

This is better, but it still relies heavily on regular expressions

address = 'One Infinite Loop, Cupertino 95014'
city_zip_code_regex = r'^[^,\\]+[,\\\s]+(.+?) \s*(\d{5})? $'
matches = re.match(city_zip_code_regex, address)


city, zip_code = matches.groups()
save_city_zip_code(city, zip_code)
Copy the code

Good way to write it

Subschema naming reduces dependence on regular expressions

address = 'One Infinite Loop, Cupertino 95014'
city_zip_code_regex = r'^[^,\\]+[,\\\s]+(? P
      
       .+?) \s*(? P
       
        \d{5})? $'
       
      
matches = re.match(city_zip_code_regex, address)


save_city_zip_code(matches['city'], matches['zip_code'])
Copy the code

2.5 Avoid making readers guess

Do not make the reader need association to know the meaning of variable names; explicit is better than implicit.

Bad writing

seq = ('Austin'.'New York'.'San Francisco')


for item in seq:
    do_stuff()
    do_some_other_stuff()
    #...
    What does # item mean?
    dispatch(item)
Copy the code

Good way to write it

locations = ('Austin'.'New York'.'San Francisco')


for location in locations:
    do_stuff()
    do_some_other_stuff()
    #...
    dispatch(location)
Copy the code

2.6 No additional context is required

If the class or object name already provides some information, it does not need to be repeated in the variable.

Bad writing

class Car:
    car_make: str
    car_model: str
    car_color: str
Copy the code

Good way to write it

class Car:
    make: str
    model: str
    color: str
Copy the code

2.7 Use default parameters instead of conditional statements

Bad writing

def create_micro_brewery(name) :
    name = "Hipster Brew Co." if name is None else name
    slug = hashlib.sha1(name.encode()).hexdigest()
    # etc.
Copy the code

It is possible to set the name parameter to a default value without using a conditional statement.

Good way to write it

def create_micro_brewery(name: str = "Hipster Brew Co.") :
    slug = hashlib.sha1(name.encode()).hexdigest()
    # etc.
Copy the code

3. The function

3.1 Function Parameters (2 or fewer)

It is important to limit the number of arguments a function can take to test the code you write. More than three function parameters can result in test combinations exploding, meaning that many different test cases need to be considered.

No parameters is the best case. One or two parameters are also good, and three parameters should be avoided. If there are more than three then you need to tidy up the function. In general, if a function has more than two arguments, that means your function probably has a lot to implement. In addition, many times a high-level object can also be used as a parameter.

Bad writing

def create_menu(title, body, button_text, cancellable) :
    #...
Copy the code

Nice way to write it

class Menu:
    def __init__(self, config: dict) :
        title = config["title"]
        body = config["body"]
        #...


menu = Menu(
    {
        "title": "My Menu"."body": "Something about my menu"."button_text": "OK"."cancellable": False})Copy the code

Another nice way to write it

class MenuConfig:
    """A configuration for the Menu. Attributes: title: The title of the Menu. body: The body of the Menu. button_text: The text for the button label. cancellable: Can it be cancelled? "" "
    title: str
    body: str
    button_text: str
    cancellable: bool = False




def create_menu(config: MenuConfig) :
    title = config.title
    body = config.body
    #...




config = MenuConfig
config.title = "My delicious menu"
config.body = "A description of the various items on the menu"
config.button_text = "Order now!"
# The instance attribute overrides the default class attribute.
config.cancellable = True


create_menu(config)
Copy the code

Excellent writing

from typing import NamedTuple




class MenuConfig(NamedTuple) :
    """A configuration for the Menu. Attributes: title: The title of the Menu. body: The body of the Menu. button_text: The text for the button label. cancellable: Can it be cancelled? "" "
    title: str
    body: str
    button_text: str
    cancellable: bool = False




def create_menu(config: MenuConfig) :
    title, body, button_text, cancellable = config
    #...




create_menu(
    MenuConfig(
        title="My delicious menu",
        body="A description of the various items on the menu",
        button_text="Order now!"))Copy the code

A better way to write it

rom dataclasses import astuple, dataclass




@dataclass
class MenuConfig:
    """A configuration for the Menu. Attributes: title: The title of the Menu. body: The body of the Menu. button_text: The text for the button label. cancellable: Can it be cancelled? "" "
    title: str
    body: str
    button_text: str
    cancellable: bool = False


def create_menu(config: MenuConfig) :
    title, body, button_text, cancellable = astuple(config)
    #...




create_menu(
    MenuConfig(
        title="My delicious menu",
        body="A description of the various items on the menu",
        button_text="Order now!"))Copy the code

3.2 Functions should perform only one function

This is by far the most important rule in software engineering. It is difficult to decouple and test a function that performs more than one function. A function can be easily refactored if it can be separated into just one action, and the code is easy to read. Even if you just follow this advice, you’ll be better than many developers.

Bad writing

def email_clients(clients: List[Client]) :
    """Filter active clients and send them an email. Screen active clients and email them """
    for client in clients:
        if client.active:
            email(client)
Copy the code

Good way to write it

def get_active_clients(clients: List[Client]) - >List[Client]:
    """Filter active clients. """
    return [client for client in clients if client.active]




def email_clients(clients: List[Client, ...]) - >None:
    """Send an email to a given list of clients. """
    for client in clients:
        email(client)
Copy the code

This is where generators can be used to improve function writing.

A better way to write it

def active_clients(clients: List[Client]) -> Generator[Client]:
    """Only active clients. """
    return (client for client in clients if client.active)




def email_client(clients: Iterator[Client]) - >None:
    """Send an email to a given list of clients. """
    for client in clients:
        email(client)
Copy the code

3.3 The function name should indicate the function function

Bad writing

class Email:
    def handle(self) - >None:
        # Do something...


message = Email()
# What is this supposed to do again?
What does this function need to do?
message.handle()
Copy the code

Good way to write it

class Email:
    def send(self) - >None:
        """Send this message. """


message = Email()
message.send()
Copy the code

3.4 Functions should have only one layer of abstraction

If a function contains more than one layer of abstraction, it is usually a function that implements too much, and should be broken up into multiple functions to make it reusable and easier to test.

Bad writing

def parse_better_js_alternative(code: str) - >None:
    regexes = [
        #...
    ]


    statements = regexes.split()
    tokens = []
    for regex in regexes:
        for statement in statements:
            #...


    ast = []
    for token in tokens:
        # Lex.


    for node in ast:
        # Parse.
Copy the code

Good way to write it

REGEXES = (
   #...
)




def parse_better_js_alternative(code: str) - >None:
    tokens = tokenize(code)
    syntax_tree = parse(tokens)


    for node in syntax_tree:
        # Parse.




def tokenize(code: str) - >list:
    statements = code.split()
    tokens = []
    for regex in REGEXES:
        for statement in statements:
           # Append the statement to tokens.


    return tokens




def parse(tokens: list) - >list:
    syntax_tree = []
    for token in tokens:
        # Append the parsed token to the syntax tree.


    return syntax_tree
Copy the code

3.5 Do not use flags as function arguments

Flags indicate that a function does more than one thing, but a function should only do one thing, so if you need flags, you’ll write one more function.

Bad writing

from pathlib import Path


def create_file(name: str, temp: bool) - >None:
    if temp:
        Path('./temp/' + name).touch()
    else:
        Path(name).touch()
Copy the code

Good way to write it

from pathlib import Path


def create_file(name: str) - >None:
    Path(name).touch()


def create_temp_file(name: str) - >None:
    Path('./temp/' + name).touch()
Copy the code

3.6 Avoid side effects of functions

A function has side effects when it does more than just input a number and return something else. For example, side effects might be writing data to files, changing global variables, or accidentally writing all your money to a stranger.

Sometimes, though, you have to create side effects in your program — for example, in the example just mentioned, you have to write data to a file. In this case, you should try to concentrate and indicate the functions that produce these side effects, for example, making sure that only one function produces data that is written to a particular file, rather than multiple functions or classes.

The main point of this advice is to avoid common pitfalls, such as analyzing state between objects without any structure, using modifiable data types that can be modified by any data, or using instance objects of a class without centralized side effects, etc. If you can follow this advice, you’ll be happier than many developers.

Bad writing

# This is a module-level name.
# It's good practice to define these as immutable values, such as a string.
# However...
name = 'Ryan McDermott'


def split_into_first_and_last_name() - >None:
    # The use of the global keyword here is changing the meaning of the
    # the following line. This function is now mutating the module-level
    # state and introducing a side-effect!
    # change global variable ();
    The second call to the function will result in a different result from the first call.
    global name
    name = name.split()


split_into_first_and_last_name()


print(name)  # ['Ryan', 'McDermott']


# OK. It worked the first time, but what will happen if we call the
# function again?
Copy the code

Good way to write it

def split_into_first_and_last_name(name: str) - >list:
    return name.split()


name = 'Ryan McDermott'
new_name = split_into_first_and_last_name(name)


print(name)  # 'Ryan McDermott'
print(new_name)  # ['Ryan', 'McDermott']
Copy the code

Another good way to write it

from dataclasses import dataclass


@dataclass
class Person:
    name: str


    @property
    def name_as_first_and_last(self) - >list:
        return self.name.split() 


# The reason why we create instances of classes is to manage state!
person = Person('Ryan McDermott')
print(person.name)  # 'Ryan McDermott'
print(person.name_as_first_and_last)  # ['Ryan', 'McDermott']
Copy the code

conclusion

The original table of contents actually has three sections:

  • Objects and data structures
  • class
    • Single Responsibility Principle (SRP)
    • Open/Closed Principle (OCP)
    • Liskov Substitution Principle (LSP)
    • Interface Segregation Principle (ISP)
    • Dependency Inversion Principle (DIP)
  • Don’t repeat

The author hasn’t updated it yet, so if you want to know more about it, you’re advised to read the corresponding section of Clean Code.



Public account recently recommended reading: \

GAN is 6 years old! It’s time to stroke him!

Hundreds of GAN papers have been downloaded! With a recent overview of generative adversarial networks! \

A bit exaggerated, a bit twisted! See how these Gans exaggerate and caricature human faces! \

It rained from heaven, but nothing for me! How about GAN for rain? \

Face to face! GAN can make the side face killer, peppa pig true face no escape? \

Losing face! GAN to predict? \

Weak water three thousand, only take your mark! How about AL (Active Learning) and GAN? \

Anomaly detection, GAN how GAN?

Virtual change of clothes! Quick overview of these latest papers how to do! \

Facial makeup migration! Take a quick look at a few papers using GAN

[1] How about the generation of GAN in medical images?

01-GAN formula concise principle of ironclad small treasure


GAN&CV communication Group, no matter small white or big guy, sincerely invite you to join! \

Discuss and exchange! Long press note to join:

Share more, long click to follow our public account: \