IT/언어

[python] python 코딩 스타일 가이드

개발자 두더지 2020. 11. 12. 23:47
728x90

코드 레이아웃


● 들여쓰기

1) 들여쓰기는 스페이스 4개를 사용하자.

2) 행을 반복해서 작성할 때는 요소의 세로를 맞추자.

3) if문의 괄호문({}) 안의 코드 부분과 복수의 조건 부분의 구분에 대해 구체적인 언급은 없지만 몇 가지 방법이 있다.

# 방법1
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 방법2
if (this_is_one_thing
        and that_is_another_thing):
    do_something()

4) 한 문장을 여러 행에 나눠 작성하고 마지막에 괄호, 브라켓을 닫을 때는 '리스트의 마지막 요소가 위치한 행의 첫 번째 화이트 스페이스가 아닌 문자 바로 아래'에 닫는 기호를 작성한다.

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]

 그러나 아래와 같이 닫는 기호를 '계속되는 행의 첫 부분의 문자'부분에 작성해도 문제가 없다.

my_list = [
    1, 2, 3,
    4, 5, 6,
]

 

● 한 행의 길이

모든 행의 최대 길이는 최대 79문자이하로 제약하는 것이 좋다.

 

● 2항 연산자의 앞에서 개행할 것인가, 뒤에서 개행할 것 인가?

Knuth의 스타일을 추전한다.

# 연산자와 피연산자를 일치시키기 쉽다.
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

 

●빈 행

1) 탑레벨의 함수 또는 클래스

2행씩 간격을 두고 정의하자.

2) 클래스 내부

1행씩 간격을 두고 메소드를 정의하자.

3) 함수 내부

로직의 경계를 표시하기 위해 빈 행을 소극적으로 사용한다.

 

● 소스파일의 엔코딩

보통 UTF-8을 사용한다. ( Python 2 버전의 경우 ASCII)

 

● import 

import문은 다음과 같은 순서로 그룹화한다.

1) 표준 라이브러리

2) third party에 관련 것 

3) 로컬 어플리케이션/ 라이브러리 등 특유의 것

위의 그룹간에 1개의 빈행을 넣어 구분한다.

 

● 모듈 레벨의 이중 언더스코어 변수명

모듈 레벨의 "이중 언더스코어 변수"(변수명의 앞뒤에 언더스코어가 2개씩 붙어 있는 변수)는 모듈과 관련된 docstring 뒤에, "from__futuer__"이외의 모든 것은 import문의 앞에 작성한다.

from __future__ import barry_as_FLUFL

__all__ = ['a', 'b', 'c']
__version__ = '0.1'
__author__ = 'Cardinal Biggles'

import os
import sys

 

 

 

문자열에 포함된 인용부


홑 따음표(')를 쓸 것인지 겹 따옴표(")를 쓸 것 인지 규칙을 정해 지키도록 하자.

그러나 3중인용부로 문자열을 감쌀 경우는 항상 겹 따옴표(""")를 사용한다.

 

 

식이나 문(文)의 공백 문자


● 자주 발생하는 대립점

(1) 괄호나 브라켓의 시작 직전이나 끝 직후

spam(ham[1], {eggs: 2})

(2) 콤마 뒤에 바로 닫는 괄호 기호

foo = (0,)

(3) 콤마나 세미클론, 클론의 직전

if x == 4: print x, y; x, y = y, x

(4) 슬라이스에서는 클론이 2중연산자와 같은 역할을 한다. 따라서 양쪽에 같은 수의 스페이스를 사용한다.

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]

(5) 함수 호출의 인수 리스트를 시작하는 여는 괄호 직전

spam(1)

(6) 인덱스나 슬라이즈의 여는 괄호의 직전

dct['key'] = lst[index]

(7) 대입(이나 다른) 연산자를 가지런히 하기 위해 연산자의 주의에 1개 이상의 스페이스를 넣는다.

x = 1
y = 2
long_variable = 3

 

● 다른 추천 항목

(1) 행 끝에 공백문자를 남기지 않도록 하자.

(2) 다음의 이항 연산자는 양쪽에 항상 1개만 스페이스를 넣는다.

대입 연산자, 괄호대입 연산자, 비교 연산자, 불(Bool) 연산자 (and, or, not)

(3) 우선순위가 다른 연산자를 다를 경우 우선 순위가 가장 낮은 연잔사의 양쪽에 스페이스를 넣는다.

i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

(4) 함수 어노테이션은 클론에 관한 보통의 규칙을 지킨다.

def munge(input: AnyStr): ...
def munge() -> PosInt: ...

(5) 어노테이션되어 있지 않은 함수의 인수에 있어 키워드 인수나 기본 파라미터를 표시하는 "=" 양쪽에 스페이스를 넣지 않는다.

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): ...

(6) 복합문(1행에 복수의 문장을 넣는 것)은 일반적으로 추천하지 않는다.

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

(7) 복합문(if/for/while)을 두는 것은 금지한다.

아래와 같이 작성하지 않도록 주의하자.

# 틀린예:
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',)

 끝의 콤마는 확장의 의미로 사용되는 경우도 있다(예를 들어, 값이나 인수, 혹은 import된 값의 리스트를 반복하여 전개될 것이 기대되는 경우나 버전 관리 시스템을 사용하고 있는 경우).

FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

 

 

코멘트(주석)


1. 코드를 변경한 경우 코멘트를 최신 상태를 반영하자.

2. 코멘트는 복수의 완전한 문장으로 쓴다.

3. 블록 코멘트는 일반적으로 하나 혹은 그 이상의 단락이 되며, 단락은 복수의 완전한 문장으로 작성한다. 각 문장은 마침표로 끝마친다.

4. 코멘트가 2개 이상의 문장이 된 경우, 문장의 마침표 뒤는 2개의 스페이스를 넣는다. 그러나, 마지막 문장에는 넣지 않는다.

5. 다른 사람이 보아도 명쾌하게 알기 쉬운 코멘트를 작성한다.

 

● 블록 코멘트

블록 코멘트는 일반적으로 그 뒤에 계속되는 몇 개의(혹은 모든) 코드에 적용돼, 그 코드와 같은 레벨에 들여쓰기 된다. 블록 코멘트의 각행은 "#"과 띄어쓰기 1개로 시작된다.

 

● 인라인 코멘트 (; 코드와 같은 행에 쓰는 코멘트)

(1) 인라인 코멘트는 많이 사용되지 않는다.

(2) 코드와 인라인 코멘트의 사이에는 적어도 2개의 띄어쓰기를 둔다.

(3) 인라인 코멘트는 "#"과 띄어쓰기 1개로 시작한다. 

 

● docstrings

좋은 docstrings쓰기 위한 규약은 PEP 257에 정리되어 있다.

(1) 공개된 모든 모듈이나 함수, 클래스, 메소드의 docstrings를 쓴다. docstrings는 공개되지 않는 메소드에는 필요하지 않지만, 그 메소드가 어떤 동작을 하고 있는지 설명하는 코멘트는 'def'의 행 앞에 작성한다.

(2) 복수행의 docstring은 """으로 행을 닫는다.

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.
"""

 

 

 

명령규약


 새로운 모듈과 패키지 (third party의 프레임워크를 포함) 는 규약에 따라 작성한다. 그러나 기존의 라이브러리와 다른 스타일을 채용하고 있는 경우는 내용을 일치시키길 바란다.

 

● 가장 중요한 원리

 공개되어 있는 API의 일부로써 유저에게 보여지는 이름은 구현보다도 사용법을 반영한 이름으로 한다.

 

● 일반적으로 사용되고 있는 명명방법

 Python의 속성이나 메소드의 이름의 앞에는 클래스명을 붙이고 함수명의 앞에는 모듈명을 붙인다. 추가로, 다음과 같이 언더스코어를 이름 앞뒤에 붙이는 특별한 사용법이 알려져 있다.

1) '_single_leading_underscore'

내부에서만 사용한다는 것을 표시한다. 예를 들어 'from M import *'는 언더스코어로 시작하는 이름의 객체를 'import'하지 않는다.

2) 'single_trailing_underscore_'

PYthon의 키워드와 충돌하는 것을 방지하기 위해 사용되는 규약.

3) '__double_leading_underscore'

클래스의 속성에 이름을 붙일 때에 이름의 맹글링 기구를 호출한다.

4) '__double_leading_and_trailing_underscore__'

유저가 제어하는 이름 공간에 존재하는 "매직" 객체 혹은 "매직" 속성.

 

● 지켜야할 명명규약

1) 이러한 이름은 적지말자.

단일 문자 'l' (소문자 엘), 'O' (대문자 오), 'I' (대문자 아이)는 변수에 작성하지 말자. (폰트에 따라 이러한 문자는 숫자 1또는 0과 구분하기 힘들다)

2) ASCII와의 호환성

표준 라이브러리로 사용되는 식별자는 ASCII와 호환성을 고려할 필요가 있다.

3) 패키지와 모듈의 이름

모듈의 이름은 모두 소문자의 짧은 단어로 한다.

4) 클래스의 이름

클래스의 이름은 보통 CapWords방식을 사용한다.

5) 변수명의 이름

(1) 변수명의 이름에는 보통 CapWords방식을 사용한다.

(2) 'T'나 'AnyStr'이나 'Num'과 같이 짧은 단어

(3) 공변(共変)이나 반변(反変)처럼 행동하는 변수를 선언할 때는  '_co'나 '_contra'와 같은 이름을 변수의 끝에 붙여주는 것을 추천한다.

6) 예외의 명명

예외는 클래스 내에 있으므로 클래스의 명명규약이 적용된다. 그러나 예외의 이름 끝에는 'Error'를 붙인다.

7) 글로벌 변수의 이름

'from M import *'방식으로 import되도록 설계되어 있는 모듈은 글로벌 변수를 엑스포트하는 것을 방지하기 위해 __all__의 방식을 사용하던가, 엑스포트하고 싶지 않은 글로벌 변수의 앞에 언더스코어를 붙이는 예전 규약을 사용하던가 하면 된다.

8) 함수와 메소드에 전달하는 인수

(1) 인스턴스 메소드의 인수 이름의 처음에는 항상 'self'를 붙인다.  

(2) 클래스 인스턴스의 인수 이름의 처음에는 항상 'cls'를 사용한다.

9) 메소드명과 인스턴스 변수

(1) 함수의 명명규약 (소문자만을) 을 사용한다.

(2) 공개되어 있지 않은 메소드나 인스턴스 변수에만 언더스코어를 맨 앞에 붙인다.

(3) 서브클래스와 이름이 충돌한 경우는 Python의 맹글링 기구를 호출하기 위해 언더 스코어를 맨 앞에 2개 붙인다.

 ※ 일반적으로 언더스코어를 이름의 맨 앞에 2개 붙이는 방식은 서브 클래스화되도록 설계된 클래스의 속성이 충돌될 가능성을 피하기 위해 사용한다.

10) 정수

정수는 보통 모듈 레벨에 정의( ; 모든 정수는 대문자로 쓰고, 단어는 언더스코어로 구분한다)

11) 계승의 설계

(1) 공개 (public) 속성의 맨 앞에는 언더스코어를 붙이지 않는다.

(2) 공개 속성의 이름이 예약어와 충돌하는 경우는 속성의 이름 뒤에 언더스코어를 추가한다.

(3) 공개된 속성을 심플하게 하기 위해서는 복잡한 액세서(; 객체지향 프로그래밍에서 객체 내부의 멤버 변수인 속성이나 프로퍼티에 대해 외부에서 접근할 수 있도록 하기 위해 사용하는 메소드) 와 뮤테이터 (Mutator; 내부 상태를 변경하기 위핸 메소드) 를 공개하지 않고, 속성의 이름만 공개하는 것이 베스트이다.

(4) 서브 클래스화하여 사용하는 클래스가 있도록 한다. 서브 클래스로 사용되길 바라지 않는 속성이 있는 경우 그 이름의 맨 앞에 언더스코어를 2개 붙이는 것을 검토해보도록 하라.

12) 공개 인터페이스와 내부 인터페이스 

하휘 호환성은 공개되어 있는 인터 페이스에만 보증된다. 따라서 공개 인터페이스와 내부 인터페이스를 유저가 명확히 구별할 수 있도록 하는 것이 중요하다.

(1) 문서화되어 있지 않은 인터페이스는 모두 내부적인 것으로 간주한다.

(2) 인토로스펙션이 제대로 기능하도록 하기 위해, 모듈은 공개하고 있는 API는 '__all__' 속성을 사용하여 선언한다.

(3) '__all__'속성을 적절히 설정하여도 내부 인터페이스 (패키지, 모듈, 클래스, 함수, 그 외 이름)은 이름의 앞에 언더스코어를 1개 붙인다.

(4) 어떤 인터 페이스가 포함되어 있는 이름 공간 (패키지, 모듈, 클래스)가 내부적인 것으로 간주되는 경우는 그 인터페이스도 내부 인터페이스로 간주된다.

(5) import된 이름은 항상 구현에 대한 상세 내용을 표현하고 있다고 간주한다.

 

 

프로그래밍과 관련된 추천항목


1) 다른 Python구현(PyPy, Jython, IronPython, Cython, Psyco 등)에 불리하지 않는 코드를 쓴다.

2) None과 비교할 때는 항상 is 나 is not을 쓴다.

3) not....is.....가 아닌, is not 연산자를 쓴다.

# 올바른 작성법:
if foo is not None:

# 틀린 작성법:
if not foo is None:

4) 확장 비교(rich comparion)을 사용해 정렬을 구현할 경우 모든 연산(__eq__, __ne__, __lt__, __le__, __gt__, __ge__)을 구현하는 것이 베스트이다.

5) 람다식을 직접 식별자에 결부시키는 대입문을 작성하는 것이 아닌, 항상 def 문을 사용한다.

# 올바른 작성법:
def f(x): return 2*x

# 틀린 작성법:
f = lambda x: 2*x

6) BaseException이 아닌, Exception으로부터 예외가 파생되도록 한다.

7) 예외 체인은 적절히 사용한다.

8) 예외를 catch할 때는 가능할 때 언제라도 예외를 지정하지 않은 그대로의 except:가 아닌 특정의 예외를 지정하도록 한다.

그대로의 'except'예외를 사용할 때는 단 두 가지의 경우로 한정한다.

① 예외 핸들러가 'trackback'을 출력하거나 로깅하는 경우

② 리소스의 뒤처리가 필요한 경우, 뒤처리를 한 후에 'raise'를 사용하여 상류에 예외를 전송하도록 하게 할 경우.

9) 오퍼레이팅 시스템의 에러를 catch할때는 Python 3.3이후에서는 'errno'의 값을 조사하는 것이 아닌 새로운 오퍼레이딩 시스템 관련의 에러 1개의 층을 명시적으로 사용한다.

10) 모든 'try/except'에 대해, try에 포함되는 범위를 필요한 최소한의 코드만을 작성하자.

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

11) 리소스가 코드의 특정부분에만 사용되는 경우, 사용 후 바로 의뢰할 수 있는 방법으로 뒤처리할 수 있도록 'with'문을 사용한다.

12) 컨텍스트 매니저는 리소스의 취득이나 해방이외의 것을 할 때는 항상 다른 함수나 메소드를 통해서 호출한다.

# 올바른 작성법:
with conn.begin_transaction():
    do_stuff_in_transaction(conn)

# 틀린 작성법:
with conn:
    do_stuff_in_transaction(conn)

13) return문을 일관되게 작성한다. 어떠한 값도 돌려주지 않는 return문은 명시적으로 'return None'을 작성한다.

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)

14) string모듈보다도 문자열 메소드를 사용한다.

15) 문자열에 특정 접두사나 접미사가 붙어 있는가를 체크할 때는 ''.startswith() 와 ''.endswith()를 사용한다.

16) 오브젝트의 형비교는 항상 isinstance() 를 사용한다.

17) 시퀀스(문자열, 리스트, 튜플)에 대해서는 빈 시퀀스가 False인 것을 이용한다.

18) 행 끝에 공백 문자에 의존한 문자열 리터럴을 쓰지 않는다.

19) Bool형의 값과 True 나 False를 비교할 때 == 를 사용하지 않는다.

# 올바른 작성법:
if greeting:

# 틀린 작성법(1):
if greeting == True:

# 틀린 작성법(2):
if greeting is True:

20) try...finally 의 결합중에 'finally'밖으로 탈출하는 제어구문 'return/break/continue'을 사용하는 것은 추천한다.

 

● 함수 어노테이션

1) linter이나 형 체크 프로그램과 같은 툴은 Python 인터프리터와는 다른 툴이며, 사용할지 말지는 임의이다.

2) 형 체크를 원하지 않는 사람은 무시해도 된다.

3) 하위 호환성을 유지할 필요가 있는 코드에대해 형 어노테이션을 코멘트의 형식으로 작성해도 된다.

 

● 변수 어노테이션

1) 모듈 레벨의 변수나 클래스 변수, 인스턴스 변수, 그리고 로컬변수에 대한 어노테이션은 클론(:)뒤에 띄어쓰기 1개를 넣는다.

2) 클론의 앞에는 띄어쓰기하지 말자.

3) 변수의 오른쪽에 대입을 할 때는 등호의 앞뒤로 띄어쓰기 1개만을 넣는다.

# 올바른 작성법:

code: int

class Point:
    coords: Tuple[int, int]
    label: str = '<unknown>'
    
# 틀린 작성법:

code:int  # 클론 뒤에 띄어쓰기되어 있지 않다.
code : int  # 클론 앞에 띄어쓰기가 되어 있다.

class Test:
    result: int=0  # 등호의 앞뒤가 띄어쓰기가 되어 있지 않다.

 


참고자료

https://pep8-ja.readthedocs.io/ja/latest/#id19

728x90