Skip to content

Latest commit

 

History

History
155 lines (125 loc) · 4.08 KB

BetterWay24.md

File metadata and controls

155 lines (125 loc) · 4.08 KB

24. None 과 독스트링을 사용해 동적인 디폴트 인자를 지정하라

1. 독스트링

  • Documentation String
  • 보통 함수나 모듈 설명을 기록할 때 사용
  • 사용법
    • """ """
    • ''' '''
def log(message, when=None):
    """메시지와 타임스탬프를 로그에 남긴다.

    Args:
        message: 출력할 메시지.
        when: 메시지가 발생한 시각(datetime).
              디폴트 값은 현재 시간이다.
    """
    if when is None:
        when = datetime.now()
    print(f'{when}: {message}')

2. 잘못된 디폴트 인자 사용법

from time import sleep
from datetime import datetime

def log(message, when=datetime.now()):
    print(f'{when}: {message}')

log('안녕!')
sleep(0.1)
log('다시 안녕!')

>>>
2020-08-18 11:29:12.857588: 안녕!
2020-08-18 11:29:12.857588: 다시 안녕!
  • 원래 의도는 log 함수 호출 시 when 값에 항상 datetime.now() 가 호출되어 동적으로 할당하는 것 이었음
  • 그러나 디폴트 인자는 load될 때 한 번만 evaluation
  • 해당 예제의 시간 값은 항상 같음

3. 개선 방법

  • 디폴트 값으로 None 지정
  • 실제 동작을 독스트링으로 기록
  • 변수가 None인지를 체크하여 값 할당하면 됨
def log(message, when=None):
    """메시지와 타임스탬프를 로그에 남긴다.

    Args:
        message: 출력할 메시지.
        when: 메시지가 발생한 시각(datetime).
              디폴트 값은 현재 시간이다.
    """
    if when is None:
        when = datetime.now()
    print(f'{when}: {message}')

log('안녕!')
sleep(0.1)
log('다시 안녕!')

>>>
2020-08-18 12:06:27.168336: 안녕!
2020-08-18 12:06:27.274338: 다시 안녕!
  • when 변수는 디폴트 값이 None 이며 독스트링에 when 디폴트 값의 할당을 설명하였음
  • 또한 if 절을 통해 함수 호출로 할당하도록 함
import json
def decode(data, default={}):
    try:
        return json.loads(data)
    except ValueError:
        return default

foo = decode('잘못된 데이터')
foo['stuff'] = 5
bar = decode('또 잘못된 데이터')
bar['meep'] = 1
print('Foo:', foo)
print('Bar:', bar)

>>>
Foo: {'stuff': 5, 'meep': 1}
Bar: {'stuff': 5, 'meep': 1}
  • decode 함수의 default 변수의 디폴트 값은 비어있는 dictionary
  • decode 호출로 foo 엔 비어있는 dictionary 를 할당하였고 그후 stuff 키에 따른 값 5 가 추가되었음
  • 두번째 decode 호출로 비어 있는 dictionary 를 할당하고 이후 meep 키에 따른 값 1 이 추가되었을까?
    • 아니다
    • dictionarycall by reference 이므로 foobar 는 같은 객체를 가리킴
    • 그러므로 dictionary 는 값이 업데이트 되며 공유됨
  • 해결방안
    • None 디폴트 값, 독스트링, 함수 실행될 때마다 dictionary 새로 할당
def decode(data, default=None):
    """문자열로부터 JSON 데이터를 읽어온다

    Args:
        data: 디코딩할 JSON 데이터.
        default: 디코딩 실패 시 반환할 값이다.
            디폴트 값은 빈 딕셔너리다.
    """
    try:
        return json.loads(data)
    except ValueError:
        if default is None:
            default = {}
        return default

foo = decode('잘못된 데이터')
foo['stuff'] = 5
bar = decode('또 잘못된 데이터')
bar['meep'] = 1
print('Foo:', foo)
print('Bar:', bar)
assert foo is not bar

>>>
Foo: {'stuff': 5}
Bar: {'meep': 1}
  • type annotation 을 사용한 방법
    • Optional 을 사용했으므로 when의 값은 datetime 혹은 None
from typing import Optional

def log_typed(message: str,
              when: Optional[datetime]=None) -> None:
    """메시지와 타임스탬프를 로그에 남긴다.

    Args:
        message: 출력할 메시지.
        when: 메시지가 발생한 시각(datetime).
            디폴트 값은 현재 시간이다.

    """
    if when is None:
        when = datetime.now()
 print(f'{when}: {message}')