Programming,  Python

[Python] Coding Convention (PEP 8)

각 언어별로 coding guideline이 조금씩 다르다. 되도록이면 이 guideline을 지키면서 프로그램을 작성하면 추후에 다른 사람들이 봤을 때도 가독성이 높아질 것이다.

자세하게 설명하기 전에 요약하면 다음과 같다.

  1. Module과 package 이름은 짧고 lower_case_with_underscore
  2. 다른 모듈에서 import 할 때 보일 필요가 없는 함수, 변수는 변수명 앞에 _를 추가 (즉 private)
  3. 상수는 A_CONSTANT
  4. 함수는 naming_convention
  5. 클래스는 NamingConvention
  6. 한 라인의 길이가 79 길이가 넘지 않도록 한다. (이것은 대략적인 A4 용지 사이즈)

Code Lay-out

  • 들여쓰기는 공백 4칸을 권장
  • 한 줄은 최대 79자까지
  • 최상위(top-level) 함수와 클래스 정의는 2줄씩 띄어 씀
  • 클래스 내의 메소드 정의는 1줄씩 띄어 씀

Whitespace in Expressions and Statements

  • 다음과 같은 곳의 불필요한 공백은 피함
    • 대괄호([])와 소괄호(())안
    • 쉼표(,), 쌍점(:)과 쌍반점(;) 앞
  • 키워드 인자(keyword argument)와 인자의 기본값(default parameter value)의 = 는 붙여 씀

Comments

  • Comment가 한 줄이라도 “”” 를 사용하면 나중에 확장성이 높아짐
  • Closing “””를 같은 줄에 넣는게 보기 좋음
  • 주석 위 아래로 black를 두지 않음

Naming Convention

  • 변수명에서 _(밑줄)은 위치에 따라 다음과 같은 의미
    • _single_leading_underscore: 내부적으로 사용되는 변수를 말함
    • single_trailing_underscore_: 파이썬 기본 키워드와 충돌을 피하려고 사용
    • __double_leading_underscore: 클래스 속성으로 사용되면 그 이름을 변경 (ex. FooBar에 정의된 __boo는 _FooBar__boo로 변경)
    • __double_leading_and_trailing_underscore__: 마술(magic)을 부리는 용도로 사용되거나 사용자가 조정할 수 있는 네임스페이스 안의 속성을 뜻함. 이런 이름을 새로 만들지 마시고 오직 문서대로만 사용
  • 소문자 L, 대문자 O, 대문자 I는 변수명으로 되도록 사용 금지. 어떤 폰트에서는 가독성이 굉장히 안 좋음
  • 모듈(Module) 명은 짧은 소문자로 구성되며 필요하다면 밑줄로 나눔
    • 모듈은 파이썬 파일(.py)에 대응하기 때문에 파일 시스템의 영향을 받으니 주의
    • C/C++ 확장 모듈은 밑줄로 시작
  • 클래스 명은 카멜케이스(CamelCase)로 작성
    • 내부적으로 쓰이면 밑줄을 앞에 붙임
    • 예외(Exception)는 실제로 에러인 경우엔 “Error”를 뒤에 붙임
  • 함수명은 소문자로 구성하되 필요하면 밑줄로 나눔
    • 대소문자 혼용은 이미 흔하게 사용되는 부분에 대해서만 하위호환을 위해 허용
  • 인스턴스 메소드의 첫 번째 인자는 언제나 self
  • 클래스 메소드의 첫 번째 인자는 언제나 cls
  • 메소드명은 함수명과 같으나 비공개(non-public) 메소드, 혹은 변수면 밑줄을 앞에 붙임
  • 서브 클래스(sub-class)의 이름충돌을 막기 위해서는 밑줄 2개를 앞에 붙임
  • 상수(Constant)는 모듈 단위에서만 정의하며 모두 대문자에 필요하다면 밑줄로 나눔

Programming Recommendations

  • 코드는 될 수 있으면 어떤 구현(PyPy, Jython, IronPython등)에서도 불이익이 없게끔 작성되어야 합니다.
  • None을 비교할때는 is나 is not만 사용합니다.
  • 클래스 기반의 예외를 사용하세요.
  • 모듈이나 패키지에 자기 도메인에 특화된(domain-specific)한 기반 예외 클래스(base exception class)를 빌트인(built-in)된 예외를 서브클래싱해 정의하는게 좋습니다. 이 때 클래스는 항상 문서화 문자열을 포함해야 합니다.
  • raise ValueError('message')가 (예전에 쓰이던) raise ValueError, 'message' 보다 낫습니다.
  • 예외를 except:로 잡기보단 명확히 예외를 명시합니다.(ex. except ImportError:
  • try: 블록의 코드는 필요한 것만 최소한으로 작성합니다.
  • string 모듈보다는 string 메소드를 사용합니다. 메소드는 모듈보다 더 빠르고, 유니코드 문자열에 대해 같은 API를 공유합니다.
  • 접두사나 접미사를 검사할 때는 startswith()와 endwith()를 사용합니다.
  • 객체의 타입을 비교할 때는 isinstance()를 사용합니다.
  • 빈 시퀀스(문자열, 리스트(list), 튜플(tuple))는 조건문에서 거짓(false)입니다.
  • 불린형(boolean)의 값을 조건문에서 ==를 통해 비교하지 마세요.

Example

아래는 괜찮은 example code가 있어서 가져왔다.

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""This module's docstring summary line.
This is a multi-line docstring. Paragraphs are separated with blank lines.
Lines conform to 79-column limit.
Module and packages names should be short, lower_case_with_underscores.
Notice that this in not PEP8-cheatsheet.py
Seriously, use flake8. Atom.io with https://atom.io/packages/linter-flake8
is awesome!
See http://www.python.org/dev/peps/pep-0008/ for more PEP-8 details
"""


import os  # STD lib imports first
import sys  # alphabetical

import some_third_party_lib  # 3rd party stuff next
import some_third_party_other_lib  # alphabetical

import local_stuff  # local stuff last
import more_local_stuff
import dont_import_two, modules_in_one_line  # IMPORTANT!
from pyflakes_cannot_handle import *  # and there are other reasons it should be avoided # noqa
# Using # noqa in the line above avoids flake8 warnings about line length!


_a_global_var = 2  # so it won't get imported by 'from foo import *'
_b_global_var = 3

A_CONSTANT = 'ugh.'


# 2 empty lines between top-level funcs + classes
def naming_convention():
    """Write docstrings for ALL public classes, funcs and methods.
    Functions use snake_case.
    """
    if x == 4:  # x is blue <== USEFUL 1-liner comment (2 spaces before #)
        x, y = y, x  # inverse x and y <== USELESS COMMENT (1 space after #)
    c = (a + b) * (a - b)  # operator spacing should improve readability.
    dict['key'] = dict[0] = {'x': 2, 'cat': 'not a dog'}


class NamingConvention(object):
    """First line of a docstring is short and next to the quotes.
    Class and exception names are CapWords.
    Closing quotes are on their own line
    """

    a = 2
    b = 4
    _internal_variable = 3
    class_ = 'foo'  # trailing underscore to avoid conflict with builtin

    # this will trigger name mangling to further discourage use from outside
    # this is also very useful if you intend your class to be subclassed, and
    # the children might also use the same var name for something else; e.g.
    # for simple variables like 'a' above. Name mangling will ensure that
    # *your* a and the children's a will not collide.
    __internal_var = 4

    # NEVER use double leading and trailing underscores for your own names
    __nooooooodontdoit__ = 0

    # don't call anything (because some fonts are hard to distiguish):
    l = 1
    O = 2
    I = 3

    # some examples of how to wrap code to conform to 79-columns limit:
    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if width == 0 and height == 0 and \
           color == 'red' and emphasis == 'strong' or \
           highlight > 100:
            raise ValueError('sorry, you lose')
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so -- values are %s, %s" %
                             (width, height))
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)

    # empty lines within method to enhance readability; no set rule
    short_foo_dict = {'loooooooooooooooooooong_element_name': 'cat',
                      'other_element': 'dog'}

    long_foo_dict_with_many_elements = {
        'foo': 'cat',
        'bar': 'dog'
    }

    # 1 empty line between in-class def'ns
    def foo_method(self, x, y=None):
        """Method and function names are lower_case_with_underscores.
        Always use self as first arg.
        """
        pass

    @classmethod
    def bar(cls):
        """Use cls!"""
        pass

# a 79-char ruler:
# 34567891123456789212345678931234567894123456789512345678961234567897123456789

"""
Common naming convention names:
snake_case
MACRO_CASE
camelCase
CapWords
"""

# Newline at end of file

Reference

  1. https://www.python.org/dev/peps/pep-0008/
  2. https://3months.tistory.com/385
  3. https://spoqa.github.io/2012/08/03/about-python-coding-convention.html

Leave a Reply

Your email address will not be published. Required fields are marked *