Python

Python - Clean code - better/good/bad examples

foxlee 2021. 2. 10. 11:10

github.com/zedr/clean-code-python

 

zedr/clean-code-python

:bathtub: Clean Code concepts adapted for Python. Contribute to zedr/clean-code-python development by creating an account on GitHub.

github.com


Use meaningful and pronounceable variable names

import datetime

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

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

Use the same vocabulary for the same type of variable

## bad
def get_user_info(): pass
def get_client_data(): pass
def get_customer_record(): pass

## good
def get_user_info(): pass
def get_user_data(): pass
def get_user_record(): pass

## better
from typing import Union, Dict, Text

class Record:
    pass

class User:
    info : str

    @property
    def data(self) -> Dict[Text, Text]:
        return {}

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

 

Use searchable names

import time

## bad 
time.sleep(86400)

## good
SECONDS_IN_A_DAY = 60 * 60 * 24
time.sleep(SECONDS_IN_A_DAY)

Use explanatory variables

## bad
import re

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

matches = re.match(city_zip_code_regex, address)
if matches:
    print(f"{matches[1]}: {matches[2]}")
    
## good
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)
if matches:
    print(f"{matches['city']}, {matches['zip_code']}")

Avoid Mental Mapping

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

## bad
for item in seq:
    print(item)
    
## good
for location in locations:
    print(location)

Don"t add unneeded context

## bad

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

## good
class Car:
    make: str
    model: str
    color: str

Use default arguments instead of short circuiting or conditionals

from typing import Text
import hashlib

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

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

Functions should do one thing

from typing import List

class Client:
    active: bool


def email(client: Client) -> None:
    pass


def email_clients(clients: List[Client]) -> None:
    """Filter active clients and send them an email.
    """
    for client in clients:
        if client.active:
            email(client)
            
## good
def get_active_clients(clients: List[Client]) -> List[Client]:
    """Filter active clients.
    """
    return [client for client in clients if client.active]

## good
def email_clients(clients: List[Client]) -> None:
    """Send an email to a given list of clients.
    """
    for client in get_active_clients(clients):
        email(client)
        
 ## better       
 def active_clients(clients: Iterator[Client]) -> Generator[Client, None, None]:
    """Only active clients"""
    return (client for client in clients if client.active)

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

Function names should say what they do

class Email:
	## bad
    def handle(self) -> None:
        pass
	## good
	def send(self) -> None:
        """Send this message"""

message = Email()
message.send()

Don't use flags as function parameters

  • flag를 통해 분기로 다른 일을 한다면 함수가 두 개 이상의 일은 한다는 것이므로 분리시켜야함
from typing import Text
from tempfile import gettempdir
from pathlib import Path

## bad
def create_file(name: Text, temp: bool) -> None:
    if temp:
        (Path(gettempdir()) / name).touch()
    else:
        Path(name).touch()
        
 ## good        
def create_file(name: Text) -> None:
    Path(name).touch()

## good
def create_temp_file(name: Text) -> None:
    (Path(gettempdir()) / name).touch()

Comments

  • 비즈니스 로직이 복잡한 경우에만 주석을 달자. 코드 자체를 설명할 필요는 없음
  • 주석으로 된 코드는 남기지 않기 - git 으로 관리
  • 코드 위치 남기지 않기