함수를 정의할 때, return 값이 없는 예외적 case에 None을 반환하게 하고 함수 호출 부분에서 None 값을 다루어 프로그래밍하는 경우가 종종 있을 겁니다. 다시 말하면 None에 어떠한 의미(특별한 case)를 부여하는 것이죠.
하지만 이는 좋지 못한 방법이에요.
None을 처리하는 부분에서 안정성을 보장하지 못하기 때문입니다.
예시를 함께 보겠습니다.
PNU_MAJOR = {"CSE", "ME", "NAOE", "AM"}
def is_pnu_major(major):
if major in PNU_MAJOR:
return major
return None
is_pnu_major 함수는 parameter가 부산대학교에 있는 전공이면 parameter를 반환하고, 그렇지 않으면 None을 반환합니다.
major = 'CSE'
if is_pnu_major(major) is not None:
print(f'{major}는 부산대학교에 있는 전공입니다.')
else:
print(f'{major}는 부산대학교에 없는 전공입니다.')
# CSE는 부산대학교에 있는 전공입니다.
그리고 함수 호출부에서 이 값을 적절히 처리하도록 합니다.
이는 자연스러워 보입니다.
그렇다면 이 코드는 어떤가요?
major = 'CSE'
if not is_pnu_major(major): # cf [1] 참고
print(f'{major}는 부산대학교에 있는 전공입니다.')
else:
print(f'{major}는 부산대학교에 없는 전공입니다.')
# CSE는 부산대학교에 있는 전공입니다.
이 코드는 boolean 값을 직접 판단하는 if문이 됩니다. 그럼에도 불구하고 동일하게 작동합니다.(cf [1] 참고)
그렇기 때문에 이 부분에서 실수가 발생하기 쉽습니다.
코드를 조금만 수정해보겠습니다.
PNU_MAJOR = {"CSE" : "Computer Sciecnce",
"ME" : "Machine Engineering" ... }
def pnu_major_len(major):
if major in PNU_MAJOR:
return len(PNU_MAJOR[major]) - 1
return None
위 함수는 입력한 전공이 부산대학교에 있다면 전공명의 글자 수에서 1을 뺀 값을 반환하고, 그렇지 않으면 None을 반환하는 함수입니다.
이 함수를 사용하는 다음과 같은 코드가 있습니다.
if len_major := pnu_major_len(major):
print(f'{major} 전공의 글자 수는 {1 + len_major}')
else:
print(f'{major}는 부산대학교에 없는 전공입니다.')
만약 위 함수 pnu_major_len(major)
의 return
값이 0이면 어떻게 될까요?
major = 'K'
로 정의하고 실행하면 결과는 다음과 같습니다.
K는 부산대학교에 없는 전공입니다.
if
문에서는 0
을 False
로 판단하고 else
부분이 실행됩니다.
이는 프로그래머의 의도와 다르게 작동합니다.
이러한 실수를 방지하기 위한 방법으로 두 가지가 있습니다.
return 값을 (success, result) 튜플로 반환하는 것 입니다.
PNU_MAJOR = {"CSE" : "Computer Sciecnce",
"ME" : "Machine Engineering" ... }
def pnu_major_len(major):
if major in PNU_MAJOR:
return True, len(PNU_MAJOR[major]) - 1
return False, None
success, len_major = pnu_major_len(major)
if success:
print(f'{major} 전공의 글자 수는 {1 + len_major}')
else:
print(f'{major}는 부산대학교에 없는 전공입니다.')
이렇게 success로 함수 실행의 상태를 확인할 수 있습니다.
이 방법도 좋은 해결책이지만, 변수명을 under_var(_){cf [2] 참고} 로 생략하는 파이썬 표준관례에 의해 튜플의 첫번째 인자가 쉽게 무시될 수 있습니다.
그렇게 되면, None을 반환한 경우와 마찬가지로 실수할 가능성이 높아집니다.
_, len_major = pnu_major_len(major)
if success:
print(f'{major} 전공의 글자 수는 {1 + len_major}')
else:
print(f'{major}는 부산대학교에 없는 전공입니다.')
PNU_MAJOR = {"CSE" : "Computer Sciecnce",
"ME" : "Machine Engineering" ... }
def pnu_major_len(major):
try:
return len(PNU_MAJOR[major])
except KeyError as e:
raise ValueError('없는 학과')
major = "CSE"
try:
major_len = pnu_major_len(major)
except ValueError:
print('없는 학과')
else:
print(f'학과 명은 {major_len} 자리 입니다.')
# 학과 명은 17 자리 입니다.
major = "CSS"
...
# 없는 학과
함수의 output으로 none을 return하는 경우, 이 none 값을 잘못 해석하는 실수가 흔히 일어난다. 이를 위한 가장 좋은 해결책은 None을 반환하지 않고, Exception을 호출한 쪽으로 발생시키는 것이다.
[1] None is not False
if major := get_some_pnu_major('CSE'):
if 문에서 boolean 값을 다루는 형식으로 써도 동일하게 작동합니다.
이는 None 값이 if 문에서 False처럼 역할하기 때문입니다.
하지만 None == False인 것은 아니라는 것은 명심해야 합니다. 그렇지 않으면 코드에 혼돈을 주게 되고, 가독성과 명확성에 심각한 타격을 줍니다.
[2] 변수를 사용하지 않음을 밑줄로 표시하는 파이썬 표준 관례