코드 스타일

../_images/33907150054_5ee79e8940_k_d.jpg

If you ask Python programmers what they like most about Python, they will often cite its high readability. Indeed, a high level of readability is at the heart of the design of the Python language, following the recognized fact that code is read much more often than it is written.

파이썬 코드가 쉽게 읽히고 잘 이해되는 이유는 비교적 완벽한 코드 스타일 가이드라인과 "파이썬스러운" 이디엄 때문입니다.

뿐만 아니라 베테랑 파이썬 개발자(파이써니스타)들이 코드의 어느 부분을 지적하며 이건 "파이썬스럽지" 않아, 라고 한다면 이는 보통 파이썬의 일반적인 가이드라인을 따르지 않았다는 뜻이며, 가장 좋은 방법(명심하세요: 최고의 가독성)을 따르지 않아 코드의 의도를 표현하는데 실패했다는 뜻입니다.

어떤 경우에는 파이썬 코드의 의도를 표현하기 위한 최선의 방법을 합의하기 어려울 때도 있습니다. 하지만 이런 경우는 거의 없습니다.

일반적인 개념

명쾌한 코드

파이썬으로 어둡의 마법을 부릴 수 있다면, 가장 명쾌하고 간단한 방법을 추천합니다.

나쁜 예

def make_complex(*args):
    x, y = args
    return dict(**locals())

좋은 예

def make_complex(x, y):
    return {'x': x, 'y': y}

위의 좋은 코드 예시에서 x와 y는 호출자로부터 직접 값을 받아와 곧바로 딕셔너리로 반환합니다. 이 함수를 쓰는 개발자들은 첫 줄과 마지막 줄을 읽는 것만으로 무엇을 하는 함수인지 정확히 알 수 있습니다. 하지만 나쁜 예에서는 그렇지 않습니다.

한 줄에 한 구문

간결성과 표현성을 위해 리스트 컴프리헨션(list comprehension) 같은 복잡한 구문을 만드는 경우가 있습니다. 이렇게 한 줄의 코드에 두 개의 분리 가능한 코드를 넣는 것은 잘못된 습관입니다.

나쁜 예

print('one'); print('two')

if x == 1: print('one')

if <complex comparison> and <other complex comparison>:
    # do something

좋은 예

print('one')
print('two')

if x == 1:
    print('one')

cond1 = <complex comparison>
cond2 = <other complex comparison>
if cond1 and cond2:
    # do something

함수 인자

인자는 4가지 경로를 통해 함수로 전달될 수 있습니다.

  1. 위치 인자 는 필수입니다. 그리고 디폴트 값을 가지면 안됩니다. 위치 인자는 인자의 가장 간단한 형태이자, 함수의 의미를 완전히 담고있는 함수 자체를 전달하기도 합니다. 인자의 순서는 넣은대로 들어갑니다. 예를 들어 send(message, recipient) 혹은 point(x, y) 에서 함수를 사용하는 사람은 이 함수들이 인자를 몇 개 필요로 하는지, 어느 순서로 들어가야 하는지 기억하려고 고민할 필요가 없습니다.

위의 예시에서 함수를 호출할 때 인자명을 명시하는 것도 가능합니다. send(recipient='World',message='Hello') , point(y=2, x=1) 이렇게 하면 인자의 순서를 바꿔도 상관없습니다. 하지만 위 방법은 send('Hello', 'World') , point(1, 2) 에 비하여 가독성이 떨어지고, 코드가 쓸데없이 장황해집니다.

  1. 키워드 인자 는 필수값은 아니지만 디폴트값을 가집니다. 키워드 인자는 선택적인 파라미터로서 함수에 전달됩니다. 함수가 2개 혹은 3개 이상의 위치 인자를 가지면 여기에 무슨 값을 넣어야할지 기억하기 어렵습니다. 이 때 디폴트값을 가진 키워드 인자가 도움이 됩니다. 예를 들어 이렇게 하면 send 함수를 더 명확하게 정의할 수 있습니다. send(message, to, cc=None, bcc=None) .여기서 ccbcc 는 선택적인 값입니다. 그리고 다른 값을 넣지 않으면 None 으로 해석됩니다.

Calling a function with keyword arguments can be done in multiple ways in Python; for example, it is possible to follow the order of arguments in the definition without explicitly naming the arguments, like in send('Hello', 'World', 'Cthulhu', 'God'), sending a blind carbon copy to God. It would also be possible to name arguments in another order, like in send('Hello again', 'World', bcc='God', cc='Cthulhu'). Those two possibilities are better avoided without any strong reason to not follow the syntax that is the closest to the function definition: send('Hello', 'World', cc='Cthulhu', bcc='God').

As a side note, following the YAGNI principle, it is often harder to remove an optional argument (and its logic inside the function) that was added "just in case" and is seemingly never used, than to add a new optional argument and its logic when needed.

  1. The arbitrary argument list is the third way to pass arguments to a function. If the function intention is better expressed by a signature with an extensible number of positional arguments, it can be defined with the *args constructs. In the function body, args will be a tuple of all the remaining positional arguments. For example, send(message, *args) can be called with each recipient as an argument: send('Hello', 'God', 'Mom', 'Cthulhu'), and in the function body args will be equal to ('God', 'Mom', 'Cthulhu').

하지만 이 생성자에는 문제점이 있으니 주의하셔야 합니다. 만약 함수가 같은 속성을 가진 인자의 리스트를 받는다면 ,이 리스트는 리스트나 시퀀스의 형태를 가진 하나의 인자로 정의하는 편이 보다 명확할 때가 많습니다. 예를 들어 send 가 복수의 수신자를 가진다면 이런 식으로 명확하게 정의하는 편이 좋습니다: send(message, recipients) 로 정의하고, send('Hello', ['God', 'Mom', 'Cthulhu']) 로 호출합니다. 이 방식에서는 함수를 사용하는 사람들이 미리 수신자 명단을 리스트로 가공할 수 있고, 어느 시퀀스를 넘겨받든 이터레이터만 있으면 원하는 방식으로 풀 수 있습니다.

  1. 임의 키워드 인자 딕셔너리 는 함수에 인자를 넘기는 마지막 방식입니다. 만약 함수가 필요로 하는 인자가 몇 개가 들어올지 알 수 없으되 이름이 붙어있다면, kwargs 생성자를 사용할 수 있습니다. 함수 본문에서 kwargs 는 이름붙은 모든 인자의 딕셔너리가 되는데 이러한 인자들은 오로지 kwargs 로만 받을 수 있습니다.

비슷한 이유로 임의 인자 리스트 를 쓸 때도 조심해야합니다: 이런 강력한 테크닉은 꼭 필요한 경우에만 써야합니다. 그리고 다른 간단하고 명쾌한 생성자로도 함수의 의도를 표현하기에 충분하다면 절대 쓰면 안됩니다.

어느 인자를 위치 인자로 쓸지, 어느 인자를 선택적 키워드 인자로 쓸지, 임의 인자를 넘기는 고급 기술을 쓸지 말지를 결정하는 것은 전적으로 함수를 작성하는 프로그래머에게 달렸습니다. 지금까지의 조언이 현명하게 쓰인다면, 파이썬 함수를 이렇게 즐겁게 작성할 수 있습니다:

  • 읽기 쉽다(이름과 인자를 설명할 필요가 없다)
  • 변경하기 쉽다(새 키워드 인자를 추가해도 코드의 다른 부분을 망가뜨리지 않는다)

마법의 지팡이를 피하기

파이썬은 해커를 위한 막강한 도구입니다. 파이썬에는 어떠한 종류의 교묘한 트릭이라도 가능하게 해주는 다양한 도구들이 있습니다. 예를 들자면 이런 것들입니다:

  • 객체들이 어떻게 생성되고 초기화되었는지를 변경
  • 파이썬 인터프리터가 모듈을 임포트 하는 방법을 변경
  • It is even possible (and recommended if needed) to embed C routines in Python.

하지만 이 모든 방법은 결점이 있습니다. 그리고 목표를 향해 곧바로 나아가는 방법이 늘 더 좋습니다. 가장 중요한 문제점은 이러한 생성자를 쓰면 가독성이 엄청나게 훼손된다는 점입니다. 파이린트나 파이플레이크 같은 많은 코드 분석 툴이 이런 "마법" 코드를 파싱하지 못합니다.

우리는 파이썬 개발자들이 이러한 무한한 가능성을 알았으면 좋겠습니다. 해결불가능한 문제는 없다는 자신감을 심어주기 때문입니다. 하지만 어떻게, 특히 어느 시점에 이런 마법의 지팡이를 사용하지 말아야 하는지 를 아는 것은 대단히 중요합니다.

쿵푸의 달인처럼, 파이써니스타는 한 손가락으로 사람을 죽일 수 있지만 절대 실전에서 그것을 사용하지 않습니다.

우리는 모두 책임감 있는 사용자다

위에서 본대로 파이썬에서는 많은 트릭이 가능하지만 이런 것들은 위험할 수 있습니다. 어느 클라이언트 코드가 객체의 속성과 메소드를 오버라이드 할 수 있다는 것은 이 위험성에 대한 좋은 예시입니다. 파이썬에는 "private" 키워드가 없습니다. 이는 자바처럼 오류를 막기 위한 많은 메커니즘을 가지고 있는 방어적인 언어와는 아주 다릅니다. 이러한 철학은 다음과 같이 표현할 수 있습니다: "우리는 모두 책임감 있는 사용자다".

This doesn't mean that, for example, no properties are considered private, and that no proper encapsulation is possible in Python. Rather, instead of relying on concrete walls erected by the developers between their code and others', the Python community prefers to rely on a set of conventions indicating that these elements should not be accessed directly.

private 속성과 그 상세한 구현을 위한 코딩 컨벤션은 "내부적으로 사용하는" 모든 변수 앞에 언더스코어(_)를 붙이는 것입니다. 만약 클라이언트 코드가 이 룰을 무시하고 어떤 엘리먼트에 접근하려고 한다면 그 코드가 겪을지도 모를 모든 문제와 오용은 전적으로 클라이언트 코드의 책임입니다.

이 컨벤션을 따를 것을 추천드립니다: 클라이언트 코드에서 가져다 쓰라고 만든 메소드나 속성이 아니라면 반드시 앞에 언더스코어(_)를 붙이세요. 이렇게 하면 각 코드의 역할을 더 명확히 분리시켜줄 뿐만 아니라, 기존 코드의 수정도 쉬워집니다. 그리고 private 속성으로 표시하는 것은 언제든지 가능하지만, public 속성을 private로 바꾸기란 훨씬 더 어려운 작업이 될겁니다.

반환값

When a function grows in complexity, it is not uncommon to use multiple return statements inside the function's body. However, in order to keep a clear intent and a sustainable readability level, it is preferable to avoid returning meaningful values from many output points in the body.

다음은 함수가 값을 리턴하는 주요 케이스입니다: 함수가 정상적으로 실행되었을 때의 리턴값, 그리고 잘못된 입력 파라미터나 들어가거나 다른 이유로 인하여 함수의 계산이나 작업을 완료할 수 없어 에러가 발생하는 경우가 있습니다.

두번째 경우에서 예외를 발생시키고 싶지 않다면, None 이나 False 같이 함수를 정상적으로 수행할 수 없다는 의미의 리턴값이 필요할 것입니다. 이런 경우에는 가능한 한 일찍 부정확한 구문이 발견되었음을 알리는 편이 좋습니다. 이 방법이 함수의 구조를 매끈하게 하는데 도움이 될 것입니다: 에러 때문에 리턴하는 구문 아래의 모든 코드는 해당 함수의 주요 결과물을 계산하기 위한 조건을 만족한다고 볼 수 있습니다. 이런 경우에는 여러 개의 리턴 구문이 필요합니다.

하지만 정상적으로 코드가 돌아가는 부분에도 함수에 여러 개의 종료 지점이 있는 경우, 리턴하는 결과물을 디버깅하기 어렵습니다. 따라서 함수에 하나의 종료점만 두는 편을 추천합니다. 이렇게 하면 코드의 경로를 뽑아내기에 좋습니다. 그러니 여러 개의 종료점이 있으면 이는 곧 리팩토링이 필요한 지점일지도 모릅니다.

def complex_function(a, b, c):
    if not a:
        return None  # Raising an exception might be better
    if not b:
        return None  # Raising an exception might be better
    # Some complex code trying to compute x from a, b and c
    # Resist temptation to return x if succeeded
    if not x:
        # Some Plan-B computation of x
    return x  # One single exit point for the returned value x will help
              # when maintaining the code.

이디엄

A programming idiom, put simply, is a way to write code. The notion of programming idioms is discussed amply at c2 and at Stack Overflow.

자연스러운 파이썬 코드를 흔히들 파이써닉 스럽다고 말합니다.

이를 위한 명백한 방법(그리고 유일한 방법이라고 말하고 싶습니다)이 있지만, 파이썬 초보자에게는 이상적인 파이썬 코드 작성법이 와닿지 않을 수도 있습니다. 그러니 좋은 이디엄들을 의식적으로 숙지해야 합니다.

다음은 몇 가지 일반적인 파이썬 이디엄들입니다:

언패킹

리스트나 튜플의 길이를 알고 있다면, 언패킹을 하면서 각 엘리먼트마다 이름을 붙여줄 수 있습니다. 예를 들면 enumerate() 는 리스트 내부에 있는 각각의 아이템에 2개의 엘리먼트가 있는 튜플을 만들어줍니다:

for index, item in enumerate(some_list):
    # do something with index and item

이런 식으로 변수들을 스왑할 수도 있습니다.

a, b = b, a

중첩 언패킹도 됩니다:

a, (b, c) = 1, (2, 3)

파이썬3에서는 PEP 3132 에서 언패킹을 하는 새로운 방법이 소개되었습니다:

a, *rest = [1, 2, 3]
# a = 1, rest = [2, 3]
a, *middle, c = [1, 2, 3, 4]
# a = 1, middle = [2, 3], c = 4

사용하지 않을 변수 만들기

사용하지 않을 변수를 가져다가 어딘가에 할당하는 경우 (예를 들면 언패킹 할 경우) __ 를 사용하세요:

filename = 'foobar.txt'
basename, __, ext = filename.rpartition('.')

주석

이 안내서에서는 쓸모없는 변수를 치워버리는데 언더스코어 두 줄 "__" 을 사용할 것을 추천하지만, 다른 많은 파이썬 스타일 안내서에서는 언더스코어 한 줄 "_" 을 사용하라고 합니다. 하지만 "_" 에는 문제가 있습니다. "_" 이 일반적으로 gettext() 함수의 별칭(alias)로 쓰일 뿐 아니라, 대화형 프롬프트상에서 이전 명령어의 결과값을 가지고 있는 변수로 쓰이기 때문입니다. 그 대신 언더스코어 두 줄을 사용하면 아주 깔끔하고 편리할 뿐만 아니라, 위에서 이야기한 경우가 돌발적으로 나타나 코드를 간섭하는 리스크를 제거할 수 있습니다.

동일한 내용물을 가진 길이 N의 리스트 만들기

파이썬 리스트의 * 연산자를 사용하세요.

four_nones = [None] * 4

길이가 N인 리스트가 들어있는 리스트 만들기

리스트는 변경 가능하기 때문에, * 연산자는 (위에서처럼) N개의 리퍼런스가 있는 같은 리스트를 만들 것입니다. 하지만 아마 이렇게 만들고 싶지는 않았을 것입니다. 대신에 리스트 컴프리헨션(list comprehension)을 사용하세요: (역주: 번역이 마땅치 않아 영문 그대로 사용하였습니다. 타 번역서에서는 리스트 축약, 리스트 해석, 리스트 내장, 리스트 내포 등으로 번역되어 있습니다. )

four_lists = [[] for __ in range(4)]

리스트로 문자열 만들기

문자열을 만드는 일반적인 이디엄은 빈 문자열 변수에 str.join() 을 사용하는 것입니다.

letters = ['s', 'p', 'a', 'm']
word = ''.join(letters)

이것은 변수의 글자 의 값을 붙여줍니다. 이 이디엄은 리스트와 튜플에도 적용 가능합니다.

컬렉션에서 아이템 찾기

컬렉션에서 무언가를 찾아야 할 때가 있습니다. 두 가지 옵션을 살펴봅시다. 리스트와 셋(set)입니다:

예시 삼아 다음의 코드를 작성해봅시다:

s = set(['s', 'p', 'a', 'm'])
l = ['s', 'p', 'a', 'm']

def lookup_set(s):
    return 's' in s

def lookup_list(l):
    return 's' in l

Even though both functions look identical, because lookup_set is utilizing the fact that sets in Python are hashtables, the lookup performance between the two is very different. To determine whether an item is in a list, Python will have to go through each item until it finds a matching item. This is time consuming, especially for long lists. In a set, on the other hand, the hash of the item will tell Python where in the set to look for a matching item. As a result, the search can be done quickly, even if the set is large. Searching in dictionaries works the same way. For more information see this StackOverflow page. For detailed information on the amount of time various common operations take on each of these data structures, see this page.

이러한 성능의 차이 때문에 다음과 같은 경우에는 리스트 대신 셋(set)이나 딕셔너리(dictionary)를 사용하는 편이 좋습니다:

  • 컬렉션이 아이템을 많이 가질 경우
  • 컬렉션 안의 아이템을 반복적으로 검색할 경우
  • 중복 아이템이 없는 경우

크기가 작거나 검색을 자주 하지 않는 컬렉션의 경우에는 오히려 해시테이블을 만드는데 필요한 추가적인 시간과 메모리가 검색할 때 절약되는 시간에 비해 더 큰 경우도 많습니다.

파이썬의 선(禪)

PEP 20 으로도 알려진 파이썬 디자인의 기본 원칙

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

For some examples of good Python style, see these slides from a Python user group.

PEP 8

PEP 8 is the de facto code style guide for Python. A high quality, easy-to-read version of PEP 8 is also available at pep8.org.

읽어볼 것을 강력 추천합니다. 모든 파이썬 커뮤니티가 이 문서의 가이드라인을 준수하려고 노력합니다. 시간이 흐름에 따라 여기서 멀어지는 프로젝트도 있을겁니다. 그렇지 않은 분들은 이 추천사항을 수정해주세요.

That being said, conforming your Python code to PEP 8 is generally a good idea and helps make code more consistent when working on projects with other developers. There is a command-line program, pycodestyle (previously known as pep8), that can check your code for conformance. Install it by running the following command in your terminal:

$ pip install pycodestyle

그런 다음 파일 하나 혹은 여러 개의 파일에다 돌리면 PEP 8 위반 여부 보고서를 얻을 수 있습니다.

$ pycodestyle optparse.py
optparse.py:69:11: E401 multiple imports on one line
optparse.py:77:1: E302 expected 2 blank lines, found 1
optparse.py:88:5: E301 expected 1 blank line, found 0
optparse.py:222:34: W602 deprecated form of raising exception
optparse.py:347:31: E211 whitespace before '('
optparse.py:357:17: E201 whitespace after '{'
optparse.py:472:29: E221 multiple spaces before operator
optparse.py:544:21: W601 .has_key() is deprecated, use 'in'

Auto-Formatting

There are several auto-formatting tools that can reformat your code, in order to comply with PEP 8.

autopep8

The program autopep8 can be used to automatically reformat code in the PEP 8 style. Install the program with:

$ pip install autopep8

포맷할 파일에다가 이렇게 사용하시며 됩니다:

$ autopep8 --in-place optparse.py

--in-place 플래그를 빼면 프로그램이 콘솔에 바로 뽑아준 변경된 코드로 리뷰를 할 수 있습니다. --aggressive 플래그는 더 많은 변화를 수행하고, 여러 번 수행해서 더 많은 효과를 볼 수도 있습니다.

yapf

While autopep8 focuses on solving the PEP 8 violations, yapf tries to improve the format of your code aside from complying with PEP 8. This formatter aims at providing as good looking code as a programmer who writes PEP 8 compliant code. It gets installed with:

$ pip install yapf

Run the auto-formatting of a file with:

$ yapf --in-place optparse.py

Similar to autopep8, running the command without the --in-place flag will output the diff for review before applying the changes.

black

The auto-formatter black offers an opinionated and deterministic reformatting of your code base. Its main focus lies in providing a uniform code style without the need of configuration throughout its users. Hence, users of black are able to forget about formatting altogether. Also, due to the deterministic approach minimal git diffs with only the relevant changes are guaranteed. You can install the tool as follows:

$ pip install black

A python file can be formatted with:

$ black optparse.py

Adding the --diff flag provides the code modification for review without direct application.

컨벤션(convention)

읽기 쉬운 코드 작성을 위한 코딩 컨벤션을 소개합니다.

Check if a variable equals a constant

You don't need to explicitly compare a value to True, or None, or 0 -- you can just add it to the if statement. See Truth Value Testing for a list of what is considered false.

Bad:

if attr == True:
    print('True!')

if attr == None:
    print('attr is None!')

Good:

# Just check the value
if attr:
    print('attr is truthy!')

# or check for the opposite
if not attr:
    print('attr is falsey!')

# or, since None is considered false, explicitly check for it
if attr is None:
    print('attr is None!')

딕셔너리의 엘리먼트에 접근하기

dict.has_key() 메소드를 쓰지 마세요. 대신에 x in d 문법을 쓰거나, dict.get() 에 디폴트값을 넣어 쓰세요.

Bad:

d = {'hello': 'world'}
if d.has_key('hello'):
    print(d['hello'])    # prints 'world'
else:
    print('default_value')

Good:

d = {'hello': 'world'}

print(d.get('hello', 'default_value')) # prints 'world'
print(d.get('thingy', 'default_value')) # prints 'default_value'

# Or:
if 'hello' in d:
    print(d['hello'])

리스트를 조작하는 지름길

List comprehensions provides a powerful, concise way to work with lists.

Generator expressions follows almost the same syntax as list comprehensions but return a generator instead of a list.

Creating a new list requires more work and uses more memory. If you are just going to loop through the new list, prefer using an iterator instead.

Bad:

# needlessly allocates a list of all (gpa, name) entires in memory
valedictorian = max([(student.gpa, student.name) for student in graduates])

Good:

valedictorian = max((student.gpa, student.name) for student in graduates)

Use list comprehensions when you really need to create a second list, for example if you need to use the result multiple times.

If your logic is too complicated for a short list comprehension or generator expression, consider using a generator function instead of returning a list.

Good:

def make_batches(items, batch_size):
    """
    >>> list(make_batches([1, 2, 3, 4, 5], batch_size=3))
    [[1, 2, 3], [4, 5]]
    """
    current_batch = []
    for item in items:
        current_batch.append(item)
        if len(current_batch) == batch_size:
            yield current_batch
            current_batch = []
    yield current_batch

Never use a list comprehension just for its side effects.

Bad:

[print(x) for x in sequence]

Good:

for x in sequence:
    print(x)

Filtering a list

Bad:

Never remove items from a list while you are iterating through it.

# Filter elements greater than 4
a = [3, 4, 5]
for i in a:
    if i > 4:
        a.remove(i)

Don't make multiple passes through the list.

while i in a:
    a.remove(i)

Good:

Use a list comprehension or generator expression.

# comprehensions create a new list object
filtered_values = [value for value in sequence if value != x]

# generators don't create another list
filtered_values = (value for value in sequence if value != x)

Possible side effects of modifying the original list

Modifying the original list can be risky if there are other variables referencing it. But you can use slice assignment if you really want to do that.

# replace the contents of the original list
sequence[::] = [value for value in sequence if value != x]

Modifying the values in a list

Bad:

Remember that assignment never creates a new object. If two or more variables refer to the same list, changing one of them changes them all.

# Add three to all list members.
a = [3, 4, 5]
b = a                     # a and b refer to the same list object

for i in range(len(a)):
    a[i] += 3             # b[i] also changes

Good:

It's safer to create a new list object and leave the original alone.

a = [3, 4, 5]
b = a

# assign the variable "a" to a new list without changing "b"
a = [i + 3 for i in a]

enumerate() 를 사용하면 리스트 안의 순서를 세어 기억시켜둘 수 있습니다.

a = [3, 4, 5]
for i, item in enumerate(a):
    print(i, item)
# prints
# 0 3
# 1 4
# 2 5

enumerate() 함수는 직접 번호를 매기는 코드를 다루는 방법보다 가독성이 좋습니다.

파일에서 읽기

with open 문법으로 파일을 읽으세요. 파일을 자동으로 닫아줍니다.

Bad:

f = open('file.txt')
a = f.read()
print(a)
f.close()

Good:

with open('file.txt') as f:
    for line in f:
        print(line)

with 문은 파일을 반드시 닫아주기 때문에 더 좋습니다. 심지어 with 블럭 안에서 예외(exception)이 발생해도 그렇습니다.

줄 잇기

코드의 논리적 줄이 어느 정도를 넘어서면 몇 개의 물리적 줄로 쪼갤 필요가 있습니다. 파이썬 인터프리터는 줄의 마지막 문자가 역슬래시()일 경우 자동으로 줄을 이어줍니다. 이는 많은 경우에 도움이 되지만 망가지기 쉬운 경우가 있기에 일반적으로 피해야합니다: 역슬래시 뒤에 빈 칸(white space)가 있는 경우, 코드가 망가지고 예상치 못한 결과가 나옵니다.

A better solution is to use parentheses around your elements. Left with an unclosed parenthesis on an end-of-line, the Python interpreter will join the next line until the parentheses are closed. The same behavior holds for curly and square braces.

Bad:

my_very_big_string = """For a long time I used to go to bed early. Sometimes, \
    when I had put out my candle, my eyes would close so quickly that I had not even \
    time to say “I’m going to sleep.”"""

from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \
    yet_another_nice_function

Good:

my_very_big_string = (
    "For a long time I used to go to bed early. Sometimes, "
    "when I had put out my candle, my eyes would close so quickly "
    "that I had not even time to say “I’m going to sleep.”"
)

from some.deep.module.inside.a.module import (
    a_nice_function, another_nice_function, yet_another_nice_function)

그러나 대부분의 경우 긴 논리적 줄을 나누는 짓은 동시에 너무 많은 일을 하려고 한다는 신호입니다. 이는 가독성을 해칩니다.