본문 바로가기
파이썬

[Python] PEP 8 - 파이썬 코드 스타일 가이드

by 책 읽는 개발자_테드 2022. 9. 4.
반응형

파이썬 PEP 8을 번역한 내용입니다. https://peps.python.org/pep-0008/

 

코드는 작성된 것보다 더 자주 읽혀진다. 여기에 제공되는 가이드라인은 코드의 가독성을 개선하고, 광범위한 파이썬 코드에서 일관성을 유지하기 위 것이다. PEP20에서 말했듯이 가독성이 중요하다.

 

스타일 가이드는 일관성에 관한 것이다. 스타일 가이드에서 일관성은 중요하고, 프로젝트 내에서는 일관성이 더 중요하고, 하나의 모듈 또는 기능 내에서는 일관성이 가장 중요하다.

 

언제 일관성이 없어지는지 알아야한다. 때로는 스타일 가이드 추천들이 적용되지 않을 수 있다. 확신이 서지 않으면, 본인의 최선의 판단을 사용하자. 다른 예시들을 보고, 무엇이 최선인지 결정하자. 그리고 질문하는걸 주저하지 말자.

 

특히 PEP를 준수하기 위해 이전 버전과의 호환성을 깨뜨리면 안된다. 특정 가이드라인을 무시해야하는 다른 좋을 이유로 다음과 같은 것들이 있다.

 

1. 가이드라인을 적용하면 이 PEP를 따르는 코드를 읽는 데 익숙한 사람이라도 코드를 덜 읽을 수 있다. 

2. 코드를 깨뜨리기도 하는 주변 코드와 일관성을 유지할 수 있다.(비록 이것이 다른 사람의 난장판을 정리할 수 있는 기회이기도 하지만)

3. 문제의 코드가 가이드라인의 도입보다 앞선 것이며 해당 코드를 수정할 다른 이유가 없을 수 있다.

4. 코드가 스타일 가이드에서 권장하는 기능을 지원하지 않는 이전 버전의 파이썬과 계속 호환되어야 할 수 있다.

 

코드 레이아웃

들여쓰기

· 들여쓰기 수준 하나 당 4개의 공백을 사용한다. 

· 멀티 라인은 중괄호 내부를 결합하는 파이썬의 암시적 라인을 사용하거나, 내어쓰기(Hanging indent)를 사용하여 래핑된 요소를 수직으로 정렬한다.

· 내어쓰기를 사용할 때 다음 사항을 고려한다. 첫 번째 줄에는 인수가 없어야 하며, 연속된 라인으로 자신을 명확하게 구분하기 위해 추가 들여쓰기를 사용한다.

## 올바른 코드:

# 여는 구분자를 가진 정렬
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)
    
## 잘못된 코드:

# 수직 정렬을 사용하지 않을 때 첫 번째 줄의 인수는 금지된다.
Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# 들여쓰기를 구분할 수 없으므로, 추가 들여쓰기가 필요하다.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

 

 

연속된 라인의 경우 4-space 규칙이 선택 사항이다.

 

# 내어쓰기는 4칸과 다르게 들여쓰기 할 수 있다.
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

 

· if 문의 조건 부분이 여러 줄에 걸쳐 작성되어야 할 때, if 키워드와 단일 공백, 여는 괄호 조합은 멀티라인 조건문의 후속 줄을 위해 자연스러운 4-space 들여쓰기를 생성한다. 

 

이렇게 하면 if 문 내부의 중첩 들여쓰기된 코드 모음과 시각적 충돌이 발생할 수 있으며, 자연스럽게 4칸으로 들여쓰기 된다.

 

# 추가 들여쓰기가 없다.
if (this_is_one_thing and
    that_is_another_thing):
    do_something()


# 조건부 멀티라인에 추가 들여쓰기를 더한다.
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

 

· 멀티라인 구문에서 닫는 중괄호/대괄호/괄호는 다음과 같이 목록의 마지막 줄에서 공백이 아닌 첫 번째 문자 아래에 정렬될 수 있다. 

 

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

 

또는 다음과 같이 멀티 라인 구성을 시작하는 줄의 첫 번째 문자 아래에 정렬될 수 있다.

 

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

 

탭, 스페이스

· 스페이스는 선호되는 들여쓰기 방법이다.

· 탭은 이미 탭으로 들여쓰기된 코드와 일관성을 유지하기 위해서만 사용해야한다.

 

최대 라인 길이

· 모든 줄은 최대 79자로 제한한다.

· 구조적 제한이 긴 텍스트 블록(독스트링 또는 주석)의 경우 줄 길이는 72자로 제한되어야 한다.

 

필요한 에디터 창 너비를 제한하면 여러 파일을 나란히 열수 있으며 인접한 열에 두 버전을 표시하는 코드 검토 도구를 사용할 때 잘 작동한다.

 

대부분의 도구에서 기본 래핑은 코드의 시각적 구조를 방해하여 이해하기 어럽게 만든다. 도구가 줄 바꿈할 때 마지막 열에 마커 글리프를 배치하더라도 창 너비가 80으로 설정된 편집기에서 줄 바꿈을 피하기 위해 제한이 선택된다. 일부 웹 기반 도구는 동적 줄 바꿈을 전혀 제공하지 않을 수 있다.

 

일부 팀은 더 긴 라인 길이를 선호한다. 이 문제에 대한 합의에 도달할 수 있는 팀에서 독점적으로 또는 주로 유지 관리하는 코드의 경우 주석과 독스트링이 여전히 72자로 줄 바꿈되는 경우 라인 길이 제한을 최대 99자로 늘릴 수 있다.

 

파이썬 표준 라이브러리는 보수적이며, 행을 79자로 제한하길 요구한다. 독스트링/주석은 72자로 제한한다.

 

· 긴 라인 래핑에 선호되는 방법은 괄호, 대활호 및 중괄호 안에 파이썬의 암시적 연속 라인을 사용하는 것이다. 

· 긴 라인은 표현식을 괄호로 묶어 여러 라인으로 나눌 수 있다. 연속 라인을 위해 백슬래시를 사용하는 것보다 우선적으로 사용해야한다. 

 

때때로 백슬래시가 적절할 수 있다. ex) 길고 여러 개인 with-문은 파이썬 3.10 이전에는 암시적 라인 연속은 사용할 수 없었으므로 백슬래시는 이 경우에 허용된다.

 

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



이진 연산자 전후 줄 바꿈

· 수십 년 동안 권장되온 스타일은 이진 연산자 이후에 줄 바꿈하는 것이다.

· 하지만 이는 두 가지 면에서 가독성을 떨어뜨릴 수 있다.

1. 연산자는 화면의 여러 열에 흩어져 있는 경향이 있다.

2. 각 연산자는 피연산자에서 이전 줄로 이동한다.

 

여기서 눈은 추가된 항목과 빼는 항목을 구분하기 위해 추가 작업을 수행해야 한다.

# 문제: 연산자가 피연산자에서 멀리 떨어져 있다.
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

 

· 가독성 문제를 해결하기 위해 각 라인에 연산자, 피연산자 순으로 코드를 배치할 수 있다.

- 이를 Knuth 스타일이라 한다.

 income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

 

빈 줄

· 최상위 함수 및 클래스 정의를 두 개의 빈 줄로 묶는다.

· 클래스 내부의 메서드 정의는 단일 빈 줄로 둘러싸여 있다.

· 관련 기능 그룹을 구분하기 위해 여분의 빈 줄을 사용할 수 있다. 관련된 한 줄짜리 묶음 사이에 빈줄을 생략할 수 있다.

· 논리적 섹션을 나타내기 위해 함수에 빈 라인을 드물게 사용한다.

· 파이썬은 control-L(^L) 폼 피드 문자를 공백으로 허용한다. 

 

많은 도구에서 이러한 문자를 페이지 구분 기호로 취급하므로 파일의 관련 섹션 페이지를 구분하는 데 사용할 수 있다. 일부 에디터와 웹 기반 코드 뷰어는 control-L을 폼 피드로 인식하지 못할 수 있으며, 그 자리에 다른 자형(glyph)이 표시된다. 

 

소스 파일 인코딩

· 핵심 파이썬 배포판의 코드는 항상 UTF-8을 사용해야하며, 인코딩 선언이 있어서는 안 된다.

 

표준 라이브러리에서 비 UTF-8 인코딩은 테스트 목적으로만 사용해야 한다. ASCII가 아닌 문자는 가급적이면 장소와 사람 이름을 나타낼 때만 사용해야한다. ASCII가 아닌 문자를 데이터로 사용하는 경우 z̯̯͡a̧͎̺l̡͓̫g̹̲o̡̼̘ 및 BOM(Byte Order Mark)와 같은 유니코드 문자를 사용하면 안된다. 

 

파이썬 표준 라이브러리의 모든 식별자는 ASCII 전용 식별자를 사용해야 하며 가능하면 영어 단어를 사용해야 한다. 전 세계 사용자를 대상으로 하는 오픈 소스 프로젝트는 유사한 정책을 채택하는 것이 좋다.  

 

import

· import는 일반적으로 별도의 줄에 있어야 한다.

 

# 옳음:
import os
import sys

# 틀림:
import sys, os

 

다음은 허용된다.

 

# 옳음:
from subprocess import Popen, PIPE

 

· import는 항상 파일 상단, 모듈 주석 및 독스트링 바로 뒤, 모듈 전역 및 상수 앞에 배치된다.

· import는 다음 순서로 그룹화되어야 한다.

1. 스탠다드 라이브러리 import

2. 관련 써드 파티 import

3. 특정 로컬 애플리케이션/라이브러리 import

 

각 import 그룹 사이에 빈 라인을 넣어야 한다.

 

· 절대(absolute) import는 시스템이 잘못 구성된 경우(예: 패키지 내부의 디렉터리가 sys.path에 있는 경우) 일반적으로 더 읽기 쉽고 더 잘 작동하는 경향이 있으므로 권장된다. 또는 최소한 더 나은 오류 메시지를 제공하는 경향이 있다

 

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

 

· 명시적인 상대 import는 특히 절대 import를 사용하는 것이 불필요하게 장황하고, 복잡한 패키지 레이아웃을 처리할 때 절대 import에 대한 허용 가능한 대안이다.

 

from . import sibling
from .sibling import example

 

표준 라이브러리 코드는 복잡한 패키지 레이아웃을 피하고, 항상 절대 import를 사용해야한다.

 

· 클래스를 포함하는 모듈에서 클래스를 가져올 때 일반적으로 다음과 같이 입력해도 된다.

 

import myclass
import foo.bar.yourclass

 

· 와일드카드 import(from <module> import *)는 네임스페이스에 어떤 이름이 있는지 명확하지 않게 하여, 자동화 도구와 코드를 읽는 사람을 혼동시키므로 피해야 한다.

 

와일드카드 import에 대한 한 가지 방어 가능한 사용 사례가 있다. 내부 인터페이스를 공개 API의 일부로 다시 게시하는 것이다.

 

모듈 수준의 던더 이름

· 모듈 수준 던더(__all__, __author__, __version__과 같이 앞 뒤로 밑줄 2개가 있는)는 모듈 docstring 뒤에 위치해야한다. 또한 from __future__ imports를 제외한 모든 import 문 앞에 위치해야 한다.

 

""" docstring은
모듈 수준 던더 앞에 위치합니다.
"""
 
from __future__ import barry_as_FLUFL
 
__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'
 
import os
import sys

 

문자열 따옴표

· 파이썬에서 작은따옴표 문자열과 큰따옴표 문자열은 동일하다. 하지만 PEP은 이 둘을 함께 사용하는 것을 권장하지 않는다.

- 문자열에 작은따옴표 또는 큰따옴표 문자가 포함된 경우 둘 중 하나만을 사용하여 문자열에서 백슬래시 사용을 방지해야한다. 이것은 가독성을 향상시킨다.

· 삼중 따옴표로 묶인 문자열의 경우 항상 큰 따옴표를 사용하여 PEP 257의 독스트링 규칙과 일관성을 유지하자.

 

표현식과 명령문의 공백

짜증 나는 행위들(Pet Peeves)

· 다음과 같은 상황에서는 불필요한 공백을 피하자.

- 소괄호, 중괄호, 대괄호 바로 안에 공백을 피하자.

 

# 옳음:
spam(ham[1], {eggs: 2})
 
# 틀림:
spam( ham[ 1 ], { eggs: 2 } )

 

- 트레일링 콤마와 닫는 괄호 사이에 공백을 피하자. 

 

# 옳음:
foo = (0,)
 
# 틀림:
bar = (0, )

 

- 콤마, 세미콜론, 콜론 바로 앞에 공백을 피하자.

 

# 옳음:
if x == 4: print(x, y); x, y = y, x
 
# 틀림:
if x == 4 : print(x , y) ; x , y = y , x

 

- 슬라이스에서 콜론은 이항 연산자처럼 작동하며, 양쪽에 동일한 양이 있어야 한다(가장 낮은 우선 순위의 연사자로 처리).

- 확장된 슬라이이스(extended slice)에서 두 콜론에 동일한 간격을 적용해야 한다. 예외적으로, 슬라이스 매개변수가 생략되면, 공백이 생략된다.

 

# 옳음:
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]
 
# 틀림:
ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]

 

- 함수 호출의 인수 목록을 시작하는 여는 괄호 바로 앞에 공백을 피하자.

 

# 옳음:
spam(1)
 
# 틀림:
spam (1)

 

- 인덱싱 또는 슬라이싱을 시작하는 여는 괄호 바로 앞에 공백을 피하자.

 

# 옳음:
dct['key'] = lst[index]
 
# 틀림:
dct ['key'] = lst [index]



- 연상자 주위에 둘 이상의 공백을 두어 다른 연산자와 정렬하는 것을 피하자.

# 옳음:
x = 1
y = 2
long_variable = 3
 
# 틀림:
x             = 1
y             = 2
long_variable = 3

 

다른 추천사항들

· 어디에서나 후행 공백을 피해야한다.

· 할당(=), 증가 할당(+=, -= 등), 비교(==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans(and, or, not) 같은 이항 연산자는 항상 양쪽에 단일 공백으로 묶는다.

· 우선 순위가 다른 연산자를 사용하는 경우 우선 순위가 가장 낮은 연산자 주위에 공백을 추가하는 것이 좋다. 이때 두 개 이상의 공백을 사용하지 말고, 이항 연산자의 양쪽에 항상 같은 양의 공백을 사용한다.

 

# 옳음:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
 
# 틀림:
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

 

- 함수 애너테이션은 콜론에 대한 일반 규칙을 사용해야 하며, 화살표 주위에 항상 공백이 있어야 한다.

 

# 옳음:
def munge(input: AnyStr): ...
def munge() -> PosInt: ...
 
# 틀림:
def munge(input:AnyStr): ...
def munge()->PosInt: ...

 

- 키워드 매개변수 또는 기본 값을 나타내기 위해 사용하는 = 기호 주위에는 공백을 사용하지 말자.

 

# 옳음:
def complex(real, imag=0.0):
    return magic(r=real, i=imag)
 
# 틀림:
def complex(real, imag = 0.0):
    return magic(r = real, i = imag)

 

그러나 매개 변수 애너테이션과 기본 값을 결합할 때는 = 기호 주위에 공백을 사용하자.

 

# 옳음:
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...
 
# 틀림:
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...

 

- 복합 문(같은 줄에 여러 문)은 일반적으로 권장되지 않는다.

 

# 옳음:
if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()

# 틀림:
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()

 

if/for/while을 작은 바디에서 같은 줄에 함께 사용할 수는 있지만, 다중 절에서는 사용하면 안된다.

 

# 틀림:
if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()
 
# 틀림:
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()
 
try: something()
finally: cleanup()
 
do_one(); do_two(); do_three(long, argument,
                             list, like, this)
 
if foo == 'blah': one(); two(); three()

 

트레일링 콤마 사용하기

· 트레일링 콤마는 주로 선택적으로 사용된다.

- 예외적으로 하나의 요소를 갖는 튜플을 만들 때는 필수적이다. 

 

# 옳음:
FILES = ('setup.cfg',)
 
# 틀림:
FILES = 'setup.cfg',

 

· 트레일링 콤마가 중복되면 버전 제어 시스템을 사용할 때, 값 또는 인수 list를 사용할 때, imports한 아이템이 시간이 지남에 따라 확장될 것으로 예상되는 경우 유용하다.

- 패턴은 각 값을 한 줄에 단독으로 넣고, 항상 후행 쉼표를 추가하고 다음 줄에 다는 괄호를 추가하는 것이다.

- 그러나 닫는 구분자와 같은 줄에 후행 쉴표를 사용하는 것은 이치에 맞지 않다. (위 싱글톤 튜플은 제외)

 

# 틀림:
FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )
# 옳음:
FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

 

주석

· 코드와 모순되는 주석은 주석이 없는 것보다 나쁘다. 코드가 변경될 때 항상 주석을 최신 상태로 유지하는 데 우선 순위를 두자.

· 주석은 완전한 문장이어야 한다. 소문자로 시작하는 식별자가 아닌 경우 첫 번째 단어는 대문자여야 한다.

· 블록 주석은 일반적으로 완전한 문장으로 구성된 하나 이상의 단락으로 구성되며 각 문장은 마침표로 끝난다.

· 여러 문장으로 된 주석에서는 마지막 문장을 제외하고 문장 끝 마침표 뒤에 두 개의 공백을 사용해야 한다.

· 비영어권 국가의 파이썬 코더가 본인의 코드를 읽을 수 있다는 120% 확신이 없다면, 영어로 의견을 작성하자.

 

블록 주석

· 블록 주석은 일반적으로 뒤에 오는 일부 코드에 적용되며 해당 코드와 동일한 수준으로 들여쓰기된다.

· 주석 내부에 들여 쓴 텍스트가 아닌 경우 블록 주석의 각 줄은 # 및 단일 공백으로 시작한다.  

· 블록 주석 내부의 단락은 # 하나를 포함하는 행으로 구분된다.

 

인라인 주석

· 인라인 주석은 조금만 사용하자.

· 인라인 주석은 불필요하며, 명백하게 명시되어 있으면 실제로 주의를 산만하게 한다. 다음과 같이 사용하면 안된다.

 

x = x + 1                 # Increment x

 

다음은 유용할 수 있다.

 

x = x + 1                 # Compensate for border

 

· 인라인 주석은 명령문과 같은 줄에 있는 주석이다. 인라인 주석은 명령문에서 두 개 이상의 공백으로 분리되어야 한다. 그리고 # 및 단일 공백으로 시작해야 한다.

 

Docstring

· 좋은 docstring을 작성하기 위한 규칙은 PEP-257에 정의되어있다.

· 모든 공개 모듈, 함수, 클래스 및 메서드에 대한 docstring을 작성하자. 비공개 메서드에는 필요하지 않지만, 메서드가 하는 일을 설정하는 주석이 있어야 한다. 이 주석은 def 라인 뒤에 나타나야 한다.

· PEP-257은 좋은 docstring 규칙을 설명한다. 가장 중요한 것은 여러 줄의 docstring을 끝내는 “””가 한 줄에 있어야 한다. 

 

"""Return a foobang
 
Optional plotz says to frobnicate the bizbaz first.
"""

 

· 한 줄의 독스트링의 경우 닫는 “””를 같은 줄에 유지한다. 

 

"""Return an ex-parrot."""

 

네이밍 컨벤션

파이썬 라이브러리의 명명 규칙은 조금 엉망이므로 완전히 일관성을 가질 수는 없다. 그럼에도 불구하고 권장되는 명명 표준은 있다. 새 모듈 및 패키지는 이러한 표준에 따라 작성되어야 하지만, 기존 라이브러리의 스타일이 다른 경우 내부 일관성이 선호된다.

 

오버라이딩 원칙

API의 공개 부분으로 사용자에게 표시되는 이름은 구현보다 사용법을 반영하는 규칙을 따라야 한다.

 

네이밍 스타일

다양한 네이밍 스타일이 있다. 용도와 상관없이 어떤 명명 스타일이 사용되고 있는지 인식할 수 있으면 도움이 된다.

 

다음과 같은 네이밍 스타일이 일반적으로 구별된다.

- b (single lowercase letter)

- B (single uppercase letter)

- lowercase

- lower_case_with_underscores

- UPPERCASE

- UPPERCASE_WITH_UNDERSCORES

- CapitalizedWords 

CapWords, CamelCase, StudlyCaps로 불린다. CapWords에서 두문자어(머리글자로 만든 말)를 사용할 때 두문자어의 모든 문자를 대문자로 사용하자. 즉 HTTPServerError는 httpServerError보다 낫다.

- mixedCase (CapWords와 초기 소문자가 다름)

- Capitalized_Words_With_Underscores (ugly!)

 

· 짧은 고유 접두사를 사용하여 관련 이름을 그룹화하는 스타일도 있다. 이는 파이썬에서 많이 사용되지 않지만, 완전성을 위해 언급된다. 

 

ex) os.stat() 함수는 항목이 전통적으로 st_mode, st_size, st_mtime 등과 같은 이름을 갖는 튜플을 반환한다.

- 프로그래머에게 익숙한 POSIX 시스템 호출 구조체의 필드와의 대응성을 강조하기 위한 것이다.

 

· X11 라이브러리는 모든 공개 기능에 선행 X를 사용한다. 파이썬에서 이 스타일은 일반적으로 속성 및 메서드 이름 앞에 객체가 붙고 함수 이름 앞에 모듈 이름이 붙기 때문에 일반적으로 불필요한 것으로 간주된다.

 

· 선행 또는 후행 밑줄을 사용하는 다음과 같은 특수형식이 인식된다. 이는 일반적으로 모든 대소문자 규칙과 결합될 수 있다.

- _single_leading_underscore: 내부 사용을 약하게 표현한다. 예를 들어 from M import *는 이름이 밑줄로 시작하는 객체를 가져오지 않는다.

- single_trailing_underscore_: 파이썬 키워드와의 충돌을 피하기 위해 규칙에 따라 사용한다.

예) tkinter.Toplevel(master, class_=’ClassName’)

- __double_leading_underscore: 클래스 속성을 네이밍 할 때, 네임 맹글링을 호출한다. 

예) FooBar 클래스 내부에 __boo는 _FooBar_boo가 된다.

- __double_leading_and_trailing_underscore__: 사용자가 제어하는 네임스페이스에 있는 매직 객체 또는 속성이다.

예) __init__, __import__, __file__

 

관행적인 네이밍 컨벤션

피해야하는 이름

· 소문자 l, 대문자 O와 I를 단일 문자 변수 이름으로 사용하지 말자.

- 일부 글꼴에서 이러한 문자는 숫자 1 및 0과 구별할 수 없다. l을 사용하고 싶을 때는 L을 대신 사용하자.

 

아스키 호환성

· 표준 라이브러리에서 사용되는 식별자는 PEP 3131의 정책 섹션에 설명된 대로 아스키와 호환되어야 한다.

 

패키지와 모듈 이름

· 모듈은 모두 소문자로 된 짧은 이름을 가져야 한다. 가독성을 높이면 모듈 이름에 밑줄을 사용할 수 있다. 밑줄 사용은 권장되지 않지만 파이썬 패키지는 모두 소문자로 된 짧은 이름을 가져야 한다.

· C또는 C++로 작성된 확장 모듈에 더 높은 수준 인터페이스를 제공하는 수발되는 파이썬 모듈이 있는 경우 C/C++ 모듈에는 선행 밑줄(예: _socket)이 있어야한다.

 

클래스 이름

· 클래스 이름은 일반적으로 CapWords 규칙을 사용해야 한다.

· 인터페이스가 문서화되고 주로 호출 가능한 것으로 사용되는 경우 함수에 대한 명명 규칙을 대신 사용할 수 있다.

· 빌트인 이름에는 별도의 규칙이 있다. 대부분의 빌트인 이름은 단일 단어 또는 두 단어이며, CapWords 규칙은 예외 이름 및 기본 제공 상수에만 사용된다.

 

타입 변수 이름

· PEP 484에 도입된 타입 변수의 이름은 일반적으로 T, AnyStr, Num과 같은 짧은 이름을 선호하는 CapWords를 사용해야 한다.

· 공변 또는 반공변 행동을 선언하는 데 사용되는 변수에 접미사 _co 또는 _contra를 추가하는 것이 좋다.

 

from typing import TypeVar
 
VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)

 

예외(Exception) 이름

· 예외는 클래스여야 하므로 클래스 명명 규칙이 여기에 적용된다. 그러나 예외가 사실상 에러라면 예외 이름 접미사에 Error를 붙여야한다.

 

전역 변수 이름

· 전역 변수에 대한 규칙은 함수에 대한 규칙과 거의 같다.

· from M import *를 통해 사용하도록 설계된 모듈은 __all__ 메커니즘을 사용하여 전역을 내보내는 것을 방지하거나, 이러한 전역에 밑줄을 접두사로 사용하는 이전 규칙을 사용해야 한다.

 

함수와 변수 이름 

· 함수 이름은 소문자만 가능하며, 가독성을 향상 시키기 위해 단어는 언더스코어(_)로 나뉘어야한다.

· 변수 이름은 함수 이름과 같은 컨벤션을 따른다.

· mixCase는 이전 버전과의 호환성을 유지하기 위해 이미 널리 사용되는 스타일(예:threading.py)인 컨텍스트에서만 허용된다.

 

함수와 메서드 인수

· 인스턴스 메서드를 위해 첫 번째 인수는 언제나 self를 사용한다.

· 클래스 메서드를 위해 첫 번째 인수는 언제나 self를 사용한다.

· 함수 인수의 이름이 예약어와 충돌하는 경우 일반적으로 약어나 맞춤법 손상을 사용하는 것보다 후행 밑줄을 하나만 추가하는 것이 좋다. ex) class_가 clss보다 낫다.



메서드 이름과 인스턴스 변수

· 함수 네이밍 규칙을 사용한다. (소문자, 단어 언더스코어 구분)

· non-public 메서드 및 인스턴스 변수에 대해서만 선행 밑줄을 하나 사용한다.

· 서브클래스와의 이름 충돌을 피하려면 두 개의 선행 밑줄을 사용하여 파이썬의 name mangling 규칙을 호출한다.

- 파이썬은 이러한 이름을 클래스 이름으로 맹글링한다. Foo 클래스의 __a라는 속성이 있으면 Foo.__a에서 액세스할 수 없다.

- 일반적으로 이중 선행 밑줄은 하위 클래스로 설계된 클래스의 속성과 이름 충돌을 피하기 위해 사용한다.

 

상수

· 상수는 일반적으로 모듈 수준에서 정의되며 단어를 단어를 구분하는 밑줄과 함께 모두 대문자로 작성된다.

ex) MAX_OVERFLOW, TOTAL

 

상속을 위한 설계

· 항상 클래스의 메서드와 인스턴스 변수가 public 또는 non-public인지를 결정해야한다.

· public 속성(attribute)은 클래스의 클라이어트가 사용할 것으로 예상되는 속성이다. 따라서 이전 버전과 호환되지 않은 변경 사항을 방지해야한다.

· Non-public 속성은 제 3자가 사용하도록 의도되지 않은 속성이다. 비공개 속성이 변경되거나 제거되지 않는다는 보장은 없다.

- 여기서 private이라는 용어를 사용하지는 않는다. 파이썬에서는 어떤 속성도 실제로 private이 아니기 때문이다.

 

· 속성의 또 다른 범주는 서브 클래스 API의 일부인 속성이다. (다른 언어의 protected 속성)

· 일부 클래스는 클래스 동작의 측면을 확장하거나 수정하기 위해 상속되도록 설계된다. 이러한 클래스를 디자인 할 때는 어떤 속성이 public인지, 하위 클래스의 API의 일부인지, 기본 클래스에서만 사용해야 하는지 명시적으로 결정해야한다.

 

· 위 사실들에 기반한 파이써닉 가이드라인:

- public 속성은 선행 밑줄이 없어야 한다.

- public 속성 이름이 예약된 키워드와 충돌하는 경우 속성 이름에 단일 후행 밑줄을 추가한다. 이는 약어나 잘못된 맞춤법 보다 선호된다. (단, ‘cls’는 클래스로 알려진 모든 변수나 인수, 특히 클래스 메서드의 첫 번째 인수에 대해 선호되는 철자다.)

- 단순한 public 데이터 속성의 경우 복잡한 접근자(accessor)/변이자(mutator) 메서드 없이 속성 이름만 노출하는 것이 가장 좋다. 

더보기

▶ 단순한 데이터 속성이 기능적 동작을 확장해야 하는 경우 파이썬은 향후 개선을 위한 쉬운 경로를 제공한다는 점을 명시하자. 이 경우 속성을 사용하여 간단한 데이터 속성 액세스 구문 뒤에 기능 구현을 숨긴다.

▶ 계산 비용이 많이 드는 작업에는 속성을 사용하지 말자. 속성 표기법은 액세스가 저렴하다고 믿게 만든다.

- 클래스를 하위 클래스로 만들려는 경우 하위 클래스에서 사용하지 않는 속성이 있는 경우 두 개의 선행 밑줄로 이름을 지정하고 후행 밑줄은 사용하지 않는 게 좋다.

더보기

▶ 이것은 클래스 이름이 속성 이름으로 변경되는 파이썬의 이름 맹글링 알고리즘을 호출한다. 이렇게 하면 서브클래스에 같은 이름의 속성이 실수로 포함되는 경우 속성 이름 충돌을 방지하는 데 도움이 된다.

 

▶ 맹글링된 이름에는 단순 클래스 이름만 사용되므로 하위 클래스가 동일한 클래스 이름과 속성 이름을 모두 선택하면 여전히 이름 충돌이 발생할 수 있다.

 

▶ 이름 맹글링은 디버깅 및 __getattr_()과 같은 특정 용도를 덜 편리하게 만들 수 있다. 그러나 이름 맹글링 알고리즘은 문서화되어 있으며 수동으로 수행하기 쉽다.

 

▶ 모든 사람이 이름 맹글링을 좋아하지는 않다. 실수로 이름이 충돌하지 않도록 해야 하는 필요성과 발신자가 사용할 수 있는 가능성 사이에서 균형을 유지해야한다.

 

Public & Internal Interface

· 이전 버전과의 호환성 보장은 공용 인터페이스에만 적용된다. 따라서 사용자는 public 인터페이스와 내부 인터페이스를 명확하게 구분할 수 있어야 한다.

 

· 문서에서 일반적인 하위 호환성 보장에서 제외되는 임시 또는 내부 인터페이스로 명시적으로 선언하지 않는 한 문서화된 인터페이스는 public으로 간주된다. 문서화되지 않은 모든 인터페이스는 내부 인터페이스로 간주되어야 한다.

 

 

· 자기성찰(introspection)을 더 잘 지원하기 위해 모듈은 __all__ 속성을 사용하여 public API에서 이름을 명시적으로 선언해야 한다.

- __all__을 빈 몬록으로 설정하면 모듈에 공개 API가 없음을 나타낸다.

 

- __all__이 적절하게 설정되어 있더라도 내부 인터페이스(패키지, 모듈, 클래스, 함수, 속성 또는 기타 이름)에는 여전히 하나의 선행 밑줄 접두사가 있어야 한다.

 

 

· 포함하는 네임스페이스(패키지, 모듈 또는 클래스)가 내부로 간주되는 경우 인터페이스도 내부로 간주된다.

 

- 가져온 이름은 항상 구현 세부 사항으로 간주되어야 한다. 다른 모듈은 os.path 또는 하위 모듈의 기능을 노출하는 패키지의 __init__ 모듈과 같이 포함하는 모듈 API의 명시적으로 문서화된 부분이 아닌한 가져온 이름에 대한 간접 액세스에 의존해서는 안 된다.

 

 

프로그래밍 추천사항

 

·  코드는 파이썬의 다른 구현(PyPy, Jython, IronPython, Cython, Psyco 등)에 불이익을 주지 않는 방식으로 작성되어야 한다.

- 예를 들어 a += b 또는 a = a + b 같은 형식의 명령문에 대한 CPython의 효율적인 문자열 연결 구현에 의존하면 안된다. 이 최적화는 CPython에서도 취약하며(일부 타입에서만 작동) 참조 횟수 계산 방식(Reference counting)을 사용하지 않는 구현에서는 전혀 존재하지 않는다.

- 라이브러리 성능에 민감한 부분에서는 ''.join() 형식을 대신 사용해야 한다. 이렇게 하면 다양한 구현에서 선형 시간에 연결이 발생한다.

 

 

· None과 같은 싱글톤과의 비교는 항상 is 또는 is not으로 수행해야하며, equality 연산자를 사용하면 안된다.  

- 또한 x가 None이 아닌 경우를 의미할 때 if x를 작성하는 것에 주의하자. ex) 기본값이 None인 변수나 인수가 다른 값으로 설정되었는지 테스트할 때 주의하자. 다른 값은 boolean context에서 거짓일 수 있는 타입(예: 컨테이너)을 가질 수 있다.

 

· is not 연사자 사용 O, not ... is 연산자 사용 X. 두 식은 기능적으로 동일하지만, 전자가 더 읽기 쉽고 선호된다.

# 옳음:
if foo is not None:

# 틀림:
if not foo is None:

 

· 여러 개의 비교로 순서 지정 작업을 구현할 때는 특정 비교만 수행하기 위해 다른 코드에 의존하는 것보다 6가지 작업(__eq__, __ne__, __lt__, __le__, __gt__, __ge__)을 모두 구현하는 것이 가장 좋다.

- 관련된 노력을 최소화하기 위해 functools.total_ordering() 데코레이터는 누락된 비교 메서드를 생성하는 도구를 제공한다.

- PEP207은 파이썬에 반사성 규칙이 가정됨을 나타낸다. 따라서 인터프리터는 y > x 를 x < y로, x == y를 y == x로 바꿀 수 있다.

- sort() 및 min() 연산은 < 연산자를 사용하도록 보장되고, max() 함수는 > 연산자를 사용한다. 그러나 다른 컨텍스트에서 혼동이 발생하지 않도록 6가지 작업을 모두 구현하는 것이 가장 좋다. 

 

 

· 람다식을 식별자에 직접 바인딩하는 할당문 대신 항상 def문을 사용하자.

# 옳음:
def f(x): return 2*x

# 틀림:
f = lambda x: 2*x

 

첫 번째 형식은 결과 함수 객체의 이름이 일반 <lambda> 대신 구체적으로 'f'임을 의미한다.

- 이는 일반적으로 traceback 및 문자열 표현에 더 유용하다. 

- 대입문을 사용하면 정시적 def 문에 비해 람다식이 제공할 수 있는 유일한 이점(더 큰 표현식 안에 포함될 수 있다.)이 제거된다.

 

 

· BaseException이 아닌 Exception에서 예외를 파생시키자.

- BaseException 직접 상속은 예외를 잡는 것은 거의 항상 잘못된 일이므로 예약되어있다.

- 예외가 발생하는 위치가 아니라 예외를 포착하는 코드에 필요할 가능성이 있는 구분을 기반으로 예외 계층을 설계한다. 즉, "무엇이 잘못 되었습니까?"라는 질문에 답하는 것을 목표로하자.

- 클래스 명명 규칙이 적용된다. 예외가 오류인 경우 예외 클래스에 접미사 "Error"를 추가한다.

- non-local 흐름 제어 또는 다른 형태의 신호에 사용되는 오류가 아닌 예외에는 특별한 접미사가 필요하지 않다.

 

· 예외 체이닝을 적적하게 사용하자. raise X from Y는 원래의 traceback을 잃지 않고 명시적 대체를 나타내는 데 사용해야 한다.

- 의도적으로 내부 예외를 대체할 때(raise X from None 사용), 관련 세부 정보가 새 예외로 전송되었는지 확인하자. ex) KeyError를 AttributeError로 변환할 때 속성 이름을 유지하거나 새 예외 메시지에 원래 예외의 텍스트 포함

 

 

· 예외를 잡을 때 bare except: 절을 사용하는 대신에 구체적인 예외를 언급하자. 

 

try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

 

bare except: 절은 SynstemExit 및 KeyboardInterrupt 예외를 포착하여 Control-C로 프로그램을 중단하기 어렵게 만들고, 다른 무제를 위장할 수 있다. 프로그램 오류를 알리는 모든 예외를 포착하려면 Exception: 예외를 사용하자. (bare except는 except은 BaseException:과 동일하다)

 

경험에 따르면 'except'절의 사용을 두 가지 경우로 제한하는 것이 좋다.

1. 예외 핸들러가 traceback을 출력하거나 기록할 경우 적어도 사용자는 오류가 발생했음을 알 수 있다.

2. 코드가 일부 정리 작업을 수행해야 하지만 예외가 상승을 통해 위쪽으로 전파되도록 허용하는 경우 try...finally이 경우를 처리하는 더 좋은 방법이 될 수 있다.

 

· 운영체제 오류를 포착할 때 errno 값의 내부 검사보다 파이썬 3.3에 도입된 명시적 예외 계층이 선호된다.

 

· 모든 try/except 절에 대해 try 절을 필요한 최소 코드양으로 제한해야한다. 이것은 마스킹 버그를 방지한다.

 

# 옳음:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

# 틀림:
try:
    # Too broad!
    return handle_value(collection[key])
except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)

 

· 리소스가 코드의 특정 섹션에 로컬인 경우 with 문을 사용하여 사용 후 신속하고 안정적으로 정리되도록 한다. try/finally 문도 허용된다.

·  컨텍스트 관리자는 리소스 획득 및 이외의 작업을 수행할 때마다 별도의 함수 또는 메서드를 통해 호출해야한다.

# 옳음:
with conn.begin_transaction():
    do_stuff_in_transaction(conn)
# 틀림:
with conn:
    do_stuff_in_transaction(conn)

 

후자는 __enter__및 __exit__ 메서드가 트랜잭션 후 연결을 닫는 것 이외에 다른 작업을 수행하고 있음을 나타내는 정보를 제공하지 않는다. 이 경우 명시적인 것이 중요하다.

 

 

·  반환문에서 일관성을 유지하자. 함수의 모든 return문은 표현식을 반환하거나 아무 것도 반환하지 않아야 한다.

- return 문이 표현식을 반환하는 경우 값이 반환되지 않는 모든 return 문은 이를 명시적으로 return None으로 명시해야 하며 명시적 return 문이 함수 끝에 있어야 한다.

# 옳음:
def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None
def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)
    
# 틀림:
def foo(x):
    if x >= 0:
        return math.sqrt(x)
def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

 

· 문자열 슬라이싱 대신 ''.startswith() 및 ''.endswith()를 사용하여 접두사 또는 접미사를 확인하자. startswith() 및 endwith()는 더 깔끔하고, 오류가 덜 발생한다.

# 옳음:
if foo.startswith('bar'):
# 틀림:
if foo[:3] == 'bar':

 

·  객체 타입 비교는 타입을 직접 비교하는 개신 항상 isinstance()를 사용해야 한다.

# 옳음:
if isinstance(obj, int):
# 틀림:
if type(obj) is type(1):

 

·  시퀸스(sttrings ,lists, tuples)의 경우 빈 시퀸스가 false라는 사실을 사용하자.

# 옳음:
if not seq:
if seq:

# 틀림:
if len(seq):
if not len(seq):

 

·  중요한 후행 공백(trailing whitespace)에 의존하는 문자열 리터럴을 작성하지 말자. 이러한 후행 공백은 시각적으로 구별할 수 없으며 , 일부 에디터(또는 최근에는 reindent.py)가 이를 자른다.

 

· ==를 사용하여 boolean 값을 True 또는 False와 비교하지 말자.

 

# 옳음:
if greeting:

# 틀림:
if greeting == True:

 

다음과 같은 표현은 더 나쁘다.

 

# 틀림:
if greeting is True:

 

· try...finally의 finally 내부에서 흐름 제어 명령문 return/break/continue를 사용하는 것은 흐름 제어 문이 finally 외부로 점프하는 것이므로 권장되지 않는다. 이러한 명령문이 finally 제품군을 통해 전파되는 활성 예외를 암시적으로 취소하기 때문이다.

 

# 틀림:
def foo():
    try:
        1 / 0
    finally:
        return 42

 

Function Annotations

· PEP 484의 승인으로 함수 애너테이션에 대한 스타일 규칙이 변경되었다.

- 함수 애너테이션은 PEP 484 구문을 사용해야 한다.

- PEP 8에서 이전에 권장되었던 애너테이션 스타일에 대한 실험(experimentation)은 더 이상 권장되지 않는다. 그러나 stdlib 외부에서는 PEP 484 규칙 내에서 실험이 권장된다.

ex) 거대한 써드파티 라이브러리 또는 애플리케이션을 PEP 484 스타일 유형 애너테이션으로 마크업하고, 해당 애너테이션을 추가하는 것이 얼마나 쉬운지 검토하고 해당 주석의 존재가 코드 이해도를 높이는지 관찰한다. 

- 파이썬 표준 라이브러리는 이러한 애너테이션을 채택하는 데 있어 보수적이어야 하지만, 새 코드와 대규모 리팩토링에 사용할 수 있다.

- 함수 애너테이션을 다르게 사용하려는 경우 다음 형식의 애너테이션을 넣는 것이 좋다.

 

# type: ignore

 

이것은 타입 검사기가 모든 주석을 무시하도록 지시한다. 

 

· 린터와 마찬가지로 타입 검사기는 선택 사항이며 별도의 도구다. 기본적으로 파이썬 인터프리터는 타입 검사로 인해 메시지를 발행해서는 안 되며 주석에 따라 동작을 변경해서는 안 된다.

 

· 타입 검사기를 사용하고 싶지 않은 사용자는 무시해도 된다. 그러나 써드파티 라이브러리 패키지의 사용자는 해당 패키지에 대해 타입 검사기를 실행할 수 있다. 이를 위해 PEP 484는 스텁 파일(타입 검사기가 해당.py 보다 우선하여 읽는 .pyi파일)의 사용을 권장한다.

- 스텁 파일은 라이브러리와 함께 배포되거나 typeshed repo를 통해 별도로 배포될 수 있다.

 

Variable Annotations

· PEP 526 은 변수 애너테이션을 소개한다. 이들에 대한 스타일 권장 사항은 위에서 설명한 함수 애너테이션의 권장 사항과 유사하다.

- 모듈 수준 변수, 클래스 및 인스턴스 변수, 지역 변수에 대한 주석은 콜론 뒤에 단일 공백이 있어야 한다.

- 콜론 앞에 공백이 없어야 한다.

-  할당문에 오른쪽이 있는 경우 = 등호는 양쪽에 하나의 공백이 있어야 한다.

# 옳음:
code: int

class Point:
    coords: Tuple[int, int]
    label: str = '<unknown>'

# 틀림:
code:int  # No space after colon
code : int  # Space before colon

class Test:
    result: int=0  # No spaces around equality sign

 

-  PEP 526이 파이썬 3.6에서 허용되지만 모든 버전의 파이썬에서 스텁 파일에 대한 기본 구문은 변수 주석 구문이다.

 

 

반응형

댓글