본문 바로가기
  • 실행력이 모든걸 결정한다
Python

[Python] 모범 코딩 전략(1) - 파이썬의 시작

by 김코더 김주역 2023. 2. 16.
반응형

1. 파이썬의 버전을 파악할 것

python --version

또는

python3 --version

- 파이썬 버전은 3이 강력히 권장된다.

 

 

 

2. PEP 8 스타일 가이드를 따를 것

- PEP은 파이썬 개선 제안이라는 파이썬 코드 작성 가이드다.

- PEP 8은 깔끔한 파이썬 코드를 작성하는 방법을 자세히 알려준다.

- 일관된 스타일을 사용하면 코드에 더 친숙하게 접근하고, 코드를 더 쉽게 읽을 수 있다.

 

1) 공백

- 탭 대신 스페이스로 들여쓰기를 할 것

- 문법적으로 중요한 들여쓰기에는 4칸 스페이스를 사용할 것

- 라인 길이는 79자 이하일 것

- 긴 식을 다음 줄에 이어서 쓸 경우에는 4칸 스페이스를 더 들여쓸 것

- 최상위 함수와 클래스 선언 사이를 구분 짓기 위해 2줄을 띄울 것

- 클래스 안에서 메소드 사이에는 1줄을 비워둘 것

- 딕셔너리에서 키와 클론(:) 사이에는 공백을 넣지 않고, 콜론 다음에 스페이스를 하나 넣을 것

- 변수 대입에서 = 전후에는 스페이스를 하나씩만 넣을 것

- 타입 표기를 덧붙이는 경우에는 변수 이름과 콜론 사이에 공백을 넣지 않고, 콜론과 타입 정보 사이에는 스페이스를 하나만 넣을 것

nums: List[int] = [1, 2, 3]

 

 

2) 명명 규약

- 함수, 변수, 속성은 소문자 스네이크식 표기법을 사용할 것 - 예) snake_case

- 보호되어야 하는 인스턴스 속성은 일반적인 소문자 스네이크식 표기법을 따르되, _(밑줄)로 시작할 것 - 예) _snake_case

- 한 클래스 내에서만 쓰여야 하는 비공개 인스턴스 속성은 일반적인 소문자 스네이크식 표기법을 따르되, __(밑줄 2개)로 시작할 것 - 예) __snake_case

- 클래스는 파스칼식 표기법을 사용할 것 - 예) PascalCase

- 모듈 수준의 상수는 대문자 스네이크식 표기법을 사용할 것 - 예) SNAKE_CASE

- 클래스의 인스턴스 메소드는 첫 번째 인자로 self를 받을 것

- 클래스 메소드는 첫 번째 인자로 cls를 받을 것

 

 

3) 식과 문

- 긍정적인 식을 부정하지 말고 부정을 내부에 넣을 것

if not a is b # (X)
if a is not b # (O)

- 반복 가능한 객체가 비어있는지 검사할 때는 길이를 0과 비교하지 말고 'if not 객체' 조건을 사용할 것

- 반복 가능한 객체가 비어있지 않은지 검사할 때는 길이가 0보다 큰지 비교하지 말고 'if 객체' 조건을 사용할 것

- if, for, while, except 복합문을 한 줄로 표현하지 않고, 명확성을 위해 각 부분을 여러 줄에 나눠 배치할 것

- 식을 한 줄 안에 다 쓸 수 없는 경우, 식을 괄호로 둘러싸고 줄바꿈과 들여쓰기를 활용할 것 (\는 권장하지 않음)

 

 

4) 임포트

- from-import문은 항상 파일의 맨 위에 위치시킬 것

- 외부에서 임포트할 때는 절대적인 이름을 사용할 것 - 예) from django.views.generic import ListView

- 다른 모듈에서 임포트해야 할 때는 명시적 상대 임포트를 이용할 것

from .models import Item # O
from models import Item # X

- import *는 과다 로딩 및 덮어 쓰기 문제가 발생할 수 있으니 지양할 것

- 임포트는 다음과 같은 순서로 섹션을 나누고, 각 섹션 안에서는 알파벳 순서로 배치할 것

  • 표준 라이브러리 모듈
  • 코어 장고 모듈
  • 장고와 무관한 외부 모듈
  • 커스텀 모듈

 

 

+ 추가 개념)

- Pylint는 파이썬 소스 코드에 대한 유명한 정적 분석기로, PEP 8 스타일 가이드를 자동으로 실행해 주고, 실수하기 쉬운 다양한 유형의 오류를 감지해준다. 여러 IDE와 에디터에서도 비슷한 기능을 제공하는 플러그인들이 있다.

- 함수나 변수 등의 이름은 함축적으로 짓지 않도록 하자.

 

 

 

3. bytes와 str를 잘 구분할 것

- bytes에는 8비트 값의 시퀀스가 들어 있고, str에는 유니코드 코드 포인트의 시퀀스가 들어있다.

- b-문자열은 bytes 형 문자열을 나타낼 때 사용된다.

print('hello'.encode()) # b'hello' 출력
print(b'hello') # b'hello' 출력

- 유니코드 데이터를 인코딩하거나 디코딩하는 부분을 인터페이스의 가장 먼 경계 지점에 위치시키자.

- 처리할 입력이 원하는 문자 시퀀스인지 확실히 하려면 다음과 같은 도우미 함수를 사용하자.

def to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value

def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str, str):
        value = bytes_or_str.encode(bytes_or_str)
    else:
        value = bytes_or_str
    return value

- bytes와 str 인스턴스를 연산자에 섞어서 사용할 수 없다. 이진 데이터 파일을 다룰 때도 이진 모드('rb'나 'wb')를 사용해야 한다.

- 파일을 다룰 때 시스템 디폴트 인코딩을 파악해두고, 필요에 따라 open 함수에 encoding 파라미터를 명시적으로 전달하자.

import locale

print(locale.getpreferredencoding()) # 시스템 디폴트 인코딩 반환

 

 

 

4. 문자열 formatting에는 f-문자열을 사용할 것

- Formatting(형식화)이란, 미리 정의된 문자열에 데이터 값을 끼워 넣어서 사람이 보기 좋은 문자열로 저장하는 것이다.

- 파이썬 3.6부터는 형식화 방식으로 인터폴레이션을 통한 f-문자열 방식이 도입되었다. 이 문법에서는 형식 문자열 앞에 f 문자를 붙여야 한다.

- f-문자열은 위치 지정자 안에 임의의 파이썬 식을 직접 포함시키는 방식이다. 그래서 다음과 같은 장점들이 나오게 된다.

  • 파라미터의 순서에 얽매이지 않음
  • 불필요한 중복을 제거할 수 있음
  • 식이 짧음
  • 가독성이 좋음
key = 'val'
value = 1.356
print(f'{key} = {value:.2f}') # val = 1.36

다음과 같이 연산자뿐만 아니라 함수도 사용할 수 있고

for i, (item, count) in enumerate(pantry)
    print(f'#{i+1}: {item.title():<10s} = {round(count)}')

함수의 파라미터도 변수로 줄 수 있다.

def add(x, y):
    return x + y

a=1
b=2
print(f'{a+b} = {add(a, b)}') # 3 = 3

그리고 다음과 같이 파이썬 식을 형식 지정자 옵션에 넣을 수도 있다.

places = 3
number = 3.14159265
print(f'{number:.{places}f}') # 3.142

- C 스타일의 형식화(%)나 str.format 방식은 가독성면에서 권장되지 않는다. 즉, f-문자열을 택하자.

 

 

 

5. 복잡한 식을 쓰는 대신 도우미 함수를 작성할 것

- 코드를 줄여쓰는 것보다 가독성을 좋게 하는 것이 더 가치 있다.

- 특히 같은 로직을 반복해 사용할 때는 반드시 도우미 함수를 사용하자.

- boolean 연산자를 식에 사용하는 것보다 if-else 식을 사용하는 편이 가독성이 좋다.

 

 

 

6. 인덱스를 사용하는 대신 대입을 사용해 데이터를 언패킹하라

- 언패킹 구문을 사용하면 한 문장 안에서 여러 값을 대입할 수 있다. 튜플, 리스트, 시퀀스, 이터러블(중첩 포함)등 다양한 패턴을 언패킹 구문에 사용할 수 있다.

item = ('apple', 'banana')
# first = item[0] # 비추천
# second = item[1] # 비추천
first, second = item
print(first, second) # apple banana

- 언패킹 구문을 활용하여 임시 변수 없이도 간단하게 두 변수를 스왑할 수 있다. 아래의 경우에는 내부적으로 이름 없는 임시 튜플이 사용된다.

a = 3
b = 4
a, b = b, a
print(a, b) # 4 3

- 반복문에서도 다음과 같이 언패킹 구문을 활용할 수 있다.

snacks = [('apple', 500), ('banana', 300), ('lemon', 400)]

for idx, (name, price) in enumerate(snacks, 1): # 1부터 시작
    print(f'#{idx}: {name}은 {price}원입니다.')

- 함수 인자(*args), 키워드 인자(**kwargs), 다중 반환 값 등에 대한 언패킹 기능도 제공된다.

 

 

 

7. range보다는 enumerate를 사용할 것

- enumerate 내장 함수는 루프 인덱스와 이터레이터의 다음 값으로 이뤄진 쌍을 넘겨준다. next 내장 함수 또는 for문을 통해 다음 원소를 가져올 수 있다.

snacks = [('apple', 500), ('banana', 300), ('lemon', 400)]

it = enumerate(snacks)
print(next(it)) # (0, ('apple', 500))
print(next(it)) # (1, ('banana', 300))

for idx, (name, price) in enumerate(snacks):
    print(idx, name, price) # 반복마다 0 apple 500, 1 banana 300, 2 lemon 400이 순서대로 출력

- enumerate의 두 번째 파라미터(start)로 어디부터 수를 세기 시작할지 지정할 수 있다. 디폴트는 0이다.

snacks = [('apple', 500), ('banana', 300), ('lemon', 400)]

for idx, (name, price) in enumerate(snacks, 1):
    print(idx, name, price) # 반복마다 1 apple 500, 2 banana 300, 3 lemon 400이 순서대로 출력

 

 

 

8. 여러 이터레이터에 동시에 접근하려면 zip을 사용할 것

- zip(x) : 동일한 개수로 이루어진 자료형을 하나씩 묶어서 zip 객체로 반환

print(list(zip([1,2,3],['a','b','c'],[7,8,9]))) # [(1, 'a', 7), (2, 'b', 8), (3, 'c', 9)]

- 예를 들어, names, counts 라는 리스트가 있다고 하면 다음과 같이 언패킹이 가능하다.

for name, count in zip(names, counts):
    ...

- 입력 이터레이터들의 길이가 서로 다르다면, zip의 경우에는 가장 짧은 이터레이터 길이까지만 튜플을 내놓는다. 그런데 이런식으로 긴 이터레이터의 뒷부분을 무시하는 것은 바람직하지 않기 때문에 itertools 내장 모듈의 zip_longest을 사용하는 것이 권장된다. zip_longest는 존재하지 않는 값이 있다면 자신에게 전달된 fillvalue로 대신하며, fillvalue의 기본값은 None이다.

 

 

 

9. 반복문 바로 뒤에 else 블록을 사용하지 말 것

- 반복문 바로 뒤에 나오는 else 블록을 사용함으로써 얻을 수 있는 표현력보다는 미래에 이 코드를 이해하려는 사람들이 느끼게 될 부담이 더 크다.

※ 반복문 바로 뒤에 나오는 else 블록은 루프가 끝나자마자 실행된다는 특징이 있다. 루프 중간에 break을 만나면 else 블록이 실행되지 않는다.

 

 

 

10. 왈러스 연산자를 사용해 반복을 피할 것

- 왈러스 연산자는 파이썬 언어에서 고질적인 코드 중복 문제를 해결하고자 파이썬 3.8 버전에서 새로 도입된 구문으로, 일반 대입문은 =을 사용하는 반면, 왈러스 연산자는 :=을 사용한다.

- 왈러스 연산자를 사용하면 조건식처럼 일반 대입문이 쓰일 수 없는 위치에서도 변수에 값을 대입할 수 있다.

- 예를 들어, 특정 변수에 함수의 결과값을 저장하는 동시에 if 조건식에도 활용하기 위해 다음과 같은 코드를 작성했다고 하자.

def add(a, b):
    return a + b


if (val=add(6, 7)) > 10:
    print("high")
else:
    print("low")

print(val)

다음과 같이 문법 에러가 발생한다.

이 때, 왈러스 연산자를 사용하면 특정 변수에 대한 대입과 검증을 동시에 수행할 수 있다.

def add(a, b):
    return a + b


if (val:=add(6, 7)) > 10:
    print("high")
else:
    print("low")

print(val)

while 조건식에서도 왈러스 연산자를 이용하면 유용한 경우가 많으니 적극 활용하도록 하자.

 

 

● 참고 자료 : 파이썬 코딩의 기술 개정 2판 - 브렛 슬라킨

 

 

 

 

반응형

댓글