본문 바로가기

더 나은 엔지니어가 되기 위해/파이썬을 파이썬스럽게

슬기로운 파이썬 트릭 1 - 파이썬 코드를 정돈하기 위한 패턴

이 글은 슬기로운 파이썬 트릭을 읽고 핵심만 빠르게 정리한 글이다.
더 나은 파이썬 개발자가 되기 위해, 파이썬다운 코드에 대한 내용을 다룬다.
1장은 소개 글이라, 1장은 생략하고 2장부터 시작한다. 


2.1. assert 문으로 방어하기

파이썬의 단언문은 프로그램 내부 자체 검사로 조건을 테스트하는 디버깅 도구다.

def apply_discount(product, discount):
    price = int(product['price'] * (1 - discount))
    assert 0 <= price <= product['price']  # 조건이 거짓이면 AssertionError 를 일으킨다.
    return price

단언문은 개발자가 버그를 식별하는데 도움이 되지만, 런타임 에러를 처리하기 위한 메커니즘은 아니다.

# 아래 같이 AssertionError 를 예외 처리하면 안된다.
try:
    apply_discount(product, discount)
except AssertionError:
    pass

따라서 데이터 유효성 검증에 단언문을 사용하지 말자.

인터프리터 설정으로 단언문을 전역적으로 비활성화될 수 있다.

# 아래와 같이 단언문을 유효성 검증에 사용하면 안된다.
def delete_product(product_id, user):
    assert user.is_admin(), "Must be admin"  # AssertionError 와 동시에 메시지를 출력한다.
    assert store.has_product(product_id), "Unknown product"
    store.get_product(product_id).delete()
# 유효성 검증에는 아래와 같이 일반적인 if 문을 사용하자.
def delete_product(product_id, user):
    if not user.is_admin():
        raise AuthError("Must be admin to delete")
    if not store.has_product(product_id):
        raise ValueError("Unknown product id")
    store.get_product(product_id).delete()

항상 참이 되는 단언문은 피하자.

이는 프로그래머의 실수다.

# tuple 에 값이 들어있으면 항상 True 로 판명된다.
assert (1 == 2, "This should fail")
# 위 코드의 의도대로라면, 아래와 같이 수정되어야 한다.
assert 1 == 2, "This should fail"

2.2. 보기 좋은 쉼표 배치

Bad Case

names = ['Alice', 'Bob', 'Dilbert' ]
names = [
    'Alice',
    'Bob',
    'Dilbert'
]

Good Case

names = [
    'Alice',
    'Bob',
    'Dilbert',
]
  • 예상치 못한 파이썬의 문자열 리터럴 결합을 피할 수 있다.
  • git diff 등으로 수정된 코드를 확인할 때, 변경된 부분만 빠르게 확인 가능하다,.

2.3. 컨텍스트 매니저와 with 문

Bad Case

some_lock = threading.Lock()
some_lock.acquire()
try:
    pass  # 뭔가를 한다.
finally:
    some_lock.release()

Good Case

with some_lock:
    pass  # 뭔가를 한다.
  • with 문은 표준적인 try / finally 문 사용을, 이른바 컨텍스트 매니저에 캡슐화하여 예외 처리를 단순하게 한다.
  • with 문은 주로 시스템 리소스의 안전한 획득 및 해제를 관리하는데 사용한다.
  • with 문을 효과적으로 사용하면 리소스가 새는 것을 방지하고 코드를 읽기 쉽게 만들 수 있다.

2.4. 밑줄 문자와 Dunder

_var

내부적인 사용을 위한 것이다. 파이썬 인터프리터가 강제하지 않고, 프로그래머에게 명시적인 힌트로 작용한다.

class Test:
    def __init__(self):
        self.foo = 11
        self._bar = 23  # 외부애서 이 변수는 사용하지 말 것을 표현한다.

var_

관례에 따라 파이썬 키워드와의 이름 충돌을 방지한다.

def make_object(name, class_):  # 파이썬 키워드 변수로 등록되어 있는 class 와 이름 충돌을 방지한다.
    pass

__var

클래스에서 사용될 때, 파이썬 인터프리터에 의해 네임 맹글링이 적용된다.

class Test:
    def __init__(self):
        self.__baz = 42  # 이 속성에 실제로 접근하려면 _Test_baz 로 접근해야 한다.
>>> test = Test()
>>> test.__baz
AttributeError: 'Test' object has no attribute '__baz'
>>> test._Test__baz
42

__var__

파이썬에서 정의한 특별한 메서드 (매직 메서드) 다. 프로그래머는 이 명명법을 사용하면 안 된다.

class Test:
    # 아래와 같이 __로 시작하는 메소드는 파이썬에서 사용하는 특수한 메서드들이다.
    def __init__(self):
        pass
    def __enter__(self):
        pass

_

때때로 임시 변수나 중요하지 않은 변수의 이름으로 사용된다.
파이썬 REPL 세션에서 마지막 표현식의 결과를 나타내기도 한다.

for _ in range(32):  # 반복문을 돌 때, _ 는 전혀 사용되지 않는다. (이름을 _ 으로 붙인 이유다)
    print("Hello, World")

2.5. 문자열 형식화

"구식" 문자열 형식화

>>> name = "heumsi"
>>> "Hello, %s" % name
'Hello, heumsi'

"신식" 문자열 형식화

>>> name = "heumsi"
>>> "Hello, {name}".format(name=name)
'Hello, heumsi'

리터럴 문자열 (파이썬 3.6 이상)

>>> name = "heumsi"
>>> f"Hello, {name}"
'Hello, heumsi'

템플릿 문자열

>>> from string import Template
>>> name = "heumsi"
>>> t = Template("Hello, $name")
>>> t.substitute(name=name)
'Hello, heumsi'

사용 팁

  • 형식 문자열이 사용자가 제공한 것인 경우 템플릿 문자열을 사용하여 보안 문제를 방지하자.
  • 그렇지 않고 파이썬 3.6 이상인 경우 리터럴 문자열을 사용하자.
  • 파이썬 3.6 미만인 경우, "신식" 문자열 형식화를 사용하자.

2.6. 파이썬의 선

늘 마음에 두고 돌아봐야 할 것. import this 로 확인할 수 있다.

파이썬의 선, 팀 피터

아름다운 코드는 지저분한 코드보다 낫다.
명확한 코드는 암시적인 코드보다 낫다.
단순한 코드가 복잡한 코드보다 낫다.
복잡한 코드가 난해한 코드보다 낫다.
단조로운 코드가 복잡한 코드보다 낫다.
읽기 쉬운 코드는 읽기 어려운 코드보다 낫다.
가독성은 중요하다.
규칙을 깰 정도로 특별한 경우란 없다.
하지만 실용성은 이상을 능가한다.
에러를 결코 조용히 넘어가지 않도록 한다.
명시적으로 조용히 넘어가라고 하더라도 조용히 넘어가지 않는다.
모호한 코드를 대면할 때마다 추측하고 싶은 유혹을 거절하라.
문제를 해결할 단 하나의 명확하고 바람직한 방법이 있을 것이다. 
하지만 처음에 코딩을 할 때는 잘 모를 수 있기에 코드의 동작 방법을 정확히 알지 못 할 수 있다.
아무 것도 안하는 것보다 지금 하는 게 낫다.
하지만 아무 것도 하지 않는 것이 지금 당장 하는 것보다 나을 수도 있다.
설명하기 어려운 구현이라면 좋은 아이디어는 아니다.
쉽게 설명할 수 있는 구현이라면 좋은 아이디어일 것이다.
네임스페이스는 매우 훌륭한 아이디어이다. 많이 사용하자