betterway19함수가 여러 값을 반환하는 경우 절대로 네 값 이상을 언패킹하지 말라

김승환·2021년 7월 11일

코딩의 기술

목록 보기
10/36

언패킹 구문의 한가지 효과는 함수가 둘 이상의 값을 반환할 수 있다는 것이다.

  • ex) 악어 개체군에 대한 여러 통계를 계산한다고 하자.
  • 학어의 몸 길이 정보를 담은 list를 이용해 개체군에서 가장 긴 악어와 가장 짧은 악어의 몸 길이를 계산해야한다.
#두 값을 반환하는 것처럼 보이는 함수다.
def get_stats(numbers):
    minimum = min(numbers)
    maximum = max(numbers)
    return minimum, maximum

lengths = [63, 73, 72, 60, 67, 66, 71, 61, 72, 70]

minimum, maximum = get_stats(lengths)  # 반환 값이 두 개

print(f'최소: {minimum}, 최대: {maximum}')

최소: 60, 최대: 73

# 더 간단한 튜플 두 변수를 언패킹한 예시
first, second = 1, 2
assert first == 1
assert second == 2

print(f' first는 {first}')
print(f' second는 {second}')

def my_function():
    return 1, 2

first, second = my_function()
assert first == 1
assert second == 2

print(f' first는 {first}')
print(f' second는 {second}')

first는 1
second는 2
first는 1
second는 2

  • 별표식을 이용한 여러 값을 반환 받는 예시
  • ex) 각 악어 개체의 몸 길이가 전체 개체군의 몸 길이 평균에 비해 얼마나 큰지 계산하는 함수가 필요할 때
    - 비율로 이뤄진 list를 반환하지만, 별표 식을 사용해 이 list의 중간 부분을 한꺼번에 받으면 평균 몸길이와 비교해 가장 긴 억어와 가장 짧은 악어의 비율을 알 수 있다.
# 별표식을 이용하여 평균으로 부터의 비율계산.
lengths = [63, 73, 72, 60, 67, 66, 71, 61, 72, 70]

def get_avg_ratio(numbers):
    average = sum(numbers) / len(numbers)
    scaled = [x / average for x in numbers]
    scaled.sort(reverse=True)
    return scaled

longest, *middle, shortest = get_avg_ratio(lengths)
print(f'최대 길이: {longest:>4.0%}')
print(f'최소 길이: {shortest:>4.0%}')

최대 길이: 108%
최소 길이: 89%

  • 프로그램 요구 사항이 몸 길이의 평균, 중앙값, 악어 개체군의 개체 수까지 요구하는 것으로 바뀌었다고 하자.
  • 위 get_stats를 응용하여 통계를 계산하고 결과를 튜플로 반환해 호출하는 쪽에서 값을 언패킹하게 할 수 있다.
def get_stats(numbers):
    minimum = min(numbers)
    maximum = max(numbers)
    count = len(numbers)
    average = sum(numbers) / count
    sorted_numbers = sorted(numbers)
    middle = count // 2
    if count % 2 == 0:
        lower = sorted_numbers[middle - 1]
        upper = sorted_numbers[middle]
        median = (lower + upper) / 2
    else:
        median = sorted_numbers[middle]

    return minimum, maximum, average, median, count

minimum, maximum, average, median, count = get_stats(lengths)

print(f'최소 길이: {minimum}, 최대 길이: {maximum}')
print(f'평균: {average}, 중앙값: {median}, 개수: {count}')

최소 길이: 60, 최대 길이: 73
평균: 67.5, 중앙값: 68.5, 개수: 10

# 위 코딩의 문제점은 
# 1. 모든 변환 값이 수이기 때문에 순서를 혼동하기 쉽다.
# 이런 실수는 나중에 알아내기 어려운 버그를 만든다.

#아래는 실수 했을 때 예시

minimum, maximum, average, median, count = get_stats(
    lengths)
print(f'최소 길이: {minimum}, 최대 길이: {maximum}')
print(f'평균: {average}, 중앙값: {median}, 개수: {count}')

#평균과 중앙값이 바꼈다.
minimum, maximum, median,average, count = get_stats(lengths)
print(f'최소 길이: {minimum}, 최대 길이: {maximum}')
print(f'평균: {average}, 중앙값: {median}, 개수: {count}')

최소 길이: 60, 최대 길이: 73
평균: 67.5, 중앙값: 68.5, 개수: 10
최소 길이: 60, 최대 길이: 73
평균: 68.5, 중앙값: 67.5, 개수: 10

# 두번째는 함수를 호출하는 부분과 반환 값을 언패킹하는 부분이 길고, 여러 방법으로 줄을 바꿀수 있어 가독성이 떨어진다.

(minimum, maximum, average,
 median, count) = get_stats(lengths)

(minimum, maximum, average, median, count
    ) = get_stats(lengths)
  • 이런 문제를 피하려면 언패킹할 때 4개 이상 사용하면 안된다.
  • 이보다 많은 언패킹 상황이라면 경량 클래스(lightweight class)나 namedtuple을 사용하는 것이 낫다.
profile
인공지능 파이팅!

0개의 댓글