Способ почистить код в питоне

глубокое обучение

Всего113статья, эта статья о8000слово, чтение занимает ок.20минута

оригинал:GitHub.com/тогда конечно/чисто-…

Python-версия чистоты кода. Каталог выглядит так:

  1. вводить
  2. Переменная
  3. функция

1. Введение

Принципы разработки программного обеспечения из книги Роберта К. Мартина «Чистый код», и эта статья представляет собой чистый код для версии Python. Это не руководство по стилю, а руководство о том, как писать читабельный, удобный и рефакторинговый код на pyhton.

Не все изложенные здесь принципы должны строго соблюдаться, и даже лишь некоторые из них будут общепризнанными. Нижеследующее — просто руководство, но оно написано автором «Чистого кода» с многолетним опытом программирования.

Версия Python здесь 3.7+.


2. Переменные

2.1 Используйте осмысленные и интерпретируемые имена переменных

неправильное написание

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

хорошее письмо

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

2.2 Использование одного и того же словаря для переменных одного типа

неправильное написание: здесь используются три разных имени для сущностей с одинаковым символом подчеркивания.

get_user_info()
get_client_data()
get_customer_record()

хорошее письмо: Если объекты одинаковы, используемые функции должны быть согласованы.

get_user_info()
get_user_data()
get_user_record()

лучшее правописание: python — это объектно-ориентированный язык программирования, поэтому функции одного и того же объекта могут быть помещены в класс как атрибуты экземпляра или методы.

class User:
    info : str


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


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

2.3 Используйте поисковые имена

Обычно мы видим больше кода, чем пишем, поэтому очень важно, чтобы код, который мы пишем, был удобочитаемым и доступным для поиска, необъявление некоторых значимых переменных может затруднить понимание нашей программы, пример показан ниже.

неправильное написание

# 86400 表示什么呢?
time.sleep(86400)

хорошее письмо

# 声明了一个全局变量
SECONDS_IN_A_DAY = 60 * 60 * 24


time.sleep(SECONDS_IN_A_DAY)

2.4 Использование переменных с интерпретацией

неправильное написание

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

нормальное правописание

Это лучше, но по-прежнему сильно зависит от регулярных выражений.

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)

хорошее письмо

Уменьшите зависимость от регулярных выражений с помощью именования подшаблонов

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


save_city_zip_code(matches['city'], matches['zip_code'])

2.5 Не заставляйте читателей гадать

Не заставляйте читателя связываться, чтобы знать значение имени переменной, явное лучше, чем неявное.

неправильное написание

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


for item in seq:
    do_stuff()
    do_some_other_stuff()
    # ...
    # item 是表示什么?
    dispatch(item)

хорошее письмо

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


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

2.6 Нет необходимости добавлять дополнительный контекст

Если имя класса или объекта уже предоставляет некоторую информацию, нет необходимости повторять ее в переменной.

неправильное написание

class Car:
    car_make: str
    car_model: str
    car_color: str

хорошее письмо

class Car:
    make: str
    model: str
    color: str

2.7 Используйте параметры по умолчанию вместо условных выражений

неправильное написание

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

Это можно записать прямо вnameПараметр устанавливает значение по умолчанию без использования условного оператора для оценки.

хорошее письмо

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

3. Функция

3.1 Функциональные параметры (2 или менее)

Важно ограничить количество аргументов функции, чтобы помочь протестировать код функции, который вы пишете. Более 3 параметров функции могут привести к ситуации, когда тестовая композиция взрывается, то есть необходимо рассмотреть много разных тестовых случаев.

Нет идеальных параметров. Один-два параметра тоже подойдут, трех следует избегать. Если их более 3, то функции должны быть аккуратно организованы. В общем, если функция имеет более 2-х параметров, это означает, что ваша функция может многого добиться. Кроме того, во многих случаях в качестве параметра может использоваться объект высокого уровня.

неправильное написание

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

красивое написание

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

Еще один хороший способ написать

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)

отличное письмо

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

лучшее правописание

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

3.2 Функция должна выполнять только одну функцию

Это, безусловно, самое важное правило в разработке программного обеспечения. Если функция выполняет несколько функций, ее трудно отделить и протестировать. Если функцию можно разделить только на одно действие, функцию можно легко реорганизовать, а код будет легче читать. Даже если вы просто последуете этому совету, вы будете лучше многих разработчиков.

неправильное написание

def email_clients(clients: List[Client]):
    """Filter active clients and send them an email.
       筛选活跃的客户并发邮件给他们
    """
    for client in clients:
        if client.active:
            email(client)

хорошее письмо

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)

Вот собственно использование генераторов для улучшения написания функции.

лучшее правописание

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)

3.3 Имя функции должно указывать на то, что она делает

неправильное написание

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


message = Email()
# What is this supposed to do again?
# 这个函数是需要做什么呢?
message.handle()

хорошее письмо

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


message = Email()
message.send()

3.4 Функции должны иметь только один уровень абстракции

Если функция содержит более одного уровня абстракции, это обычно означает, что функция реализует слишком много функций, и функцию следует разбить на несколько функций, чтобы обеспечить повторное использование и более простое тестирование.

неправильное написание

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.

хорошее письмо

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

3.5 Не передавайте флаги в качестве аргументов функции

Флаг означает, что функция реализует более одной функции, но функция должна делать только одну вещь, поэтому, если вам нужен флаг, вы напишете еще одну функцию.

неправильное написание

from pathlib import Path


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

хорошее письмо

from pathlib import Path


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


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

3.6 Как избежать побочных эффектов функции

Функция имеет побочные эффекты, когда она делает больше, чем просто вводит значение и возвращает другие значения. Например, побочными эффектами могут быть запись данных в файл, изменение глобальных переменных или случайная запись всех ваших денег незнакомцу.

Однако иногда в программе должны быть созданы побочные эффекты — например, в только что упомянутом примере данные должны быть записаны в файл. В этом случае вы должны попытаться сосредоточиться и указать функции, которые производят эти побочные эффекты, например, чтобы гарантировать, что только одна функция будет записывать данные в конкретный файл, а не несколько функций или классов.

Суть этого совета заключается в том, чтобы избежать распространенных ловушек, таких как анализ состояния между объектами без какой-либо структуры, использование модифицируемых типов данных, которые могут модифицироваться любыми данными, или использование экземпляров объектов классов, не акцентируя внимания на побочных эффектах и ​​т.д. Если вы последуете этому совету, вы будете счастливее многих разработчиков.

неправильное написание

# 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!
    # 这里采用了全局变量,并且函数的作用就是修改全局变量,其副作用就是修改了全局变量,
    # 第二次调用函数的结果就会和第一次调用不一样了。
    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?

хорошее письмо

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']

еще одно хорошее написание

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']

Суммировать

Исходный каталог фактически состоит из трех частей:

  • Объекты и структуры данных
  • своего рода
    • Принцип единой ответственности (Single Responsibility Principle, SRP)
    • Принцип открытия/закрытия (OCP)
    • Принцип замещения Лисков (LSP)
    • Принцип разделения интерфейсов (ISP)
    • Принцип инверсии зависимостей (DIP)
  • не повторяй

Однако автор еще не обновил его, поэтому, если вы хотите понять эту часть, рекомендуется прочитать непосредственно эту часть содержания, соответствующую «Пути чистого кода».



Публичный аккаунт недавно рекомендовал прочитать:\

GAN существует уже 6 лет! Пришло время для инсульта! 

Были загружены сотни документов GAN! С недавним обзором генеративно-состязательных сетей!\

Немного преувеличено, немного искажено! Взгляните на то, как эти GAN преувеличивают и карикатурно изображают лица!\

В небе дождь, а у меня его нет! Как насчет GAN для удаления дождя?\

Исправьте свое лицо! Сможет ли ГАН сделать так, чтобы убийце профиля и свинке Пеппе действительно негде было спрятаться?\

Угасание! ГАН прогнозировать?\

Руошуй три тысячи, только бери свою отметку! Как насчет AL (активного обучения) в сочетании с GAN?\

Обнаружение аномалий, как работает GAN?

Виртуальная переодевание! Взгляните на то, что делают эти последние газеты!\

Миграция макияжа лица! Краткий обзор нескольких статей с использованием GAN

[1] Как насчет генерации GAN на медицинских изображениях?

01-Краткий принцип формулы GAN - Маленькие сокровища в железной броне


GAN&CV группа обмена , будь вы новичок или большой парень, сердечно приглашаем вас присоединиться!\

Обсуждайте и общайтесь вместе! Нажмите и удерживайте заметку [Присоединиться к группе], чтобы присоединиться:

Чтобы больше поделиться, нажмите и удерживайте, чтобы подписаться на эту официальную учетную запись:\