16 minute read

파이썬 코딩 스타일 문서

파이썬 코딩 스타일 가이드

Code Lay-out

들여쓰기 마다 네번의 스페이스를 사용해라. (한번의 탭과 같은 결과이므로 나는 한번의 탭을 사용하겠다. 문서에서는 스페이스바를 선호한단다.)
파이썬에서는 들여쓰기에서 탭과, 스페이스바를 다르게 인식하므로 둘중에 하나만 들여쓰기에 사용하자.

Indentation

# Correct:

# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# Add 4 spaces (an extra level of indentation) to distinguish arguments from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indents should add a level.
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

# Wrong:

# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Further indentation required as indentation is not distinguishable.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

Correct 부분과 Wrong 부분을 비교하면 훨씬 argments를 잘 인지할 수 있고, 가독성이 높은 것을 한눈에 확인 할 수 있다.

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

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

둘 중에 하나를 선택해도 상관이 없으므로 앞의 것이 더 가독성이 좋아보이니 앞에 것을 사용하자.

Maximum Line Length

파이썬 코드는 한줄에 79개의 문자 길이로 제한되어 있다.
만약 긴 문자열이 필요한 경우 (데이터베이스 연결, API를 key를 사용하게 되었을때는) 어떻게 처리하는 것이 좋을까?

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

\을 이용하여 문자열이 최대한 깔끔하게 보이게 작성하자.

Should a Line Break Before or After a Binary Operator?

연산자 이전과 후에 라인을 작성한 코드를 비교해보자

# Wrong:
# operators sit far away from their operands
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

위의 것은 뭔가 읽기에 불편하고 계산하는데 머리가 복잡해질 것 같다.

# Correct:
# easy to match operators with operands
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

반면에 아래 것은 어떤 변수가 어떻게 연산이 될지 예측하기 좀더 쉽다. (이전에는 이런 코드는 아무 생각없이 작성하였다면 이제는 아래의 방법으로 작성하도록 하자)

Blank lines

최상위 함수와 클래스 정의는 두줄을 띄우고 작성하자.

클래스 내의 메서드 정의는 한줄을 띄우고 작성하자.

빈줄을 사용해 가독성이 좋게 최대한 작성하려고 노력하자.

Source File Encoding

파이썬 배포와 코딩 작성할 때에는 항상 UTF-8로 작성하도록 하자 (맥과 윈도우의 충돌이 발생할 수 도 있고 다양한 이유,,)

Imports

# Correct:
import os
import sys

# Wrong:
import os, sys

import 를 할때에는 각각 다른 라인에 하자.

또 다른 주의할 점은 항상 파일의 최상단에 하되, 모듈의 주석,docString 바로 뒤에 작성하자. 그리고 다음 3가지 순서로 그룹화가 되도록 하자.

  • 표준 라이브러리 imports
  • 관련된 third party imports
  • local 라이브러리나 어플리케이션 Imports

각 그룹들은 순서대로 작성하되 그룹별로 한줄 띄어쓰기를 하여 깔끔하게 나타내자.

# Better
import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

# ㅠㅠ
from . import sibling
from .sibling import example

위의 방법(절대적 path 명시)으로 최대한 작성하려고 하되 어쩔수 없는 상황이오면 아래의 상대적 참조를 이용하자.

Module Level Dunder Names

__all__, __author__, __version__ 와 같은 dunders의 경우 docString 뒤에 imports 앞에 작성하도록 하자.

"""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

String Quotes

', "을 둘다 string을 작성하는데 똑같은 결과를 나타낸다, 하지만 가독성을 위해 하나만 사용하도록 하자.

DocString Conventions

Whitespace in Expressions and Statements

Pet Peeves

불필요한 띄어 쓰기는 사용하지말자.

# 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 : : upper]
ham[ : upper]

# Correct:
spam(1)

# Wrong:
spam (1)

# Correct:
dct['key'] = lst[index]

# Wrong:
dct ['key'] = lst [index]

comma, semicolon, or colon 전에는 띄어쓰기 사용하지말자.

# Correct:
x = 1
y = 2
long_variable = 3

# Wrong:
x             = 1
y             = 2
long_variable = 3

맞추겠다고 굳이 띄어쓰기를 낭비하지 말자.

Other Recommendations

  • 문장이 끝날때 공백을 사용하는 것을 피하자.(보이지않기 때문 혼동이 온다, \다음에 공백과 개행 문자가 오는 것은 연속 줄 표시로 간주되지않기때문)
  • 할당(=), 증가 할당(+=, -= 등), 비교(==, <, >, !=, <>, <=, >=)와 같이 이항 연산자를 항상 양쪽에 단일 공백으로 묶자, in, not in, is, is not), Booleans(and, or, not).
  • 우선 순위가 다른 연산자들을 섞어 사용하는 경우 우선 순위가 가장 낮은 연산자 앞뒤로 공백을 추가하는 것이 좋다.(선택적이다.)
# Correct:
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

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

function annotations에서 colons 에 대해 위의 규칙들을 사용하고, ‘->’ 주위에는 띄어쓰기를 잊지말자.

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

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

함수의 keyword argument에 사용될 때, default value값을 지정할 때에는 ‘=’ 할당자에 띄어쓰기를 하지 않는다.

# Correct:
def complex(real, imag=0.0):
    return magic(r=real, i=imag)

# Wrong:
def complex(real, imag = 0.0):
    return magic(r = real, i = imag)    

그러나 주의해야 할 것은 default value를 가진 argument annotation을 혼합하였을 떄는 ‘=’ 앞뒤로 띄어쓰기 한다.

# Correct:
def munge(sep: AnyStr = None): ...
def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ...

# Wrong:
def munge(input: AnyStr=None): ...
def munge(input: AnyStr, limit = 1000): ...

같은 줄에 여러 상태를 표시 하는것은 꺼리자.

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

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

When to Use Trailing Commas

튜플에서 의무화되어 있는것을 제외하고는, 보통 끝에 콤마를 붙이는 것은 선택 사항이지만. 명확성을 위해 괄호로 묶어주어 콤마를 표시해라.

# 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,)

Comments

  • 코드와 모순되는 주석은 없는 것보다 나쁘며, 코드가 바뀔때 주석을 최신으로 업데이트 하는 것을 습관처럼하자.
  • 주석은 완전한 문장이여야 하고, 첫글자는 대문자여야한다.(소문자로 시작하는 식별자가 아닐경우)
  • 블록 주석은 완전만 하나또는 하나이상의 문장으로 구성되고 마침표로 끝난다.
  • 마지막 문장을 제외하고는 문장의 끝에 마침표 뒤에 두칸을 띄운다.
  • readability 를 생각하며 작성하자.
  • 영어로 작성하자(100프로 한국인만 보지않는다면)

Inline Comments

사실상 Inline Comments는 불필요 하지만 어쩔 수 없을때는 두칸이상을 띄운 후 사용하자.

x = x + 1                 # Compensate for border

Documentation Strings

다음 문서를 읽어보자 DocString

Naming Conventions

Descriptive: Naming Styles

다음 여러 naming styles 들이 주어진다.

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (or CapWords, or CamelCase – so named because of the bumpy look of its letters [4]). This is also sometimes known as StudlyCaps.
    Note When using acronyms in CapWords, capitalize all the letters of the acronym. Thus HTTPServerError is better than HttpServerError.

  • mixedCase (differs from CapitalizedWords by initial lowercase character!)
  • Capitalized_Words_With_Underscores (ugly!)

Prescriptive: Naming Conventions

Name to Avoid

‘l’ (lowercase letter el), ‘O’ (uppercase letter oh), or ‘I’ (uppercase letter eye) 를 single character 변수이름으로 짓지마라.(헷갈린다)

ASCII Compatibility

아스키 호환성을 다음 문서에서 참고해라. ASCII

Package and Module Names

패키지와 모듈이름을 명명할 때는 최대한 짧게, 모두 소문자로, _(underscore) 를 더해준다(가독성을 높혀줄때만 _ 사용)

Class Names

일반적으로 CapitalizedWords(CapWords, or CamelCase)를 사용한다.

Type variable Names

Type variable 이름은 다음 링크에서 자세히 소개되고 Type Hints 일반적으로는 짧은 CapWords를 선호한다.

from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)

Exception Names

Exception 도 클래스 여야하기 때문에, 위의 Class 명명 규칙을 따라 Capwords를 사용한다. 마지막에 “Error”만 붙여주도록 하자.

Global Variable Names

function 규칙과 거의 같다.

Function and Variable Names

위에 나타낸 여러 스타일 중 lower_case_with_underscores 스타일을 사용한다.

Method Names and Instance Variables

  • 함수 명명할때는 lower_case_with_underscores를 사용해라.
  • non-public methods와 instance variables에 대해서만 앞에 underscore를 추가해줘라(c++ private 같은것)
  • subclass와 이름 충돌을 피하기 위해서는 앞에 underscore 두개를 사용해라

Constants

모두 대문자와 underscore들을 이용하여 정의하자.

Designing for Inheritance
  • Public attributes 는 leading underscore가 없어야 한다.
  • 만약 public attribute name이 python keyword 와 중복되면 trailing underscore를 사용해라
  • 상속을 거부하고 싶다면 leading underscore를 두개사용하여 막을 수 있다.(c++ priavte 화)

Programming Recommendations

  • None과 같은 싱글통과의 비교의 경우 is 또는 is not 으로 수행해야 하고 절대 동등 연산자가 아니다.
# Correct:
if foo is not None:

# Wrong:
if not foo is None:

전자가 읽기 쉽고 선호된다.

  • lambda 보다는 def 를 이용해 함수 선언하는것이 선호된다(하지만 여전히 lambda도 많이 쓰는것으로 안다.)
# Correct:
def f(x): return 2*x

# Wrong:
f = lambda x: 2*x
  • 예외 구문을 사용할 때 일반 Exception을 사용하여도 되지만 특정 에러를 구체적을 나타내는 에러를 명시하라.
try:
    import platform_specific_module
except ImportError:
    platform_specific_module = None

ImportError가 예상된다면 위에처럼 명시해주자

  • try except 구문에서는 try 부분에 최소한의 코드를 작성하자.(마스킹 버그 방지)
# Correct:
try:
    value = collection[key]
except KeyError:
    return key_not_found(key)
else:
    return handle_value(value)

# Wrong:
try:
    # Too broad!
    return handle_value(collection[key])
except KeyError:
    # Will also catch KeyError raised by handle_value()
    return key_not_found(key)    
  • 코드내의 함수에서 반환문은 표현식을 반환하거나 아무것도 하지않거나 둘중 하나를 일관성있게 작성해야한다.
# Correct:
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)

# Wrong:
def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)    
  • Use ''.startswith() and ''.endswith()를 사용해라 prefixes나 suffixes를 확인하는데 있어서
# Correct:
if foo.startswith('bar'):

# Wrong:
if foo[:3] == 'bar':
  • Object type 비교에는 isinstance()를 사용해라
# Correct:
if isinstance(obj, int):

# Wrong:
if type(obj) is type(1):
  • sequences,(strings, lists, tuples) 등에는 빈 시퀀스가 거짓인것을 사용해라.
# Correct:
if not seq:
if seq:

# Wrong:
if len(seq):
if not len(seq):
  • boolean 값을 True 나 False에 “==”를 사용해 비교하지마라
# Correct:
if greeting:

# Wrong:
if greeting == True:
  • try finally 구문에서는 흐름 제어 문 return/break/continue을 사용한 구문 탈줄 용어 사용은 삼가자.
# Wrong:
def foo():
    try:
        1 / 0
    finally:
        return 42

Function annotations

함수 annotations 작성법은 다음 문서를 참고하자 function annotations

Variable annotations

변수 annotations 작성법은 다음 문서를 참고하자 variable annotations

Comments