ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 가변 인자와 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
    

    이 코드가 작동하는 흐름은 다음과 같습니다.

    1. 진입 (def): 사용자가 func(1, 2, k=3)을 호출하면, wrapper는 이것을 args=(1, 2), kwargs={'k': 3} 형태로 포장(Packing)해서 받습니다.
    2. 전달 (call): func를 호출할 때 *args, **kwargs를 붙여줍니다. 파이썬은 튜플 (1, 2)를 뜯어 1, 2로, 딕셔너리 {'k': 3}을 뜯어 k=3으로 Unpacking합니다.
    3. 결과: 원본 함수 func 입장에서는 마치 사용자가 직접 func(1, 2, k=3)을 호출한 것과 똑같은 인자를 받게 됩니다.

     


     

     

    💡 핵심 요약: Packing vs Unpacking 한눈에 보기

    구분 단계 (사용 위치) 역할 적용 대상 → 결과 기호
    Packing 함수 정의 시 (def) 여러 개의 인자를 하나로 묶기 위치 인자 → 튜플(Tuple) *args
          키워드 인자 → 딕셔너리(Dict) **kwargs
    Unpacking 함수 호출 시 (call) 묶여 있는 객체를 낱개로 풀기 Iterable(리스트, 문자열 등) → 위치 인자 *
          딕셔너리 → 키워드 인자 **
Designed by Tistory.