지식이 늘었다/웹개발

Python의 *, ** 문법

PurpleGuy101 2024. 12. 16. 14:45

 

0.

 

파이썬의 unpacking 문법인

* 연산자에 대해서 알아보자.

 

JS에서 배웠던 ... 처럼

파이썬에서 *는

정해지지 않은 입력값들을 pack해주거나,

리스트, 튜플 같은 자료구조의 데이터를 쪼개어주는 unpacking해주는 연산자이다.

 

SQL의 Select All에 사용되었던 *를 연상시키는 것도 괜찮다.

 

 

1.

 

기본적인 사용법

 

함수의 정의과정에서 전달받는 값들이 파라미터다.

파라미터에서 * 연산자를 사용할 때, *는 여러 아규먼트를 하나의 튜플로 합치는 Packing 기능을 수행한다.

 

함수의 호출과정에서 실제로 전달하는 값들이 아규먼트이다.

아규먼트에서 * 연산자를 사용할 때, *는 해당 파라미터를 여러개의 값으로 분할하는 Unpacking 기능을 수행한다. 

 

def multiply(*args):
    print(args)
    total = 1
    for arg in args:
        total = total * arg
    return total

# 이런식으로 여러개의 arguments를 입력받으면 
print(multiply(1,3,5))

# (1, 3, 5)의 튜플 출력
# 15의 결과물 출력력

 

 

2.

 

리스트, 튜플 *문법

 

def add(x,y):
	return x+y

nums = [3,5]
print(add(*nums))
# 8출력

nums2 = {3,5}
print(add(*nums2))
# 8출력

 

def add(x,y):
	return x+y

nums = [3,5,7]
print(add(*nums))

# 에러발생, TypeError: add() takes 2 positional arguments but 3 were given

 

2.

 

딕셔너리와 **문법

딕셔너리에서 파라미터 명과 동일한 key값을 가진 원소가 있다면

함수에 전달해준다.

딕셔너리에 파라미터의 key값을 가진 key:value 쌍이 없으면 오류가 발생한다. 

def add(x,y):
	return x+y

nums = {"x":15, "y":25}

# Case1 : 일반적인 경우
print(add(nums["x"], nums["y"])) # 40출력
print(add(x=nums["x"], y=nums["y"])) # 40출력

# Case2 : **을 사용
# 딕셔너리의 key값과 동일한 이름의 아규먼트를 통과시킴킴
# **를 사용
print(add(**nums)) # 40출력력
#dictionary의 

# 딕셔너리에 대응되는 argument가 없을때 -> 에러발생생
nums2 = {"y":15, "z":25}
print(add(**nums2)) 
#TypeError: add() got an unexpected keyword argument 'z'

 

 

3.

 

multiply를 수행하는 함수를 만들었다.

아래 코드에서 apply(1,3,6,7,operator="*")을 수행하면 126이 아닌 (1,3,6,7)이 반환된다.

무엇이 문제일까?

def multiply(*args):

    total = 1
    for arg in args:
        total = total * arg
    
    return total


def apply(*args, operator):
    if operator == "*":
        return multiply(args)
    elif operator =="+":
        return sum(args)
    else:
        return "No valid operator provided to apply()"
    
print(apply(1,3,6,7, operator="+")) # 17을 정상적으로 출력

print(apply(1,3,6,7,operator="*")) # (1,3,6,7) 튜플을 출력

# multiply에 (1,3,6,7) 튜플을 그대로 전달해줬기 떄문.

 

- (1) apply함수에서 *args를 통해 패킹된 (1,3,6,7) 튜플이 만들어져서 args 변수에 저장되었다.

-> (1-2) 함수 내부에서는 C언어처럼 *args라는 변수는 사용되지 않는다.

-> (1-3) 오로지 args 만 호출가능한 상태이다.

 

- (2) apply에서는 multiply(args)에 아규먼트으로 (1,3,6,7)이라는 튜플을 전달해줬으나

 

- (3) multiply의 정의부분에서는 파라미터로 multply(*args)를 받고 있다.

->(3-1) *자체가 unpacking operator인데 (1,3,6,7) 튜플을 unpack해야할 하나의 원소로 인식해서

( (1,3,6,7), ) 튜플의 튜플로 받아들였기 때문.

 

- (4) apply함수에서 multiply를 호출할 때 아규먼트에서 unpack을 수행하는 multiply(*args)를 해서 

1,3,6,7  4개의 아규먼트를 전달하고, multiply에서 pack을 수행하는 *로 다시 튜플로 만들어서 받던가

 

- (5) 애초에 apply함수에서 multiply를 호출할 때 multiply에 아규먼트로 (1,3,6,7)의 튜플을 전달하고

이를 multiply함수에서 그대로 args로 받아서 쓰는 식으로 해결해야한다.

 

말이 어려운데 직접해보면 별거없다.

Caller일때, 함수의 호출과정에서, 아규먼트 상에서 *연산자는 unpack을 수행하고

Callee일때, 함수의 정의과정에서, 파라미터 상에서  *연산자는 pack을 수행한다.

JS에서 ... 연산자와 동일하다.

 

외부에서 함수를 호출할때는 나눠주고

함수가 호출받을때 내부에서는 합쳐주는게 핵심이다.

 

 

def multiply(*args):
# 여기서 print(args)를 수행시 ((1, 3, 6, 7),)를 출력.
    total = 1
    for arg in args:
        total = total * arg
    
    return total


def apply(*args, operator):
    if operator == "*":
        return multiply(*args)
    elif operator =="+":
        return sum(args)
    else:
        return "No valid operator provided to apply()"
    
print(apply(1,3,6,7, operator="+")) # 17을 정상적으로 출력

print(apply(1,3,6,7,operator="*")) # 126을 정상적으로 출력

 

 

 

def multiply(args):

    total = 1
    for arg in args:
        total = total * arg
    
    return total


def apply(*args, operator):
    if operator == "*":
        return multiply(args)
    elif operator =="+":
        return sum(args)
    else:
        return "No valid operator provided to apply()"
    
print(apply(1,3,6,7,operator="*")) # 126 출력

 

 

4.

 

그래서, 여러개의 unlimited argument에 대해서

우리는 아래와 같이 함수를 정의해서 대응할 수 있다.

 

def both(variable, *args, **kwargs):
    print(variable)
    print(args)
    print(kwargs)

both(1,3,5, name="bob", age=25)

# 출력
# 1
# (3, 5)
# {'name': 'bob', 'age': 25}

 

 

5.

 

리스트나 튜플에서 * 사용시

destructuring 기능이 있다.

*head, tail = [1,2,3,4,5]
print(head) # [1,2,3,4] 출력
print(tail) # 5출력

head, *tail = [1,2,3,4,5]
print(head) # 1 출력
print(tail) # [2,3,4,5] 출력