Python의 *, ** 문법
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] 출력