python - 패킹과 언패킹(packing & unpacking)
패킹과 언패킹
패킹과 언 패킹은 단어의 뜻에서 알 수 있듯이 '싸다'와 '풀다'이다. 파이썬은 데이터를 변수에 대입할 때 독특한 방법을 사용할 수 있다. 간단한 예시를 통해 알아봅시다.
numbers = 1,2,3,4,5 # 패킹
# 원래는 튜플을 나타내는 괄호를 사용해야하지만 생략하는 경우가 더 많다.
# numbers = (1,2,3,4,5)
print(numbers)
> [1,2,3,4,5]
a,b,c,d,e = numbers # 언패킹
print(a,b,c,d,e)
> 1 2 3 4 5
내가 아는 자바나 자바스크립트에서는 본적이 없는 코드입니다.
패킹과 언패킹은 언더바를사용하여 불필요한 값을 언더바로 사용하여 생략할 수도 있습니다.
numbers = 1,2,3,4,5
a,_,_,d,e = numbers
print(a,d,e)
> 1 4 5
또한 여러개의 값을 뽑아서 따로 받을 수도 있습니다.
# 이 부분은 언패킹이라기보단 슬라이싱에 해당합니다.
numbers = [1,2,3,4,5]
a = numbers[0]
b = numbers[1]
rest = numbers[2:]
print(a,b,rest)
> 1 2 [3,4,5]
# 언패킹을 할 때 나머지 부분을 한꺼번에 담을 수 있습니다.
a,b,*rest = numbers
print(a,b,rest)
> 1 2 [3,4,5]
*rest,c,d,e = numbers
print(rest,c,d,e)
> [1,2] 3 4 5
a,*rest,e = numbers
print(a,rest,e)
> 1 [2,3,4] 5
# 이 예제는 튜플로도 가능합니다.
# 튜플로는 다른형식으로 보겠습니다.
a,b,c = (1,2,3)
# 괄호를 생략해도 됩니다.
d,e,f = 4,5,6
print(a,b,c,d,e,f)
> 1 2 3 4 5 6
파라미터 언패킹
def date_to(y,m,d):
return str(y) + '.' + str(m) + '.' + str(d)
# 인자를 각각 전달
print(date_to(2000,2,2))
> '2000.2.2'
# 인자를 튜플로 전달
print(date = (2000,2,2))
date_to(date)
> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: date_to() missing 2 required positional arguments: 'm' and 'd'
이번에는 예제를 먼저 보겠습니다. 세개의 인자를 받는 함수를 만들었습니다. 인자를 받아 각각의 인자들은 나누어 져서 각자의 위치에 들어가 반환이됩니다. 첫번째로 인자를 따로따로 넣은 것은 문제 없이 잘 출력이 되었습니다. 그러나 두번째 같은 데이터를 튜플 형식으로 만들어서 넣었을 때에는 예상했던 것과 같이 에러가 발생합니다. 튜플형식의 '한개'의 변수를 인자로 넣어서 첫번쨰 인자만 받았다고 인식하고 두번쨰 세번째 인자는 받지 못했다는 에러 메시지 입니다.
이 경우에는 위에서 정리한 언패킹을 통해 해결할 수있습니다. 아스트릭(*)기호를 이용하여 튜플형식의 데이터를 인자로 받을 때 풀러주는(언패킹) 것입니다. 즉, 튜플에 있는 데이터를 풀러서 인자로 나열해서 넣어주는 것입니다.
def date_to(y,m,d):
return str(y) + '.' + str(m) + '.' + str(d)
# 튜플로 전달 - 언패킹
date = (2000,2,2)
print(date_to(date))
> '2000.2.2'
언패킹으로 인자를 넣어줄 때에는 다양한 방식으로 할 수 있습니다.
def date_to(y,m,d):
return str(y) + '.' + str(m) + '.' + str(d)
# 튜플 뿐만아니라 시퀀스인 리스트도 가능하다
date_list = [2000,2,2]
print(date_to(*date_list))
> '2000.2.2'
# 부분적으로 나누어서도 가능하다
date = (2,2)
print(date_to(2000,*date))
> '2000.2.2'
파라미터 패킹
시퀀스를 풀어서 인자에 할당하는 것과 반대로 함수에서 고정되지 않은 여러 개의 데이터를 묶어 하나의 시퀀스 매개변수에 전달받을 수 있습니다. 여러개의 데이터를 콤마로 구분해 전달받는 print() 함수가 이 기법을 활용한 예입니다.
데이터를 묶어 전달받는 함수를 정의하기 위해서는 매개변수 목록에서 데이터 묶음을 전달받을 변수를 하나 정해 이름 앞에 별 기호를 붙입니다. 이 변수의 이름은 자유롭게 지을 수 있지만 인자들을 의미하는 args 라는 이름이 자주 사용됩니다.
def avg(*args):
if len(args) == 0:
return None
return sum(args) / len(args)
print(avg(1,2,3,4,5))
> 3.0
그러나 인자를 받을 때 위와 같이 각각을 받는 경우보다는 시퀀스로 받는 경우가 더 많습니다. 그렇다면 시퀀스로 받게되면 어떻게 되는지 알아봅시다.
def avg(*args):
print(args)
print(*args)
return sum(args) / len(args)
num = [1,2,3,4,5]
print(avg(num))
> ([1, 2, 3, 4, 5],)
> [1, 2, 3, 4, 5]
> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in avg
TypeError: unsupported operand type(s) for +: 'int' and 'list'
정리를 하면서도 헷갈리는 부분이 많아서 디버깅을 해보았습니다.
함수를 정의하는부분
1. 인자를 한 개 이상 받을 경우 '튜플' 형식으로 들어옵니다.
1-1. 인자에 아스트릭(*)을 사용하게 될 경우 인자는 임의 숫자의 튜플을 허용합니다.
1-2. 받은 인자는 콤마 단위로 튜플에 한개 씩 넣습니다.
(위에서 본 것과 같이 리스트를 넣었는데 통채로 하나의 튜플 값이됩니다.)
함수를 사용하는 부분
2. 인자를 한 개 이상 넣을 경우 튜플 형식으로 들어갑니다.
2-1. 인자에 아스트릭을 넣을 경우 시퀀스여야 하고 시퀀스의 값들이 하나씩 풀려서 들어갑니다.
(풀린 인자는 정의된 함수 인자부분에서 각각의 튜플 구성원으로 들어갑니다.)
때문에 위에 에러를 해결하는 방법은 두가지가 있습니다.
# 함수를 사용하는부분에서 해결
# 1. 호출 : *num -> 1,2,3,4,5 (언패킹)
# 2. 실행 : 1,2,3,4,5 -> *args -> (1,2,3,4,5) (패킹)
# 언패킹을 하지 않을 경우
# 1. 호출 : num -> [1,2,3,4,5]
# 2. 실행 : [1,2,3,4,5] -> *args -> ([1,2,3,4,5],) (통채로 하나의 값)
def avg(*args):
return sum(args) / len(args)
num = [1,2,3,4,5]
# 함수를 호출하는 부분에서 시퀀스를 풀러준다(언패킹)
# 그러면 함수를 실행할 때 언 패킹된 각각의 풀러진 시퀀스를
# 하나하나를 튜플의 값으로 받는다.
print(avg(*num))
> 3.0
두번째 방법은 함수를 정의 할 때 안에서 해결하는 것입니다.
# 함수를 실행하는 부분에서 해결
# 1. 호출 : num -> [1,2,3,4,5]
# 2. 실행 : [1,2,3,4,5] -> *args -> ([1,2,3,4,5],)
# 3. 함수 내 사용 : ([1,2,3,4,5],) -> sum(*args) (언패킹) -> sum([1,2,3,4,5]), len([1,2,3,4,5])
def avg(*args):
return sum(*args) / len(*args)
num = [1,2,3,4,5]
print(avg(num))
> 3.0
키와 값을 가진 매개변수 매핑
자료구조에는 순서를 가지고 있는 시퀀스도 있지만 키와 값으로 데이터를 가지고 있는 구조도 있습니다. 이때 넣어주는 자료구조의 이름과 인자값의 이름을 매핑하여 사용할 수 있습니다. 여기에서도 패킹과 언패킹 개념이 똑같이 들어가고 다른점은 '시퀀스'(인자의 순서)를 가지고 찾아내는 것이 아니라 '이름'을 통해 찾습니다.
# 키와 값을 가지는 데이터셋을 받을 때에는 아스트릭 두개(**)를 사용한다.
def date_to(**kwargs):
return str(kwargs['y']) + '.' + str(kwargs['m']) + '.' + str(kwargs['d'])
date = {'y':2000, 'm':2, 'd':2}
print(date_to(**date))
> '2000.2.2'