Python Packing & Unpacking - 파이썬 *, ** 사용법

# 목적

파이썬의 Packing & Unpacking 의 의미와 사용법 대해 정리를 하려고 한다. 
산술 연산자로써의 *가 아닌 Packing & Unpacking을 위한 사용법을 살펴본다.

우선 * 을 하나 사용하는 Packing과 Unpacking을 살펴보고, 두개 사용하는 경우를 살펴볼 예정이다.


# Packing - *

Packing은 함수에 전달할 인자의 개수를 유연하게 하기 위한 방법이다.

sum_all(1, 2, 3, 4, 5)
sum_all(1, 2, 3, 4, 5, 6, 7)

Packing을 사용하지 않고 위 코드를 정상적으로 동작하기 위한 함수는 아래와 같다.

def sum_all(a1=None, a2=None, a3=None, a4=None, a5=None, a6=None, a7=None):
    result = 0
    if a1:
        result += a1
    if a2:
        result += a2
    if a3:
        result += a3
    if a4:
        result += a4
    if a5:
        result += a5
    if a6:
        result += a6
    if a7:
        result += a7
    return result

이렇게 매개변수를 여러개 사용하면 보기에도 좋지 않고 확장성에는 더더욱 안좋다.

 

위와 같은 함수를 선언하지 않기 위해 인자의 개수가 변동될 상황에 대처하도록 가변 인자를 처리할 수 있다.

 

이를 해결하는 방법이 Packing이고 매개변수 앞에 *을 붙임으로써 가변 인자들을 하나로 묶어서 함수에 전달해줄 수 있다.

 

*을 하나면 사용하게 되면 위의 예시와 같은 위치 인자들을 묶어주게된다.

매개변수와 매칭이되는 키워드 인자들의 Packing은 이후 살펴본다.

 

가변 인자를 받도록 Packing을 사용한 함수는 아래와 같다.

def sum_all_2(*numbers):
	result = 0
	for number in numbers:
		result += number
	return result

실행 결과

sum_all 및 sum_all_2 함수의 결과는 동일하지만, sum_all_2 함수를 사용하는 것이 깔끔하고 확장성이 좋다.

추가로, sum_all_2 함수의 매개변수 명으로 numbers를 사용했지만, 일반적으로 *args으로 사용한다(공식 문서에서 정의한 컨벤션).

 

매개변수 앞에 *을 붙인다고 어떤 점이 달라지는지를 추가적으로 확인해보자.

def check_not_packing(a1=None, a2=None, a3=None):
    print(type(a1))
    print(type(a2))
    print(type(a3))

def check_packing(*numbers):
	print(numbers)
	print(type(numbers))
	for number in numbers:
		print(number)
		print(type(number))

check_not_packing(1, 2, 3)
check_packing(1, 2, 3)

타입을 확인한 결과

각 함수에서 매개변수로 사용되는 모든 변수들의 타입을 확인한 결과이다.

check_packing 함수의 결과를 확인하면 사용된 3개 인자들을 tuple로 묶어서 함수로 전달된다.


이 동작을 Packing이라고 한다.


# Unpacking - *

앞서 나열된 인자들을 튜플로 묶어주는 Packing을 확인했는데, 반대로 Tuple이나 List로 묶여 있는 변수들을 풀어주는 방법도 존재한다.

 

이 과정을 Unpacking이라고 부른다.

 

앞서 정의한 두개의 함수를 살짝 고친 아래 함수를 확인해보자.
아래의 상황처럼 전달해야하는 인자가 정수형 변수들이 아니라 Tuple이나 List일 경우 매개변수를 어떻게 정의해야할까? 아래 코드가 동작할까?

def not_packing(a1=None, a2=None, a3=None):
	print(a1, a2, a3)

def packing(*numbers):
	for number in numbers:
		print(number, end=" ")
	print()

not_packing([1, 2, 3])
packing([1, 2, 3])

두 함수의 실행 결과는 위와 같다.
원하는 실행 결과는 "1 2 3" 인데 List가 그대로 들어간 것을 확인할 수 있다.

두 함수에서 [1, 2, 3]을 인자로 넣어주고 "1 2 3"이 출력되도록 하기 위해선, [1, 2, 3]를 Unpacking해야한다.

 

Unpacking을 위해선 인자(List) 앞에 *을 붙이면 된다.

not_packing(*[1, 2, 3])
packing(*[1, 2, 3])

위와 같이 Tuple이나 List 같은 자료형을 풀어주는 과정을 Unpacking이라고 한다.


# Packing - **

*을 하나 사용하는 Packing에 이어서 두개를 사용한 Packing을 살펴보자.

def not_packing(a1=None, a2=None, a3=None):
	print(a1, a2, a3)

위의 함수는 매개변수로 a1, a2, a3를 명시적으로 선언했다.

 

not_packing(1, 2, 3)
not_packing(a1 = 1,  a2 = 2, a3 = 3)

함수를 첫번째와 같이 사용하면 인자를 위치로 매핑한 것이고, 두번째는 키워드로 매핑한 것이다.함수에서 사용된 인자들을 각각 위치 인자, 키워드 인자로 표현할 수 있다.

 

위치 인자로 전달한 모든 인자들을 Tuple로 받기 위해 매개변수 앞에 * 을 붙였다.

그렇다면 키워드로 매핑된 인자들을 하나로 묶어서 받기 위해선 다른 방식을 사용해야한다.

앞서 본 방식처럼 아래의 코드를 동작하기 위한 함수를 사용하기 위해선 어떤 함수가 필요할까?

print_all(a1=1,  a2=2, a3=3)
print_all(a1=1,  a2=2, a3=3,  a4=4, a5=5)

 

이러한 변동되는 키워드 인자를 받기 위해 * 연산자를 매개변수 앞에 두 개 붙일 수 있다.

해당 매개변수 명의 컨벤션은 kwargs이다.

def print_all(**kwargs):
	for number in kwargs.values():
		print(number, end=" ")
	print()

위와 같이 가변 키워드 매개변수를 사용하여 가변 키워드 인자를 Packing할 수 있다.

가변 키워드 매개변수를 사용하여 어떤식으로 인자들이 매핑되었는지를 확인해보면 다음과 같다.

def check_packing(**kwargs):
	print(kwargs)
	print(type(kwargs))
	for number in kwargs.values():
		print(type(number), end=" ")

check_packing(a1=1, a2=2, a3=3)

앞선 가변 위치 매개변수의 Packing은 인자들을 Tuple 타입으로 묶어줬는데, 가변 키워드 매개변수를 통한 인자들은 Dictionary 타입으로 Packing된다.

인자에서 사용된 키워드들은 함수 내의 매개변수 kwargs의 Key로써 전달되고, 넣어준 값들은 Value에 할당된다.

이렇게 인자를 키워드로 전달할 경우에는 ** 을 사용해 가변 인자를 Packing할 수 있다.


# Unpacking - **

Dictionary를 함수의 인자로 전달할 때 **을 사용한다.

각 Dictionary 의 Key값이 매개변수 키워드와 매핑이 되면 해당 인자가 함수에 전달된다.

def not_packing(a1=None, a2=None, a3=None):
    print(a1, a2, a3)

def packing(**kwargs):
	for number in kwargs.values():
		print(number, end=" ")

not_packing(dict(a1=1, a2=2, a3=3))
packing(dict(a1=1, a2=2, a3=3))

위 두 함수는 각각 가변 키워드 매개변수를 사용하고, 사용하지 않았다. 
이 함수들에게 Dictionary 를 인자로 전달하게 된다면 다음과 같은 결과를 확인할 수 있다.

이번엔 packing 함수에서 에러가 발생한다.


가변 키워드 매개변수를 선언한 packing 함수에서는 키워드 인자를 전달받아야하는데, Dictionary 하나가 위치 인자로 전달되었기 때문에 위와 같은 에러가 발생한다.

이를 ** 로 Unpacking 해주면 아래와 같이 정상적인 결과를 확인할 수 있다.


# Unpacking 활용 - List or Dictionary 합치기

Unpacking를 응용하면 아래와 같이 List나 Dictionary를 별도의 메소드 없이 합치는 것이 가능하다.


List 합치기

list1 = [1, 2, 3]
list2 = [4, 5, 6]

combined_list = [*list1, *list2]
print(combined_list)


Dictionary 합치기

dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}

combined_dict = {**dict1, **dict2}
print(combined_dict)

728x90
반응형