ChangHyeon Nam's Blog notes and thoughts

slicing 보다는 나머지를 모두 잡아내는 unpacking을 사용하라

Comments

[Effective Python] Better way 13: slicing 보다는 나머지를 모두 잡아내는 unpacking을 사용하라

  • 언패킹 대입에 별표식을 사용하면 대입되지 않는 모든 부분을 리스트에 잡아낼 수 있다.
  • 슬라이싱과 인덱스보다 실수할 여지가 더 적다.

  • 기본 언패킹의 한계는 언패킹할 시퀀스의 길이를 미리 알고 있어야 한다는 것이다.

      car_ages =[0,9,4,5,2,3]
      car_ages_descending = sorted(car_ages,reverse=True)
      oldest,  second_oldest = car_ages_descending
      # Traceback (most recent call last):
      # ValueError: too many values to unpack (expected 2)
    

    위의 코드와 같이 언패킹만을 이용해서 앞의 두개 원소를 가져오려고 하면 에러가 발생한다.

  • 처음 파이썬을 배운 사람들은 인덱스와 슬라이싱을 위와 같은 상황에 사용한다.

      oldest = car_ages_descending[0]
      second_oldest = car_ages_descending[1]
      others = car_ages_descending[2:]
      print(oldest,second_oldest,others)
      # 9 5 [4, 3, 2, 0]
    

    하지만 가독성이 떨어진다. 그리고 이런식으로 원소를 여러 집합으로 나눌 시에 1 차이 나는 인덱스로 인한 오류(off-by-one error)가 발생할 수 있다.

  • 그래서 파이썬에서는 별표식(starred expression)을 사용해 모든 값을 담는 언패킹을 할 수 있게 지원한다.

      oldest, second_oldest, *others = car_ages_descending
      print(oldest,second_oldest,others)
      # 9 5 [4, 3, 2, 0]
    
  • 별표식은 중간 혹은 처음에 올 수있다.

      oldest, *others,  youngest = car_ages_descending
      print(oldest,others,youngest)
      # 9 [5, 4, 3, 2] 0
    
      *others, second_youngest, youngest = car_ages_descending
      print(others,second_youngest,youngest)
      # [9, 5, 4, 3] 2 0
    
  • 별표식이 포함된 언패킹 대입을 처리하려면 필수인 부분이 적어도 하나 필요하다.

      *others = car_ages_descending
      # SyntaxError: starred assignment target must be in a list or tuple
    
  • 또한 한 수준의 언패킹 패턴에서 별표식을 두 개 이상 사용할 수 없다.

      first, *middle, *second_middle, last = car_ages_descending
      # SyntaxError: two starred expressions in assignment
    
  • 하지만 여러 계층으로 이뤄진 구조를 언패킹할 때는 서로 다른 부분에 포함되는 한, 별표식을 여러번 사용해도 된다.

      car_inventory = {'시내':('그랜저','아반떼','티코'),
      '공항':('제네시스 쿠페','소나타','K5','엑센트')}
      ((loc1,(best1,*rest1)),(loc2,(best2,*rest2))) = car_inventory.items()
      print(f'{loc1}에서 최고의 차는 {best1}, 나머지는 {rest1}')
      # 시내에서 최고의 차는 그랜저, 나머지는 ['아반떼', '티코']
    
  • 별표식은 항상 list 인스턴스가 되므로, 시퀀스에 남는 원소가 없으면 별표 식 부분은 빈 리스트가 된다.

      sort_list = [1,2]
      first,second, *rest = sort_list
      print(first,second,rest)
      # 1 2 []
    

    이런 특징은 원소가 최소 N개 들어 있다는 사실을 미리 아는 시퀀스를 처리할 때 편리하다.

  • csv 파일을 생성하는 다음 코드가 있다고 하자. csv파일은 header와 row로 구성되어 있다.

      def generate_csv():
          yield ('날짜', '제조사' , '모델', '연식', '가격')
          for i in range(100):
              yield ('2019-03-25', '현대', '소나타', '2010', '2400만원')
              yield ('2019-03-26', '기아', '프라이드', '2008', '1400만원')
    
    

    다음 제네레이이터의 결과를 별표식으로 언패킹하면 iterator의 내용 중에 첫번째(헤더)와 나머지를 쉽게 나눠서 처리할 수 있다.

      it = generate_csv()
      header, *rows = it
      print('CSV 헤더:', header)
      print('행 수: ', len(rows))
      # CSV 헤더: ('날짜', '제조사', '모델', '연식', '가격')
      # 행 수:  200
      # CSV 헤더: ('날짜', '제조사', '모델', '연식', '가격')
      # 행 수:  200
    

    하지만 별표식은 항상 리스트를 만들어 내기 때문에 이터레이터를 별표식으로 언패킹하면 컴퓨터 메모리를 모두 다 사용해서 프로그램이 멈출 수 도 있다.