-
가변 인자와 Packing/Unpacking의 원리python 2026. 1. 13. 10:29
파이썬 코드를 보다 보면 함수 정의나 호출부에서 별(*)이 붙은 변수들을 종종 마주치게 됩니다.
def my_func(*args, **kwargs): ...보통 가변 인자(Variable Arguments)라고 부르는 이 문법은 파이썬의 유연함을 담당하는 핵심 기능입니다. 단순히 "인자를 많이 받을 수 있다"를 넘어서, Packing과 Unpacking(풀기)이라는 개념을 정확히 이해해야 자유자재로 다룰 수 있습니다.
오늘은 이 *args와 **kwargs가 내부적으로 어떻게 작동하는지, 그리고 왜 함수 호출 시 func(*args)와 같이 사용하는지 정리해 보겠습니다.
가변 인자(Variable Arguments)란?
함수를 만들 때 인자의 개수가 몇 개가 될지 미리 알 수 없는 경우가 있습니다. 이때 사용하는 것이 가변 인자입니다.
- *args: 정해지지 않은 수의 위치 인자(Positional Arguments)를 받습니다.
- **kwargs: 정해지지 않은 수의 키워드 인자(Keyword Arguments)를 받습니다.


핵심 원리: 상황에 따라 달라지는 Asterisk(*)의 역할
애스터리스크(*) 연산자는 사용되는 위치에 따라 정반대의 역할을 수행합니다. 이 두 가지를 구분하는 것이 가장 중요합니다.
함수를 정의할 때: Packing
흩어져서 들어오는 인자들을 하나의 객체(튜플/딕셔너리)로 묶어주는 역할입니다.
# 정의(Definition) 단계에서의 * : Packing def wrapper(*args, **kwargs): print(f"args(튜플): {args}") print(f"kwargs(딕셔너리): {kwargs}") # 사용자가 낱개로 던진 데이터를 받아 묶음 wrapper(1, 2, a=3, b=4) # 1, 2 (낱개) → (1, 2) (튜플로 Packing됨) # a=3, b=4 (낱개) → {'a': 3, 'b': 4} (딕셔너리로 Packing됨)- *args (위치 기반 가변 인자)
- 역할: "위치(순서)로 들어오는 인자들 중, 주인이 없는 녀석들은 다 내놔라."
- 결과: 튜플(Tuple)로 묶음.
- 정확한 명칭: Positional Variable Arguments
- **kwargs (키워드 기반 가변 인자)
- 역할: "이름표(키워드) 붙여서 들어온 인자들 중, 주인이 없는 녀석들은 다 내놔라."
- 결과: 딕셔너리(Dictionary)로 묶음.
- 정확한 명칭: Keyword Variable Arguments
함수를 호출할 때: Unpacking
묶여있는 객체(리스트, 튜플, 딕셔너리)를 낱개의 인자로 풀어서 전달하는 역할입니다.
def add(a, b, c): return a + b + c numbers = [1, 2, 3] # 호출(Call) 단계에서의 * : Unpacking # 리스트를 뜯어서 a, b, c에 각각 넣어줌 print(add(*numbers))- add(numbers)로 호출하면? → 에러 발생 (a에 리스트 전체가 들어가고 b, c가 없음)
- add(*numbers)로 호출하면? → add(1, 2, 3)으로 변환되어 실행됨.
def introduce(name, age): print(f"저는 {name}이고, {age}살입니다.") # 웹이나 API에서 받은 데이터가 딕셔너리 형태라고 가정 user_info = {'name': '철수', 'age': 30} # 딕셔너리를 통째로 넣으면 에러! # **를 붙이면 {'name':'철수', 'age':30} -> name='철수', age=30 으로 풀림 introduce(**user_info)다양한 unpacking
def print_args(a, b, c): print(f"받은 인자: {a}, {b}, {c}") # print_args("ABC") -> 에러! (인자가 1개뿐이라서) print_args(*text) r = range(3) # 0, 1, 2 print_args(*r) # == print_args(0, 1, 2) 와 동일 my_set = {10, 20, 30} print("첫 번째 실행:") print_args(*my_set) print("두 번째 실행:") print_args(*my_set) # 첫 번째 실행: # 받은 인자: 10, 20, 30 # 두 번째 실행: # 받은 인자: 20, 30, 10 def my_gen(): yield "One" yield "Two" yield "Three" gen = my_gen() print_args(*gen) # 받은 인자: One, Two, Three실전 응용: 인자 전달 ("토스" 하기)
def wrapper(*args, **kwargs): # 1. 받을 때 (Packing): 어떤 인자가 오든 튜플과 딕셔너리로 묶어서 보관 print("함수 시작 전") # 2. 줄 때 (Unpacking): 보관했던 튜플과 딕셔너리를 다시 풀어서 원본 함수에 전달 result = func(*args, **kwargs) return result이 코드가 작동하는 흐름은 다음과 같습니다.
- 진입 (def): 사용자가 func(1, 2, k=3)을 호출하면, wrapper는 이것을 args=(1, 2), kwargs={'k': 3} 형태로 포장(Packing)해서 받습니다.
- 전달 (call): func를 호출할 때 *args, **kwargs를 붙여줍니다. 파이썬은 튜플 (1, 2)를 뜯어 1, 2로, 딕셔너리 {'k': 3}을 뜯어 k=3으로 Unpacking합니다.
- 결과: 원본 함수 func 입장에서는 마치 사용자가 직접 func(1, 2, k=3)을 호출한 것과 똑같은 인자를 받게 됩니다.
💡 핵심 요약: Packing vs Unpacking 한눈에 보기
구분 단계 (사용 위치) 역할 적용 대상 → 결과 기호 Packing 함수 정의 시 (def) 여러 개의 인자를 하나로 묶기 위치 인자 → 튜플(Tuple) *args 키워드 인자 → 딕셔너리(Dict) **kwargs Unpacking 함수 호출 시 (call) 묶여 있는 객체를 낱개로 풀기 Iterable(리스트, 문자열 등) → 위치 인자 * 딕셔너리 → 키워드 인자 ** 'python' 카테고리의 다른 글
모듈, 패키지, 라이브러리, 그리고 프레임워크 (1) 2026.01.13 제너레이터(Generator) 활용 예제 (0) 2026.01.11 Closure, 도대체 왜 쓰는 걸까? (0) 2026.01.09 Iterable ? Iterator? Generator? (0) 2026.01.09