Python Language
생각이나 느낌을 나타내거나 전달하기 위하여 사용하는
음성, 문자, 몸짓 등의 수단 또는 그 사회관습적 체계
언어의 사전적 정의입니다. 언어란 한 사회의 구성원 사이에 약속된 의사소통 규칙이자 오랜 세월 특유의 문화와 역사가 담긴 사회관습적 체계입니다.
여러분은 외국어를 공부한 경험이 있을 것입니다. 처음에 어떤 방식으로 접근하셨나요? 누군가는 단어와 문법을 공부하며 언어에 대한 지식을 쌓아갑니다. 누군가는 외국에서 현지인들과 소통하며 자연스럽게 외국어를 익힙니다. 어떤 방식을 사용하던지 최종 목표는 그 언어에 담긴 철학을 이해하고 자유롭게 구사하여 의사 소통 하는 것입니다.
Programming language도 마찬가지입니다. Python을 사용해서 code를 작성하고 다른 개발자와 의사 소통 하려면 우선 python을 이해해야 합니다.
Python Philosophy
Readability counts.
Python의 철학은 PEP20: The Zen of Python에서 19개 문장으로 설명합니다. 한 마디로 요약하면 readability! Python은 누가 봐도 읽기 쉽고 간결한 code를 지향합니다.
PEP8은 무엇일까요?
PEP(Python Enhancement Proposals)는 ‘Python 제안 사항’ 정도로 이해할 수 있습니다. Python의 새로운 기능이나 적용사항을 안내하고 사용 가이드라인과 정보를 제공합니다.
PEP8은 python을 만든 Guido van Rossum을 포함하여 세 명의 저자가 작성한 문서로 python code에 대한 style guide 내용을 담고 있습니다. 일종의 code 규칙을 제안하는 문서로 이해할 수 있습니다. 이 외에도 다양한 python style guide가 존재하는데 대부분 PEP8을 기반으로 만들어졌기 때문에 알아두는 것이 좋습니다. 상세한 내용은 PEP8 공식문서를 참고하세요. 이제부터 PEP8을 하나씩 알아보겠습니다.
A Foolish Consistency is the Hobgoblin of Little Minds
미국의 철학자이자 시인으로 활동한 Ralph Waldo Emerson의 ‘Self-Reliance’에 나오는 문구입니다. 원문은 아래와 같습니다.
“A foolish consistency is the hobgoblin of little mindes,
adored by little statesment and philosophers and divines.”
“어리석은 일관성은 별 볼 일 없는 정치가와 철학자, 그리고 신학자들이 사랑하는,
편협한 마음을 가진 홉고블린이다.”
문구의 앞/뒤 문맥을 살펴보면 오늘과 내일 하는 말이 모순될 지언정, 다른 사람의 시선에 휘둘리지 않고 자신을 신뢰하는 자세가 사회를 지탱하고 더 나은 방향으로 나아가도록 만든다고 이야기 합니다. 사회의 정해진 규칙과 타인이 옳다고 여기는 기준에 맞춰 바보같이 사는 것은 소인배 홉고블린에 지나지 않다는 의미이기도 합니다. 이 사상은 오늘날 미국을 지탱하는 개인주의 정신의 근간이 되었습니다.
Python code에서 가독성은 매우 중요합니다. 따라서 PEP8이 제시하는 style guide에 얽매일 필요없이 더 나은 방식이 있다면 반드시 사용하시기 바랍니다.
Code Lay-out
Indentation
하나의 indentation 레벨은 4개의 spaces를 사용합니다. 연속된 line은 괄호/중괄호/대괄호 내부에서 python의 implicit line joining을 사용하여 수직선 상에 맞추거나 hanging indent를 사용합니다. 이 때 첫 번째 line에 arguments를 넣지 않습니다. 또한 2-spaces로 대체함으로써 예외적으로 4-spaces 규칙을 벗어날 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
## Correct:
# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
var_three, var_foue)
# 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)
if문 같은 조건문의 조건이 길어지는 경우, 괄호를 사용하여 multiline으로 작성합니다. 마찬가지로 4-spaces 규칙이 기본 적용되며 경우에 따라 추가 indent를 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# No extra indentation.
if (this_is_one_thing and
that_is_another_thing):
do_something()
# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
if (this_is_one_thing and
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
# Add some extra indentation on the conditional continuation line.
if (this_is_one_thing
and that_is_another_thing):
do_something()
닫는 괄호/중괄호/대괄호는 multiline 중 마지막 line의 첫 번째 글자와 동일선상에 맞추거나, 첫 번째 line의 시작과 동일선상에 맞춥니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Closing brace/bracker/parenthesis on multiline constructs
# line up under the first non-whitespace character of the last line of list
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
# or line up under the first character of the line that starts the multiline construct
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
Tabs or Spaces?
Space가 더 선호되는 방식입니다. Tab은 이미 작성된 code에서 indent가 tab으로 작성되어 있는 경우 사용합니다. Python3에서는 space와 tab 혼용을 금지합니다. Python2에서는 혼용이 가능하지만 tab을 space로 변환하도록 강력히 권고합니다.
Maximum Line Length
모든 line은 최대 79자 이내로 제한합니다. Docstrings나 comments 같은 몇몇 구조는 최대 72자로 제한합니다. Line의 글자수를 제한하면 두 개 이상 code를 editor로 열어서 동시에 작업할 수 있습니다. 또한 더 깔끔한 code를 작성할 수 있습니다. Multiline으로 작성할 경우 앞서 언급한 indent 규칙을 준수하고 backslash를 사용합니다.
1
2
3
with open('/path/to/some/file/you/want/to/read') as file_1, \
open('/path/to/some/file/being/written') as file_2:
file_2.write(file_1.read())
Should a Line Break Before or Agter a Binary Operator?
사칙연산 기호와 line break의 위치에 관한 내용입니다. 과거에는 사칙연산 기호 이후에 line break가 위치하는 style을 권장했습니다. 하지만 다음 두 가지 이유로 가독성을 떨어뜨립니다.
- 연산기호가 line마다 다른 위치로 흩어집니다.
- 연산기호의 대상을 명확하게 파악하기 어렵습니다.
1
2
3
4
5
6
7
8
## Wrong:
# operators sit far away from their operands
income = (gross_wages +
taxable_interest +
(dividents - qualified_dividents) -
ira_deduction -
student_loan_interest)
하지만 수학에서는 전통적으로 사칙연산 기호 이전에 line break가 위치하는 style을 사용합니다. 가독성이 더 좋은 이 방식을 python에서도 권장합니다.
1
2
3
4
5
6
7
8
## Correct:
# easy to match operators with operands
income = (gross_wages
+ taxable_interest
+ (dividents - qualified_dividents)
- ira_deduction
- student_loan_interest)
Blank Lines
Top-level function과 class는 두 개의 blank lines로 감쌉니다. class 내 method는 한 개의 blank line으로 감쌉니다. 서로 연관된 functions 그룹을 구분하거나 논리적 섹션을 구분하기 위해서 추가 blank line을 사용할 수 있습니다.
Source File Encoding
Core python 배포판 code는 UTF-8을 사용합니다. 모든 python standard library의 식별자는 반드시 ASCII 식별자와 영어 단어를 사용합니다.
Imports
Imports는 line을 분리하여 수행합니다.
1
2
3
4
5
6
7
## Correct:
import os
import sys
from subprocess import Popen, PIPE
## Wrong:
import sys, os
Imports는 항상 파일의 최상단에 위치합니다. Module comments와 docstrings 다음에, 그리고 module globals와 constants 이전에 위치합니다.
Wildcard imports(from ‘module’ import *)는 다른 개발자와 automated tools에 혼동을 주기 때문에 사용하지 않습니다.
Module Level Dunder Names
Module level “dunders”(두 개의 underscore로 시작하고 끝나는 명칭입니다. 예를 들어, all, author, version, etc) module docstring 다음에, 그리고 ‘from future’ import를 제외한 import문 이전에 위치합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
"""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
Python에서 single-quoted strings(‘)와 double-quoted strings(“)를 동일하게 작동합니다. 둘 중 하나를 선택하면 계속 동일한 방식을 사용합니다. 다만 triple-quoted strings는 PEP257의 docstring convention에 따라 double quote characters(“”“)를 사용합니다.
Whitespace in Expressions and Statements
Pet Peeves
‘Pet Peeves’ 의미를 우선 알아보겠습니다. 사전적 의미는 ‘Something that a particular person finds especially annoying’ 입니다. 한 사람이 지극히 싫어하고 기피하는 총체적인 것들을 의미합니다. ‘Pet’은 보통 귀엽고 돌봐줘야 하는 존재로 많이 사용되지만 아이러니하게 19세기 혐오하거나 아주 싫은 부정적인 의미로 동시에 사용되기 시작했다고 합니다. ‘Peeve’는 훨씬 더 오래된 단어 ‘peevish’에서 파생되었는데 ‘easily irritated’ 의미를 가지고 있습니다. 두 개의 단어가 합쳐져 위와 같은 의미를 지니게 되었다고 합니다. 자세한 내용은 링크를 참고하세요.
PEP8에서도 아래 상황에서 과도한 whitespace 사용을 싫어합니다.
- 괄호, 중괄호, 대괄호 바로 안
1
2
3
4
5
## Correct:
spam(ham[1], {eggs: 2})
## Wrong:
spam( ham[ 1 ], { eggs: 2 } )
- 뒤에 따라오는 comma와 닫는 괄호 사이
1
2
3
4
5
## Correct:
foo = (0,)
## Wrong:
foo = (0, )
- comma, semicolon, colon 바로 앞
1
2
3
4
5
## Correct:
if x == 4: print(x, y); x, y = y, x
## Wrong:
if x == 4 : print(x , y) ; x , y = y , x
- 그러나 slice 안에서 colon이 binary operator처럼 작동할 때는 양쪽에 동일한 숫자의 whitespace를 추가로 사용가능
1
2
3
4
5
6
7
8
9
10
11
12
## 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]
- function 에서 arguments를 시작하는 여는 괄호 바로 앞
1
2
3
4
5
## Correct:
spam(1)
## Wrong:
spam (1)
- Indexing or slicing을 시작하는 여는 괄호 바로 앞
1
2
3
4
5
## Correct:
dct['key'] = lst[index]
## Wring:
dct['key'] = lst [index]
- 동일선상에 맞추기 위해 variable assignment시 한 개 이상의 whitespace 사용
1
2
3
4
5
6
7
8
9
## Correct:
x = 1
y = 2
long_variable = 3
## Wrong:
x = 1
y = 2
long_variable = 3
Other Recommendations
뒤에 따라오는 whitespace는 잘 보이지 않아 혼란을 줄 수 있으므로 사용하지 않습니다.
다음 binary operators는 양쪽에 각각 single whitespace를 넣습니다.: assignment(=), augmented assignment (+=, -=, etc), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not)
만약 다른 우선도를 가진 operators가 사용된다면 가장 낮은 우선도를 가진 operators 주변에 한 개의 whitespace 사용을 고려할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
## 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 annotation은 만약 arrow가 존재한다면 주변에 whitespace를 사용합니다.
1
2
3
4
5
6
7
## Correct:
def munge(input: AnyStr): ...
def munge() -> PosInt: ...
## Wrong:
def munge(input:AnyStr): ...
def munge()->PosInt: ...
- Keyword argument를 나타내거나 unannotated function parameter의 기본 값을 나타낼 때 사용하는 ‘=’ sign 주변에는 whitespace를 사용하지 않습니다.
1
2
3
4
5
6
7
## 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)
- Compound statements (multiple statements on the same line)은 보통 사용하지 않습니다.
1
2
3
4
5
6
## Correct:
if foo == 'blah':
do_blah_thing()
do_one()
do_two()
do_three()
When to Use Trailing Commas
- 뒤에 따라오는 comma의 사용은 보통 옵션입니다만, tuple에서 한 개의 element만 들어있는 경우 꼭 사용해야 합니다. 그리고 version control system, list of values, arguments 혹은 imported items가 추후 확장될 가능성이 있다면 사용하는 것이 좋습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
## Correct:
FILES = ('setup.cfg',)
FILES = [
'setup.cfg',
'tox.ini',
]
initialize(FILES,
error=True,
)
## Wrong:
FILES = 'setup.cfg',
FILES = ['setup.cfg', 'tox.ini']
initialize(FILES, error=True,)
Comments
모순된 comment는 차라리 없는 것만 못 합니다. Code가 바뀌면 comment를 꼭 업데이트 합니다. Comment는 항상 완전한 문장으로 기록합니다. 첫 번째 문자는 항상 대문자를 사용합니다.
Block comment는 한 두 개의 paragraph로 구성되고, 각 문장은 마침표로 끝맺음 합니다. Multi-sentence comment인 경우 가장 마지막 문장을 제외하고 각 마침표 이후에 두 개의 whitespace를 사용합니다.
Comment는 항상 명확하고 이해하기 쉽게 기록합니다. 비영어권 coder도 반드시 영어로 comment를 기록하세요.
Block Comments
Block comment는 code와 동일선상에 맞춰 기록합니다. Comment 각 line은 ‘#’과 single space로 시작합니다.
Inline Comments
Inline comment는 가급적 사용하지 않습니다. 만약 사용한다면 code에서 두 개 이상의 space를 넣은 뒤 inline comment를 기록합니다. Block comment와 마찬가지로 comment는 ‘#’과 single space로 시작합니다. Code 내용이 명확하다면 comment를 사용하지 않습니다.
Documentation Strings
훌륭한 documentation strings(docstrings)를 기록하는 규칙은 PEP257에 뿌리를 둡니다.
모든 public modules, functions, classes, 그리고 methods에 docstrings를 작성합니다.
Multi-lines docstrings 작성에 가장 중요한 것은 끝맺음하는 ‘”””’ 기호를 독립된 마지막 line에 위치시키는 것입니다. 단, one line docstings는 ‘”””’ 기호를 같은 line에 위치시킵니다.
1
2
3
4
5
6
7
## Correct:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
"""Return an ex-parrot."""
Naming Conventions
이미 배포된 python library의 naming convention은 완벽한 일관성을 보이지 않고 어지러운 편입니다. 그럼에도 불구하고 앞으로 안내하는 규칙에 따라 작성하기를 권장합니다.
Overriding Principle
사용자에게 보이는 API의 공개적인 파트는 사용법을 반영한 conventions를 따릅니다.
Descriptive: Naming Styles
다양한 naming style이 존재하며 어떤 종류가 있는지 인지하는 것이 도움이 될 수 있습니다.
- b (single lowercase letter)
- B (single uppercase letter)
- lowercase
- lower_case_with_underscores
- UPPERCASE
- UPPER_CASE_WITH_UNDERSCORES
- CapitalizedWords (or CapWords, or CamelCase)
- mixedCase
- Capitalized_Words_With_Underscores (ugly!)
- _single_leading_underscore
- single_trailing_underscore_
- __double_leading_underscore
- __double_leading_and_trailing_underscore__
Prescriptive: Naming Styles
Names to Avoid
다음 single character를 variable명으로 사용하지 않습니다. 일부 font에서 character와 number, lowercase와 uppercase 구분이 불가능하기 때문입니다.
- ‘l’ (lowercase letter el)
- ‘O’ (uppercase letter oh)
- ‘I’ (uppercase letter eye)
ASCII Compatibility
PEP3131에서 언급하는바와 같이 standard library에 사용하는 identifier는 반드시 ASCII와 호환되어야 합니다.
Package and Module Names
Module명은 짧게, 모두 lowercase로 명명합니다. 가독성이 좋아진다면 module명 내부에 underscore를 넣을 수 있습니다.
Python package명도 짧게, 모두 lowercase로 명명합니다. 하지만 underscore 사용은 권장하지 않습니다.
Class Names
Class명은 보통 CapWords convention을 사용합니다.
Type Variable Names
PEP484에서 소개하는 type variable명은 보통 짧은 CapWords를 사용합니다.
예) T, AnyStr, Num
Exception Names
Exception은 class이기 때문에 class naming convention을 따르며 반드시 suffix로 “Error”를 사용합니다.
Global Variable Names
Function naming convention과 동일합니다.
Function and Variable Names
Function명과 Variable명은 lowercase를 사용하며 가독성을 높힐 수 있다면 word 사이에 underscore를 사용할 수 있습니다.
Method Names and Instance Variables
Function naming convention과 동일합니다.
Constants
Constant는 module level에서 정의됩니다. 모두 capital letter로 작성하고 word 사이에 underscore를 사용합니다.
예) MAX_OVEFLOW, TOTAL
Designing for Inheritance
(생략)
Public and Internal Interfaces
(생략)
Programming Recommendations
Code는 다른 파이썬 (PyPy, Jython, IronPython, Cython, Psyco, and such) 실행에 방해를 주는 방식으로 작성하면 안됩니다.
‘None’과 같은 singleton과 비교할 때는 ‘is’ or ‘is not’을 사용합니다. Equality operator를 사용하지 않습니다.
‘is not’을 사용합니다. ‘not … is’는 사용하지 않습니다. 동일한 기능을 하지만 전자의 가독성이 더 좋고 선호되는 방식입니다.
1
2
3
4
5
## Correct:
if foo is not None:
## Wrong:
if not foo is None:
- Lambda 표현식을 활용한 assignment statement 대신에 항상 def statement를 사용합니다.
1
2
3
4
5
## Correct:
def f(x): return 2*x
## Wrong:
f = lambda x: 2*x
- Return statements는 일관성을 유지합니다. Function 내 모든 return statements가 expression을 내보내거나 혹은 모두 아무것도 내보내지 않습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
## 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)
- Prefix나 suffixe를 확인할 때 string slicing 대신 ‘ ‘.startswith()와 ‘ ‘.endswith()를 사용합니다. 더 명확하고 error 발생 가능성도 적습니다.
1
2
3
4
5
## Correct:
if foo.startswith('bar'):
## Wrong:
if foo[:3] == 'bar':
- Type을 비교할 때는 isinstance()를 사용합니다. 직접 비교하지 않습니다.
1
2
3
4
5
## Correct:
if isinstance(obj, int):
## Wrong:
if type(obj) is type(1):
- Sequence(strings, lists, tuples)를 확인할 때는 비어있는 상태가 false라는 사실을 사용합니다.
1
2
3
4
5
6
7
## Correct:
if not seq:
if seq:
## Wrong:
if len(seq):
if not len(seq):
- Boolean 값을 ‘==’ operator를 사용하요 True or False와 비교하지 않습니다.
1
2
3
4
5
6
7
8
## Correct:
if greeting:
## Wrong:
if greeting == True:
## Wrose:
if greeting is True:
Function Annotations
Function annotation은 PEP484 규칙을 따릅니다.
Variable Annotations
Variable annotation은 PEP526 에 소개되어 있습니다. Style은 위에서 설명한 function annotations와 유사합니다.
Module level의 variables, class, instance variables, 그리고 local variables의 annotations는 colon 다음에 single space를 사용해야 합니다.
Colon 전에는 space를 사용하지 않습니다.
Assignment가 오른쪽 정렬인 경우, equality sign은 양 쪽에 single space를 각각 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
## Correct:
code: int
class Point:
coords: Tuple[int, int]
label: str = '<unknown>'
## Wrong:
code:int # No space after colon
code : int # Space before colon
class Test:
retuls: int=0 # No spaces around equality sign