# 코드 레이아웃

# 들여쓰기

각 들여쓰기 레벨마다 4칸 공백을 사용하자.

여러 줄에 이어지는 문장에서는 괄호 안의 Python의 암시적 줄 결합을 사용하여 괄호들로 감싸진 요소들의 세로줄을 맞추어야 한다. 또는 내어쓰기[1]를 사용하여 세로줄을 맞추어야 한다. 내어쓰기를 사용할 때는 고려해야 할 사항이 있다. 우선, 첫 번째 줄에는 아규먼트가 없어야한다. 그리고 추가적인 들여쓰기는 여러 줄에 이어지는 문장 자체를 구분하는 데에 사용되어야 한다.

# 옳은 예:

# 열린 구분 문자(역: 여기서는 소괄호)를 기준으로 정렬된다.
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)

# 내어쓰기는 레벨을 더해야 한다. (역: 나머지 줄에 들여쓰기를 해야 한다는 의미)
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 잘못된 예:

# 세로줄을 맞추지 않았을 때, 첫 번째 줄에 아규먼트는 없어야 한다.
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)
1
2
3
4
5
6
7
8
9
10
11

여러 줄에 이어지는 문장에서 4칸 공백 들여쓰기 규칙은 선택 사항이다.

# 내어쓰기는 4칸 공백 외의 방식으로 들여쓰기가 *될 수도 있다* (역: 여기서는 2칸 공백 규칙이 적용되었다.)
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)
1
2
3
4

if-문의 조건 부분이 길어 여러 줄로 작성 될 필요가 있는 경우를 보자. if 키워드에 한 칸 공백과 열린 소괄호를 붙여 작성하면, 나머지 여러 줄로 작성 된 조건부가 저절로 4칸 공백 들여쓰기 되는 것에 주목해야 한다.

이는 if-문 내 중첩되어 있는 코드 모음과 시각적인 충돌을 일으킬 수 있다. (이 코드 모음도 4칸 공백 들여쓰기가 되어 있을 경우)

이 PEP 문서에서는 if-문 내 중첩되어 있는 문장으로부터 이러한 조건 부분에 대해 시각적으로 어떻게 추가적인 구별을 할 것인지 (또는 구별을 할 것인지 하지 않을 것인지)에 대한 명백한 입장이 없다. 이런 상황에서 혀용 가능한 선택을 제시하되, 제한하지는 않을 것이다.

# 추가적인 들여쓰기를 하지 않은 경우
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# 구문 하이라이터(syntax highlighter)를 지원하는 에디터에서
# 구별할 수 있게 주석을 더하기
if (this_is_one_thing and
    that_is_another_thing):
    # 두 조건들이 참일 때, 뭐라뭐라....
    do_something()

# 여러 줄에 이어지는 조건 줄에서 추가적인 들여쓰기를 한 경우
if (this_is_one_thing
        and that_is_another_thing):
    do_something()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

(이항 연산자의 앞뒤로 띄어쓰기를 할 것인지에 대한 논의를 아래의 섹션에서 확인할 수 있다.)

여러 줄 구조에서 닫힌 괄호들은 리스트의 마지막 줄 공백이 아닌 첫 번째 문자를 기준으로 세로줄을 맞출 수 있다.

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

또는 시작하는 줄의 첫 번째 문자를 기준으로 세로줄을 맞출 수 있다.

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

# 탭 아니면 스페이스?

스페이스가 더 나은 들여쓰기 방식이다.

탭은 이미 탭으로 들여쓰기 된 코드에서 일관성 유지를 위해서만 사용되어야 한다.

Python 3에서는 들여쓰기에 탭과 스페이스를 혼용하는 것을 허용하지 않는다.

탭과 스페이스를 혼용하여 들여쓰기 한 Python 2 코드는 오로지 스페이스만을 사용하는 것으로 바꿔야 한다.

-t 옵션과 함께 Python 2 명령 줄 인터프리터(command line interpreter)를 호출(invoke) 하면 탭과 스페이스가 잘못 혼용된 코드에 대해 경고한다. -tt 옵션을 사용할 때는 경고 대신 에러가 발생한다. 이 옵션들을 매우 권장한다!

# 한 줄의 최대 길이

모든 줄은 한 줄 당 최대 79개의 문자까지 적을 수 있다.

구조적인 제약을 덜 받는, 이어지는 긴 텍스트 블록(독스트링 또는 주석)도 줄 당 72개 문자로 제한되어야 한다.

필요한 에디터 창의 너비를 제한하는 것은 여러 파일들의 창 분할을 가능하게 하고, 인접한 열들에 두 가지 버전으로 보여주는 리뷰 툴을 사용할 때 유용하다.

대부분의 툴들에서의 디폴트 래핑은 코드의 시각적 구조를 방해하여, 이해하는 것을 더 어렵게 한다. 비록 툴이 마지막 열에 글리프 표식(glyph)[2]을 두었더라도, 창 너비를 80으로 설정한 에디터에서 래핑하는 것을 피하도록 하자. 몇 웹 기반 툴들은 다이나믹 라인(dynamic line) 래핑을 전혀 제공하지 않는다.

몇몇 팀은 줄의 길이가 더 긴 것을 아주 선호한다. 이 이슈에 동의하는 팀의 우선적으로 또는 배타적으로 유지되는 코드를 위해, 주석이나 독스트링만은 72자로 여전히 제한되도록 하면서, 줄 당 길이 제한을 99까지 늘리는 것은 허용한다.

Python 표준 라이브러리는 보수적이며, 각 줄을 79자로 제한한다. (그리고 독스트링/주석은 72자까지)

긴 줄을 래핑하는 바람직한 방법은 Python의 괄호 내 암시적 줄 잇기 방식을 사용하는 것이다. 긴 줄들은 여러줄에 걸쳐 괄호로 표현을 래핑하는 하는 것으로 줄 바꿈을 할 수 있다. 여러 줄이 계속해서 이어질 때 백슬래시를 사용하는 방법보다 이러한 방법을 사용하는 것이 바람직하다.

백슬래시가 여전히 적절한 경우가 있다. 예를 들어, 길고 여러 줄인 with-문은 암시적인 줄 잇기를 사용할 수가 없어 백슬래시 사용이 적합하다.

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
3

(여러 줄 with-문에서의 들여쓰기 뿐 아니라, 여러 줄의 if-문에 대한 이전의 논의를 보라.)

또 다른 예시로는 assert 문이 있다.

여러 줄에 걸쳐 이어지는 문장은 적절히 들여쓰기 해야함을 명심하자.

# 이항 연산자 앞뒤로 줄바꿈을 해야할까?

지난 수십 년 간 권장된 스타일은 이항 연산자 이후에 줄 바꿈을 하는 것이다. 하지만 이는 두 가지 이유로 가독성을 해칠 수 있다. 첫째, 연산자들이 화면의 여러 열(columns)에 흩어지는 경향이 있다. 둘째, 각 연산자가 대응되는 피연산자로부터 떨어져 이전 줄로 이동한다. 여기, 어느 피연산자를 더하고 빼는 것인지 알기 위해 우리의 눈이 더 열심히 일해야 하는 예시가 있다.

# 잘못된 예
# 연산자가 대응되는 피연산자로부터 멀리 떨어져 있다.
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)
1
2
3
4
5
6
7

이 가독성 문제를 해결하기 위해, 수학자들과 그들의 퍼블리셔들은 반대의 컨벤션을 다른다. Donald Knuth 는 그의 Computers and Typesetting 시리즈에서 다음의 전통적인 규칙을 설명한다. "비록 단락 내의 공식은 항상 이항 연산 및 관계 뒤에 줄 바꿈을 하지만, 표시된 공식은 항상 이항 연산 전에 줄 바꿈을 한다."[3]

수학으로부터 온 전통을 따르는 것은 대게 더 가독성 있는 코드를 결과로 낸다.

# 옳은 예:
# 연산자와 피연산자를 구별하기 쉽다.
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)
1
2
3
4
5
6
7

Python 코드에선, 컨벤션이 국지적으로 일관성이 있는 한 이항 연산자 앞이나 뒤에 줄 바꿈을 하는 것이 허용된다. 새로운 코드의 경우 Knuth의 스타일이 제안된다.

# 공백 줄

최상위 레벨 함수와 클래스 정의 앞뒤로 두 개의 공백 줄이 감싸진다.

클래스 내의 메소드 정의 앞뒤로는 한 개의 공백 줄이 감싸진다.

관련된 함수들의 모음을 분리하기 위해 추가적인 공백 줄이 (꼭 필요한 경우에만) 사용될 수 있다. 공백 줄은 관련된 one-liner[4] 의 모음 사이에서 생략될 수 있다. (예를 들면, 더미 구현의 집합)

함수 내 공백 줄은 논리적인 섹션을 구분할 때 적절하게 사용하자.

Python 은 컨트롤 + L (^L) 폼 피드(form feed) 문자를 공백으로 받아들인다. 많은 툴들이 이 문자를 페이지 구분자로 취급하므로, 이를 파일 내 서로 관련된 섹션들의 페이지를 분리하기 위해 사용할 수 있다. 단, 몇 에디터와 웹 기반 코드 뷰어에서는 컨트롤 + L 이 폼 피드로 인식되지 않을 수 있다는 점을 주의하자. 그 웹 기반 코드 뷰어는 그 위치에 다른 글리프를 보여줄 것이다.

# 소스 파일 인코딩

코어 Python 배포 코드는 항상 UTF-8을 사용해야 한다. (또는 Python 2 에서는 ASCII)

아스키(Python 2 에서) 또는 UTF-8(Python 3 에서)을 사용하는 파일들은 인코딩 선언부(declaration)를 포함하면 안된다.

표준 라이브러리에서는, 디폴트 값이 아닌 인코딩은 테스트 목적 또는 주석이나 독스트링에서 아스키 문자가 아닌 문자를 포함하는 작성자의 이름을 언급할 때만 사용되어야 한다. 반면에 \x, \u, \U, \N 이스케이프를 사용하는 것은 문자열 리터럴 내에 아스키가 아닌 데이터를 포함하기 위한 바람직한 방법이다.

Python 3.0 과 그 이상은, 다음의 정책이 표준 라이브러리에 의해 규정되어있다. (PEP 3131open in new window 참고) Python 표준 라이브러리 내의 모든 식별자들은 반.드.시. 아스키로만 이루어진 식별자들을 사용해야 하며, 가능한 한 영어를 사용해야한다. (많은 경우에, 약어나 기술적인 용어만을 영어가 아니어도 사용한다.) 덧붙여, 문자열 리터럴과 주석 또한 아스키여야한다. 다음의 경우만 예외인데, (a) 아스키가 아닌 기능을 테스트하는 테스트 케이스 그리고, (b) 라틴 알파벳(latin-1, ISO/IEC 8859-1 character set)에 기반하지 않은 작성자명 일 땐, 반드시 이 문자 집합에서 이름의 전자(transliteration)[5]를 제공해야 한다.

전세계의 사용자를 대상으로 하는 오픈 소스 프로젝트는 유사한 정책을 채택하길 권장한다.

# 가져오기

  • 가져오기(Imports)는 분리된 줄에 사용해야 한다.

    # 옳은 예
    import os
    import sys
    
    1
    2
    3
    # 잘못된 예
    import sys, os
    
    1
    2

    이것도 괜찮다.

    # 옳은 예
    from subprocess import Popen, PIPE
    
    1
    2
  • 가져오기는 항상 파일의 맨 위에 놓여져 있다. 세세하게는 모듈 주석과 독스트링 바로 다음에, 모듈 전역과 상수 바로 전에 놓여져 있다.

    가져오기는 다음의 순서에 따라 구분되어야 한다.

    1. 표준 라이브러리 가져오기
    2. 관련 서드 파티 가져오기
    3. 로컬 어플리케이션/라이브러리 가져오기

    각 가져오기 모음 사이에 공백 줄을 넣어야한다.

  • 절대 경로 가져오기가 권장된다. 대체적으로 이 편이 가독성이 좋다. 가져오기 체계가 잘못 구성 되었을 때도(예를 들어, 패키지 내부의 디렉토리가 sys.path 에서 끝나는 경우), 더 나은 행위를 하는 경향이 있다. (그게 아니더라도, 최소한 더 나은 에러 메시지를 준다.)

    import mypkg.sibling
    from mypkg import sibling
    from mypkg.sibling import example
    
    1
    2
    3

    하지만, 명시적 상대경로 가져오기는 절대경로 가져오기의 허용 가능한 대안이다. 특히, 불필요하게 장황하고 복잡한 패키지 레이아웃을 처리할 땐, 절대경로 가져오기를 사용하는 것보다 나을 수 있다.

    from . import sibling
    from .sibling import example
    
    1
    2

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

    암시적인 상대경로 가져오기는 절대 사용되어선 안되며, Python 3 에서는 제거되었다.

  • 클래스를 갖고 있는 모듈에서 클래스를 가져올 때, 다음과 같이 작성하는 것은 괜찮다.

    from myclass import MyClass
    from foo.bar.yourclass import YourClass
    
    1
    2

    만약 이 철자가 로컬 이름과 충돌이 발생하면, 명시적으로 작성하자.

    import myclass
    import foo.bar.yourclass
    
    1
    2

    그리고 "myclass.MyClass"와 "foo.bar.yourclass.YourClass"를 사용하자.

  • 와일드 카드 가져오기(from <module> import *) 는 피해야한다. 이들은 네임스페이스 안에서 보여지는 이름들을 불분명하게 만들고, 독자와 많은 자동화 툴들에 혼선을 준다. 단, 와일드 카드 가져오기를 사용할 수 있는 사례가 하나 있다. 공개 API의 일부로 내부 인터페이스를 다시 게시(republishing)하는 경우다. (예를 들어, 선택적 가속기 모듈(optional accelerator module) 내로부터 온 정의로, 인터페이스의 순수 Python 구현을 덮어 쓰는데, 정확히 어떤 정의를 덮어 쓸지 미리 알 수 없을 때)

    이름을 이러한 방법으로 다시 게시할 때, 퍼블릭 그리고 내부 인터페이스에 관한 가이드라인 은 계속 적용된다.

# 모듈 레벨 던더 이름

던더들은 앞뒤로 두개의 밑줄(underscore)로 감싸진 이름으로, __all__, __author__, __version__ 등이 있다. 모듈 레벨의 "던더들"은 모듈의 독스트링 다음, 모든 가져오기 구문 전에 있어야 한다. , from __future__ 가져오기는 제외한다. Python의 future-imports 명령(mandates)은 모듈내에서 반드시 독스트링을 제외한 어떠한 코드들 보다도 전에 놓여져 있어야 한다.

"""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
1
2
3
4
5
6
7
8
9
10
11
12
13

  1. 내어쓰기(hanging indent)란 시작 줄을 제외한 나머지 줄에 들여쓰기를 하는 형식 설정(type-setting) 스타일이다. Python 컨텍스트에서 이 개념은 열린 괄호가 줄의 공백을 제외한 마지막 문자고, 이후 닫힌 괄호가 있는 줄까지 들여쓰기 하는 스타일을 말한다. ↩︎

  2. 역: 상형문자 같은 것 ↩︎

  3. Donald Knuth의 책 "The TeXBook", 195 ~ 196 쪽. ↩︎

  4. 역: 한줄로 끝내버리는 것들open in new window ↩︎

  5. 역: 위키피디아open in new window ↩︎

Last Updated: 5/22/2021, 1:19:36 PM