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 으로 관리
- 코드 위치 남기지 않기