[파이썬] Packing, Unpacking

대심이·2024년 1월 14일

파이썬 클린 코드

목록 보기
1/5
post-thumbnail

최근 파이썬에 갓입문해서 마리아노 아나야의 <파이썬 클린 코드>라는 책을 읽고 있다.

그 중 가변인자부분이 책만 읽어서는 이해가 잘 되지 않아서 적어두려고 한다.

마지막 부분에는 나의 이해를 돕기 위해서 JavaScript도 동일한 결과를 주는 코드를 짜보았다.

Packing, Unpacking

iterable 객체를 분해, 병합하는 데 사용한다.

  • Packing: iterable 객체를 단일 변수로 할당하는 것
  • Unpacking: iterable 객체를 여러 변수로 할당하는 것

변수 앞에 *를 사용하면 가변 인자를 packing(포장)하여 쓸 수 있다.

def my_func = (*args):
	print(args)

my_func(1, 2, 3) #=> (1, 2, 3)

다음, 위치별로 변수에 unpacking(포장을 푸는 것)할 수 있다.

a, b, c = [1, 2, 3]
print(a, b, c) #=> 1 2 3

함수 호출 시 전달할 변수 앞에 *을 붙여서 unpacking할 수도 있다.

def my_func(first, second, third):
	print(first, second, third)

my_arr = [1, 2, 3]
my_func(*my_arr) #=> 1 2 3

첫 번째 예제에서 함수 내 파라미터로 쓰인 *args와 위 예제에서 쓰인 *my_arr는 둘 다 변수 앞에 *이 붙었지만 행동하는 양상이 달라서 혼란이 왔다.

정리하자면, 함수 내 파라미터 앞에 *를 붙이면 packing한다는 뜻이고, 함수에 값을 넘길 때는 *를 붙이면 unpacking한다고 볼 수 있다.

아래 예제에서 1, 2, 3은 (1, 2, 3)으로 packing되며 args는 tuple 타입을 가진다.

def my_func = (*args):
	print(type(args))

my_func(1, 2, 3) #=> <class 'tuple'>

다음 예제를 살펴보자. 변수 앞에 *을 붙이면 [1, 2, 3]이 unpacking되는 것을 볼 수 있다.

my_arr = [1, 2, 3]
print(my_arr) #=> [1, 2, 3]
print(*my_arr) #=> 1 2 3

그렇다면 변수 할당시에도 *를 붙여야할 것 같은데, 실제로 붙이면 에러가 발생한다.

my_arr = [1, 2, 3]
a, b, c = my_arr # ok
[a, b, c] = my_arr # ok
a, b, c = *my_arr #=> SyntaxError: can't use starred expression here

이 부분에 대해서 인터넷에 검색해 봤는데 잘 찾을 수 없었다. *없이도 a, b, c = my_arr에서 unpacking 작업이 일어난다고 한다.

*을 굳이 붙여서 에러를 발생하지 않게 하려면 변수 다음에 ,를 붙여야 한다. (단순히 unpacking되었다가 packing된다.)

my_arr = [1, 2, 3]
a, b, c = *my_arr,

이것을 응용하면 iterable 객체들을 묶는데 쓸 수 있다.

my_arr1 = [1, 2, 3]
my_arr2 = [4, 5, 6]
my_combined_arr = [*my_arr1, *my_arr2]
print(my_combined_arr) #=> [1, 2, 3, 4, 5, 6]

Extended Unpacking

그 다음, 부분적인 Unpacking도 할 수 있다.

my_arr = [1, 2, 3, 4, 5]
a, b, *rest = my_arr
print(a, b, rest) => 1 2 [3, 4, 5]

끝이 아닌 중간에도 쓸 수 있으며,

my_arr = [1, 2, 3, 4, 5]
a, b, *rest, last = my_arr
print(a, b, rest, last) => 1 2 [3, 4] 5

없을 경우 빈 list를 할당한다.

my_arr = [1, 2, 3]
a, b, *rest, last = my_arr
print(a, b, rest, last) #=> 1 2 [] 3

검색하다가 발견한 stackoverflow 질문에 더 다양한 예제가 있으므로 참고하면 좋을 것 같다.
https://stackoverflow.com/questions/6967632/unpacking-extended-unpacking-and-nested-extended-unpacking

JavaScript 예제

완전히 같지는 않지만, JavaScript의 구조 분해 할당(Destructuring assignment)과 나머지 매개변수(Spread)로 유사하게 동작할 수 있다.

Packing 예제

const myFunc = (...args) => {
  console.log(args)
}
myFunc(1, 2, 3) //=> [1, 2, 3]

여기서 typeof args는 "object"이며 Array.isArray(args)는 true를 반환한다.

Unpacking 예제

함수에서 인자를 넘길 때 Unpacking

const myFunc = (a, b, c) => {
	console.log(a, b, c)
}
const myArr = [1, 2, 3]
myFunc(...myArr) //=> 1, 2, 3

변수 할당시 Unpacking

const [a, b, c] = myArr
console.log(a, b, c) //=> 1, 2, 3

여기서 python과 다른 동작을 하는 부분이 있다. python에서는 할당할 변수가 [a, b, c]a, b, c 결과가 동일하지만, JS에서는 []를 넣지 않으면 에러가 발생한다.

const [a, b, c] = myArr // ok
const a, b, c = myArr // Uncaught SyntaxError: Missing initializer in const declaration

또 다른 점은 python은 요소의 개수가 동일해야 하지만, JS는 맞지 않아도 상관없다.

# Python 예제
my_arr = [1, 2, 3, 4, 5]
a, b, c = my_arr #=> ValueError: too many values to unpack (expected 3)
// JS 예제
const myArr = [1, 2, 3, 4, 5]
const [a, b, c] = myArr // ok
const [d, e, f, g, h, i] = myArr // ok
console.log(i) //=> undefined

Array 병합 Unpacking

const myArr1 = [1, 2, 3]
const myArr2 = [4, 5, 6]
const myCombinedArr = [...myArr1, ...myArr2]

Extended Unpacking 예제

JS에서도 동일하게 사용할 수 있다.

const myArr = [1, 2, 3]
const [first, ...rest] = myArr
console.log(first, rest) //=> 1, [2, 3]

하지만 중간에서 Unpacking은 할 수 없다.

const myArr = [1, 2, 3]
const [first, ...rest, last] = myArr // Uncaught SyntaxError: Rest element must be last element
profile
대범해지고 싶어서 대심이

0개의 댓글