티스토리 뷰

원문: https://testdriven.io/blog/clean-code-python/
[Clean Code in Python
This article looks at how to write clean code in Python.
testdriven.io](https://testdriven.io/blog/clean-code-python/)
내용은 일부 생략했습니다.
PEP 8
Naming
- 클래스명은 CamelCase로 한다. (
MyClass) - 변수명, 함수명, 모듈명은 snake_case로 한다. (
first_name,quick_sort(),numpy) - 상수는 대문자 snake_case로 한다. (
PI = 3.14159) - 문자열을 작은 따옴표로 할지 큰 따옴표로 할지는 일관성만 갖추면 된다.
Line formatting
- 들여쓰기는 4칸이다. 탭보다 스페이스가 선호된다.
- 한 줄은 79칸을 넘지 않는다.
- 한 줄에 여러 statement를 적지 않는다.
- Top-level 함수와 클래스 정의는 앞뒤로 두 줄의 공백을 둔다.
- 클래스 내에서 메서드 정의는 앞뒤로 한 줄의 공백을 둔다.
- 서로 다른 모듈의 import는 줄을 분리한다.
Whitespace
[]과{}내에서는 불필요한 공백을 넣지 않는다.- 줄 끝에 공백을 넣지 않는다.
- 이항 연산자 양쪽에 공백을 하나씩 넣는다.
- 키워드 인자를 지정할 때에는
=앞뒤로 공백을 넣지 않는다.
Comments
- 주석은 코드와 모순되지 않아야 한다.
- 주석은 완성된 문장이어야 한다.
- 주석을 쓸 때
#뒤에 공백이 하나 있어야 한다. - 독스트링은 첫 줄에 요약 문장을 쓰고, 그 다음에 추가 설명을 붙인다.
The Zen of Python
- 아름다운 것이 추한 것보다 낫다. (Beautiful is better than ugly.)
- 명시적인 것이 암묵적인 것보다 낫다. (Explicit is better than implicit.)
- 간결한 것이 복잡한 것보다 낫다. (Simple is better than complex.)
- 복잡한 것이 난해한 것보다 낫다. (Complex is better than complicated.)
- 수평적인 것이 내포된 것보다 낫다. (Flat is better than nested.)
- 여유로운(희소한) 것이 밀집한(조밀한) 것보다 낫다. (Sparse is better than dense.)
- 가독성은 중요하다. (Readability counts.)
- 규칙을 어겨야 할 만큼 특별한 경우라는 것은 없다. (Special cases aren't special enough to break the rules.)
- 실용성이 순수성보다 중요하기는 하지만. (Although practicality beats purity.)
- 오류는 절대로 묵시적으로 전달되어서는 안된다. (Errors should never pass silently.)
- 묵시적으로 전달하도록 '명시'한 것이 아니라면. (Unless explicitly silenced.)
- 모호함을 대할 때, 이를 추측하려는 유혹을 거부하라. (In the face of ambiguity, refuse the temptation to guess.)
- (문제해결의) 명백한 하나의 방법이 존재해야 하며, 이왕이면 유일한 방법이어야 한다. (There should be one - and preferably only one - obvious way to do it.)
- 비록 그 방법이 처음에는 명백해 보이지 않을지라도. (Although that way may not be obvious at first.)
- 지금 행동에 옮기는 것이 아예 안하는 것보다는 낫다. (Now is better than never.)
- 아예 안하는 것이 지금 당장 하는 것보다 나을 때도 많지만. (Although never is often better than right now.)
- 구현방법을 설명하기 어렵다면, 아이디어가 나쁜 것이다. (If the implementation is hard to explain, it's a bad idea.)
- 구현방법을 설명하기 쉽다면, 좋은 아이디어일지도 모른다. (If the implementation is easy to explain, it may be a good idea.)
- 네임스페이스는 개훌륭한 아이디어이다. 더 많이 사용하자! (Namespaces are one honking great idea -- let's do more of those!)
Code Principles
DRY: Don't Repeat Yourself
- 모든 정보는 하나의, 신뢰할 수 있는, 모호하지 않은 형태여야 한다.
- 코드에 중복된 부분이 있다면 iteration으로 바꿔라.
- 주의사항: DRY 원칙은 코드을 과하게 추상화시키고, 외부 의존성을 만들고, 코드가 복잡해질 수 있다. 초반부터 DRY 원칙을 적용하는 것은 좋지 않다. 잘못된 추상화보다는 조금 반복된 코드가 더 낫다.
KISS: Keep It Simple, Stupid
- 대부분 시스템은 복잡할 때보다 간단할 때 잘 동작한다.
- 시스템 설계 시 불필요한 복잡성을 발생시키지 말아야 한다.
SoC: Separation of Concerns
- 프로그램의 각 부분은 서로 다른 관심사(concern)를 가져야 한다. 대표적인 예가 MVC이다.
- 주의사항: 너무 많은 모듈을 만들지 마라. 모듈이 많아지면 문제도 많아진다. 새로운 모듈이 의미가 있을 때만 만들어라.
SOLID
- The Single-responsibility principle: 클래스를 수정하는 이유는 단 한 가지여야 한다. 클래스는 한 가지 책임만 가져야 한다.
- The Open-closed principle: 코드는 확장에는 열려 있고 수정에는 닫혀 있어야 한다. 기존 코드를 바꾸지 않고도 기능 확장이 가능해야 한다.
- The Liskov substitution principle: 하위 클래스는 상위 클래스의 자리를 문제 없이 대신할 수 있어야 한다. 상위 클래스 자리에 하위 클래스를 넣어도 계획대로 잘 동작해야 한다.
- The Interface segregation principle: 사용자가 사용하지 않는 기능을 강제로 구현하는 일은 없어야 한다. 인터페이스는 가능한 작게 나눠서, 클라이언트가 필요없는 기능에 끌려다니지 않게 해야 한다.
- The Dependency inversion principle: 구체화의 의존하지 말고 추상화에 의존하라. 추상화된 인터페이스를 먼저 설계하고, 구체적인 구현은 그에 뒤따르게 하라.
Code Formatters
Python code formatters:
Python linters
Naming convention
- 이름에는 의미와 의도가 담겨야 한다. 길고 설명적인 이름이 짧은 이름에 주석을 덧붙이는 것보다 낫다.
# This is bad
# represents the number of active users
au = 55
# This is good
active_user_amount = 55변수
- 변수명에는 명사를 쓴다.
- 변수명에는 설명적이고 의도가 드러나는 이름을 쓴다.
# This is bad
c = 5
d = 12
# This is good
city_counter = 5
elapsed_time_in_days = 12- 변수명에는 발음 가능한 이름을 쓴다.
from datetime import datetime
# This is bad
genyyyymmddhhmmss = datetime.strptime('04/27/95 07:14:22', '%m/%d/%y %H:%M:%S')
# This is good
generation_datetime = datetime.strptime('04/27/95 07:14:22', '%m/%d/%y %H:%M:%S')- 변수명에 모호하거나 직접 개발한 축약어는 쓰지 않는다.
# This is bad
fna = 'Bob'
cre_tmstp = 1621535852
# This is good
first_name = 'Bob'
creation_timestamp = 1621535852- 변수명에 쓸 단어는 일관성을 가져야 하며 동의어는 쓰지 않는다.
# This is bad
client_first_name = 'Bob'
customer_last_name = 'Smith'
# This is good
client_first_name = 'Bob'
client_last_name = 'Smith'- 상수로 쓸 값은 변수로 의미를 부여한다.
import random
# This is bad
def roll():
return random.randint(0, 36) # what is 36 supposed to represent?
# This is good
ROULETTE_POCKET_COUNT = 36
def roll():
return random.randint(0, ROULETTE_POCKET_COUNT)- 여러 자료형이 섞여있을 경우 접미사로 자료형을 붙여라.
# This is bad
names = ["Nick", "Mike", "John"]
# This is good
score_list = [12, 33, 14, 24]
word_dict = {
'a': 'apple',
'b': 'banana',
'c': 'cherry',
}- 맥락을 중복하지 마라.
# This is bad
class Person:
def __init__(self, person_first_name, person_last_name, person_age):
self.person_first_name = person_first_name
self.person_last_name = person_last_name
self.person_age = person_age
# This is good
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age함수
- 함수명에는 동사를 쓴다.
- 같은 의미의 다른 단어를 쓰지 마라.
# This is bad
def get_name(): pass
def fetch_age(): pass
# This is good
def get_name(): pass
def get_age(): pass짧고 간단한 함수를 작성하라.
하나의 함수는 하나의 작업만 한다. 해석에 몇 분이 걸리는 함수는 잘못 만든 함수이다.
# This is bad
def fetch_and_display_personnel():
data = # ...
for person in data:
print(person)
# This is good
def fetch_personnel():
return # ...
def display_personnel(data):
for person in data:
print(person)- 함수의 인자는 가능한 적게 만들어라.
# This is bad
def render_blog_post(title, author, created_timestamp, updated_timestamp, content):
# ...
render_blog_post("Clean code", "Nik Tomazic", 1622148362, 1622148362, "...")
# This is good
class BlogPost:
def __init__(self, title, author, created_timestamp, updated_timestamp, content):
self.title = title
self.author = author
self.created_timestamp = created_timestamp
self.updated_timestamp = updated_timestamp
self.content = content
blog_post1 = BlogPost("Clean code", "Nik Tomazic", 1622148362, 1622148362, "...")
def render_blog_post(blog_post):
# ...
render_blog_post(blog_post1)- 함수 내에서 flag를 사용하지 마라.
text = "This is a cool blog post."
# This is bad
def transform(text, uppercase):
if uppercase:
return text.upper()
else:
return text.lower()
uppercase_text = transform(text, True)
lowercase_text = transform(text, False)
# This is good
def uppercase(text):
return text.upper()
def lowercase(text):
return text.lower()
uppercase_text = uppercase(text)
lowercase_text = lowercase(text)- 부작용을 제거하라.
주석
과한 주석은 코드를 더럽게 만든다.
Documentation은 사용자에게, (함수, 모듈 등을) 언제 어떻게 쓰는지 설명한다.
Comment는 개발자에게, 왜 코드를 이렇게 썼는지 설명한다.
Clean Code는 개발자에게, 무엇을 하는 코드인지 설명한다.
나쁜 코드는 주석을 달지 말고 다시 짜라.
가독성이 높은 코드는 주석이 필요 없다.
# This checks if the user with the given ID doesn't exist.
if not User.objects.filter(id=user_id).exists():
return Response({
'detail': 'The user with this ID does not exist.',
})- 불필요한 주석은 쓰지 않는다.
numbers = [1, 2, 3, 4, 5]
# This variable stores the average of list of numbers.
average = sum(numbers) / len(numbers)
print(average)- 올바른 종류의 주석을 써라.
- 디버그에 사용한 코드는 남겨두지 마라.
Decorators
- 데코레이터를 이용하면 SoC(Separation of concern)을 지키면서 모듈화된 코드를 만들 수 있다.
def ask_for_passcode(func):
def inner():
print('What is the passcode?')
passcode = input()
if passcode != '1234':
print('Wrong passcode.')
else:
print('Access granted.')
func()
return inner
@ask_for_passcode
def start():
print("Server has been started.")
@ask_for_passcode
def end():
print("Server has been stopped.")
start() # decorator will ask for password
end() # decorator will ask for passwordContext manager
- 외부 자원을 이용할 때는 context manager를 써라.
Iterator
- Iterator로 loop을 간단하게 써라.
# This is bad
names = ["Mike", "John", "Steve"]
names_iterator = iter(names)
for i in range(len(names)):
print(next(names_iterator))
# This is good
names = ["Mike", "John", "Steve"]
for name in names:
print(name)'python' 카테고리의 다른 글
| 파이썬에서 3차원 그래프 interactive하게 회전시키기 (0) | 2025.10.19 |
|---|---|
| 넘파이와 파이썬의 정수 표현 범위 (0) | 2025.01.21 |
| 파이썬 중고급 스킬들 (0) | 2025.01.10 |
| conda 초간단 매뉴얼 (0) | 2024.08.20 |
| list.extend와 list.iadd의 차이 (0) | 2024.07.07 |
댓글