PEP8은 파이썬 코드를 작성할 때 지켜야 하는 스타일 가이드이다. PEP는 Python Enhancement Proposal의 약자로, 파이썬 개선 제안서를 의미하고 파이썬의 개선을 위해 제안되는 다양한 아이디어와 기능, 정보 등을 공식적으로 기술한 것이다. PEP8은 파이썬 코드의 가독성을 높이고, 파이썬 커뮤니티 내에서 일관된 코드 스타일을 유지하기 위해 만들어졌다.
이러한 스타일 가이드를 따르는 것은 코드의 일관성을 유지하고 다른 개발자가 코드를 이해하기 쉽게 돕는다. PEP8을 준수하는 것은 특히 큰 프로젝트나 많은 사람들이 함께 작업할 때 중요하다.
해달 글은 PEP8의 공식 문서를 보고 정리한 것으로, 원문을 읽고 싶으면 글 하단의 링크를 참고하길 바란다.
가독성이 중요한 것은 파이썬 철학의 일부이다. 코드의 일관성은 가독성과 연관되어 있다. PEP8이 작성된 배경도 이와 동일하다. 코드의 일관성을 위해 코드 스타일과 관련된 가이드 문서이다.
가능하다면 해당 가이드에 맞춰 코드를 작성하는 것이 좋지만, 적합하지 않은 상황도 존재한다. 무리하게 가이드에 맞추기 보다 상황에 맞게 적용하면 된다. 스타일 가이드를 맞추기 어려운 상황의 예는 다음과 같다.
들여쓰기는 4개의 공백을 사용한다. 연속 줄에서는 4칸 규칙이 선택 사항이다.
# 올바른 예시들
# '(' 기호에 맞춰 정렬한다.
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 인수를 나머지 인수와 구별하려면 공백 4개를 추가한다.
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
# 들여쓰기를 하면 레벨이 추가된다.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
# 구문을 닫는 문자 같은 경우는 아래와 같이 표현할 수 있다.
my_list = [
1, 2, 3,
4, 5, 6,
]
my_list = [
1, 2, 3,
4, 5, 6,
]
공백이 선호되는 들여쓰기 방법이다. 탭은 이미 탭으로 들여쓰기된 코드와의 일관성을 유지하기 위해서만 사용해야 한다. Python에서는 들여쓰기를 위해 탭과 공백을 혼합하는 것을 허용하지 않는다.
모든 줄을 최대 79자로 제한한다. 구조적 제한(독스트링 또는 주석)이 적은 긴 텍스트 블록의 경우 행 길이는 72자로 제한되어야 한다. 일부 팀은 더 긴 라인 길이를 선호한다. 해당 문제는 팀 내에서 합의를 하거나 또는 코드가 주석과 독스트링이 래핑되어 있다면 줄 길이 제한을 최대 99자로 늘려도 괜찮다.
너무 긴 라인에 대해서는 백슬래시를 사용할 수도 있다.
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written', 'w') as file_2:
file_2.write(file_1.read())
최상위 함수 및 클래스 정의를 두 개의 빈 줄로 구분한다. 클래스 내부의 메서드 정의는 단일 빈 줄로 구분한다.
핵심 Python 배포판의 코드는 항상 UTF-8을 사용해야 하며 인코딩 선언이 있어서는 안 된다. Python 표준 라이브러리의 모든 식별자는 ASCII 전용 식별자를 사용해야 하며, 가능할 때마다 영어 단어를 사용해야 한다.
import문은 일반적으로 별도의 줄에 있어야 한다.
# Correct:
import os
import sys
# Wrong:
import sys, os
# Correct:
from subprocess import Popen, PIPE
import문은 다음 순서로 그룹화되어야 한다. 그룹화된 import문은 빈 줄로 구분한다.
import문은 다음과 같이 선언하는 것을 지향해야 한다.
# 일반적인 경우 절대 경로로 선언한다.
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example
# 결대 경로가 불필요하게 장황할 경우 상대 경로로 선언도 일부 허용된다.
from . import sibling
from .sibling import example
# *(와일드카드)를 통한 선언은 지양해야 한다.
from mypkg.sibling import *
all, author, version과 같은 것들을 Dunder(매직 메소드)이라고 부른다. 이것들은 모듈 주석과 import 사이에 위치하여야 한다. 단, from future는 모듈 주석 바로 다음에 위치시키도록 한다.
"""This is the example module.
This module does stuff.
"""
from __future__ import barry_as_FLUFL
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
import os
import sys
Python에서는 작은 따옴표로 묶인 문자열과 큰따옴표로 묶인 문자열이 동일하다. PEP는 이에 대한 권장 사항을 제시하지 않는다. 둘 중 하나를 선택하여 해당 방식을 준수한다. 그러나 문자열에 작은따옴표 또는 큰따옴표 문자가 포함된 경우 문자열에 백슬래시가 들어가지 않도록 다른 하나를 사용해야 한다. 이를 통해 가독성을 향상 시킬 수 있다.
올바른 공백 사용과 옳지 않은 공백 사용에 대한 예시를 살펴보자.
# Correct:
spam(ham[1], {eggs: 2})
# Wrong:
spam( ham[ 1 ], { eggs: 2 } )
# Correct:
foo = (0,)
# Wrong:
bar = (0, )
# Correct:
if x == 4: print(x, y); x, y = y, x
# Wrong:
if x == 4 : print(x , y) ; x , y = y , x
# Correct:
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
# Wrong:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : step]
ham[ : upper]
# Correct:
spam(1)
# Wrong:
spam (1)
# Correct:
dct['key'] = lst[index]
# Wrong:
dct ['key'] = lst [index]
# Correct:
x = 1
y = 2
long_variable = 3
# Wrong:
x = 1
y = 2
long_variable = 3
더 많은 예시가 있다. 추가적인 예시 확인은 공식 문서를 살펴보자
후행 쉼표는 선택이지만, 한 요소의 튜플을 만들 때는 필수적이다.
# Correct:
FILES = ('setup.cfg',)
# Wrong:
FILES = 'setup.cfg',
항목이 시간이 지남에 따라 확장될 것으로 예상되는 경우 아래와 같이 표현한다. 단일 튜플의 경우는 위의 예시를 따른다.
# Correct:
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
# Wrong:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)
블록 주석은 일반적으로 그 뒤에 오는 일부 코드(또는 전체 코드)에 적용되며, 해당 코드와 같은 수준으로 들여쓰기 된다. 블록 주석의 각 줄은 # 기호와 한 칸의 공백으로 시작한다(단, 주석 내부에 추가적으로 들여쓰기된 텍스트가 있는 경우는 예외이다).
블록 주석 내에서 문단을 구분하려면, # 기호만 있는 줄을 사용하여 문단 사이를 나눈다.
인라인 주석을 자제해서 사용해야 한다. 인라인 주석은 명령문과 같은 줄에 있는 주석이다. 인라인 주석은 문에서 최소한 두 개의 공백으로 구분되어야 한다. #과 공백 하나로 시작해야 한다.
인라인 주석은 불필요하며 명백한 내용을 언급을 피해야 한다. 하지만 다음과 같이 명백한 내용이 아닌 주의 사항과 같은 경우 사용할 수도 있다.
x = x + 1 # Compensate for border
모든 공용 모듈, 함수, 클래스 및 메소드에 대한 docstring을 작성하자. 비공개 메소드에는 docstring이 필요하지 않지만 메소드가 수행하는 작업을 설명하는 주석이 있어야 한다. 이 주석은 def 라인 다음에 나타나야 한다.
# 여러줄 일 때
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
# 한 줄 일 때
"""Return an ex-parrot."""
단일 문자 변수 이름으로 문자 'l'(소문자 el), 'O'(대문자 oh) 또는 'I'를 사용하지 말자.
일부 글꼴에서는 이러한 문자가 숫자 1과 0과 구별되지 않는다. 'l'을 사용하고 싶다면 대신 'L'을 사용하자.
표준 라이브러리에 사용되는 식별자는 ASCII와 호환되어야 한다.
모듈 이름은 모두 소문자로 이루어져야 합다. 가독성을 높이려면 모듈 이름에 언더바를 사용할 수 있다. Python 패키지에는 모두 소문자로 된 짧은 이름이 있어야 하지만 언더바 사용은 권장되지 않는다.
클래스 이름은 일반적으로 CapWords(CamelCase) 규칙을 사용해야 한다.
타입 변수 이름은 일반적으로 CapWords를 사용하며, 짧은 이름을 선호한다. 예를 들어 T, AnyStr, Num과 같은 이름들이 사용된다. 공변(covariant) 또는 반공변(contravariant) 동작을 선언하는 데 사용되는 변수에는 각각 _co 또는 _contra 접미사를 추가하는 것이 권장된다.
예외는 클래스여야 하므로 여기에는 클래스 명명 규칙이 적용된다. 예외 이름에 접미사 "Error"를 사용해야 한다.
전역변수 이름은 함수 명명 규칙을 따른다.
함수 이름은 소문자여야 하며, 가독성을 높이기 위해 필요에 따라 단어를 밑줄로 구분해야 한다. 변수 이름은 함수 이름과 동일한 규칙을 따른다.
항상 인스턴스 메소드에 대한 첫 번째 인수로 self 사용하자. 항상 클래스 메소드의 첫 번째 인수로 cls 사용하자.
함수 명명 규칙을 사용하자. 가독성을 높이기 위해 필요에 따라 단어를 밑줄로 구분하고 소문자를 사용한다.
비공개 메서드와 인스턴스 변수에 대해서만 앞에 밑줄을 하나만 사용한다.
하위 클래스와의 이름 충돌을 피하려면 앞에 두 개의 밑줄을 사용하여 Python의 이름 맹글링 규칙을 사용한다.
상수는 일반적으로 모듈 수준에서 정의되며 단어를 구분하는 밑줄과 함께 모두 대문자로 작성된다. 예를 들면 MAX_OVERFLOW 및 TOTAL가 있다.
클래스의 메서드와 인스턴스 변수(통칭하여 "속성")가 public인지 private인지 항상 결정한다. 확실하지 않은 경우 private를 선택한다. public 속성을 private로 설정하는 것보다 나중에 public로 설정하는 것이 더 쉽다.
None과 같은 싱글톤과의 비교는 항상 is 또는 is not을 사용하여 수행해야 하며, 동등 연산자를 사용해서는 안 된다.__eq__, __ne__, __lt__, __le__, __gt__, __ge__)을 모두 구현하는 것이 가장 좋다.def문을 사용한다.def f(x): return 2*x, Wrong: f = lambda x: 2*x)except: 가 아닌 가능한 특정 예외를 언급한다.''.startswith() 및 ''.endswith()를 사용하여 접두사 또는 접미사를 확인한다. startwith() 및 endwith()는 더 깔끔하고 오류 발생 가능성이 적다.isinstance()를 사용해야 한다.False이라는 사실을 사용한다.True 또는 False와 비교하지 않는다.# Correct:
code: int
class Point:
coords: Tuple[int, int]
label: str = '<unknown>'
# Wrong:
code:int # No space after colon
code : int # Space before colon
class Test:
result: int=0 # No spaces around equality sign