[파이썬] 패킹, 언패킹, *args, **kwargs

InAnarchy·2023년 4월 13일
0

Python

목록 보기
7/14
post-thumbnail
post-custom-banner

가변인자

  • 임의의 개수를 넣을 수 있는 인자
  • 넣지 않을 수도 있고, 몇개를 넣을 수도 있다.

키워드 인자

키워드 인자를 사용한 작성

함수를 사용할때는 인자의 순서에 따라 값을 넣어야한다.

def personal_info(name, age, address):
     print('이름: ', name)
     print('나이: ', age)
     print('주소: ', address)
     
personal_info('홍길동', 30, '서울시 용산구 이촌동')

그렇다면 키워드 인자를 사용해서 작성한다면?

personal_info(age=30, address='서울시 용산구 이촌동', name='홍길동')

순서를 지키지 않아도 키워드에 해당하는 값이 들어간다.

초기값이 지정된 경우

만약 매개변수에서 초기값이 지정되어있다면?

def personal_info(name, age, address='비공개'):
     print('이름: ', name)
     print('나이: ', age)
     print('주소: ', address)
personal_info('홍길동', 30)

# 이름:  홍길동
# 나이:  30
# 주소:  비공개

초기값을 지정한 경우에 해당 값을 생략하고 호출한 경우 초기값이 리턴되고,

personal_info('홍길동', 30, '서울시 용산구 이촌동')

# 이름:  홍길동
# 나이:  30
# 주소:  서울시 용산구 이촌동

초기값이 지정되어 있더라도 값을 넣으면 해당 값이 전달된다.

단, 초기값이 지정된 매개변수 다음에는 초기값이 없는 매개변수가 올 수 없다. 즉

def personal_info(name, address='비공개',  age): #SyntaxError: non-default argument follows default argument
     print('이름: ', name)
     print('나이: ', age)
     print('주소: ', address)

위와 같은 경우에는 에러가 발생하니, 초기값이 지정된 매개변수는 맨 뒤에 몰아주도록 하자.

packing

  • 인자로 받은 여러개의 값을 하나의 객체로 합쳐서 튜플로 만들어줌.
  • 매개변수의 개수를 유연하게 지정할 때 사용
  • 가변인자 패킹과 키워드인자 패킹으로 나누어짐

가변인자 packing

    • 매개변수 앞에 * 을 붙임
def func(*args):
      print(args)
      print(type(args))

func(1, 2, 3, 4, 5, 6, 'a', 'b')
#(1, 2, 3, 4, 5, 6, 'a', 'b')
#<class 'tuple'>

이 때 *args는 함수로 전달된 인자들을 튜플로 묶어준다.
즉 func(1, 2, 3, 4, 5, 6, 'a', 'b')를 호출하면, args 변수는 위와 같이 튜플로 구성된다.

def sum_all(*numbers):
  result = 0
  for number in numbers:
    result += number 
  return result

print(sum_all(1, 2, 3, 4, 5, 6)) # 21

packing을 이용해 반드시 받아야하는 매개변수와, 여러개를 받을 수 있는 매개변수를 분리해서 작성할 수 있다.

def print_family_name(father, mother, *sibling):
      print("아버지 :", father)
      print("어머니 :", mother)
      if sibling: 
           print("호적 메이트..")
           for name in sibling:
                 print(name)

print_family_name("홍길동", '심사임당', '김태희', '윤아')
아버지 : 홍길동
어머니 : 심사임당
호적 메이트..
김태희
윤아

계속 예시를 보자.

exam1,exam2,exam3 = "DB", "OS", "JAVA"

def cal(*exams):
  for exam in exams:
    print(exam)
cal(exam1,exam2,exam3)

# DB
# OS
# JAVA
exam1,exam2,exam3 = "DB", "OS", "JAVA"

def cal(date, *exams):
  print(date)
  for exam in exams:
    print(exam)
cal("4/17", exam1,exam2,exam3)

# 4/17
# DB
# OS
# JAVA

단, 이 때 가변인자는 일반 변수 뒤에 있어야한다.
def cal(*exams.date): 은 에러.

키워드인자 packing

  • 매개변수에 ** 을 붙임
  • 이 때 키워드와 인자 쌍으로 이루어진 딕셔너리를 리턴
  • 키워드 = 값 형태로 함수를 호출할 수 있음
  • 함수와 달리 인자의 순서를 지키지 않아도 됨
def kwpacking(**kwargs):
     print(kwargs)
     print(type(kwargs))

kwpacking(a=1, b=2, c=3)
{'a': 1, 'b': 2, 'c': 3}
<class 'dict'>
def print_family_name(father, mother, **sibling):
      print("아버지 :", father)
      print("어머니 :", mother)
      if sibling:
           print("호적 메이트..")
           for title, name in sibling.items():
                 print('{} : {}'.format(title, name))

print_family_name("홍길동", '심사임당', 누나='김태희', 여동생='윤아')
아버지 : 홍길동
어머니 : 심사임당
호적 메이트..
누나 : 김태희
여동생 : 윤아

이 때 가변 인자와 키워드 인자 패킹을 동시에 사용할 수도 있다.

def print_family_name(*parents, **sibling):
      print("아버지 :", parents[0])
      print("어머니 :", parents[1])
      if sibling:
           print("호적 메이트..")
           for title, name in sibling.items():
                 print('{} : {}'.format(title, name))

print_family_name("홍길동", '심사임당', 누나='김태희', 여동생='윤아')

unpacking

가변인자 unpacking

  • 여러개의 객체를 포함하고 있는 하나의 객체를 풀어줌
  • 인자 앞에 * 을 붙임
  • 문자열, 튜플, 리스트, 딕셔너리, 집합 등 컨테이너 객체라면 사용 가능
def sum(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
# sum(numbers) # error

print(sum(*numbers)) #6

[1, 2, 3]을 인자로 보낼때, *을 붙이면 unpacking이 발생한다.

당연하지만 함수의 매개변수의 개수와 인자의 개수가 다를 떄는 에러가 발생한다.

def sum(a, b, c):
    return a + b + c

numbers = [1, 2, 3, 4]

print(sum(*numbers)) #TypeError: sum() takes 3 positional arguments but 4 were given
def sum(a, b, c):
    return a + b + c

print(sum(*'123')) #123
print(sum(*'ABC')) #ABC
print(sum(*'가나다')) #가나다
print(sum(*'가','나','다')) #가나다
print(sum(*{'가':1, '나':2, '다':3})) #가나다

키워드인자 unpacking

def cal(first, op, second):
    if op == '+':
        return first + second
    if op == '/':
        return first / second
    if op == '-':
        return first - second
    if op == '*':
        return first * second

prob = {
  'first': 12,
  'second': 34,
  'op': '*'
}

cal(**prob) # 결과 : 408

*args, **kwargs를 함께 사용한 예시

사실 우리는 이미 이 예시를 자주 사용했다. 대표적인 것이 print이다.

print("출력값", end = " ")

등 출력값을 가변인자로, end나 sep을 키워드 인자로 넣어 사용했었다.

def simple_function(a, b, *args, **kwargs):
    print(f"a: {a}, b: {b}")
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

simple_function(1, 2, 3, 4, 5, x=10, y=20)

# a: 1, b: 2
# args: (3, 4, 5)
# kwargs: {'x': 10, 'y': 20}

여담이지만 실수를 했는데

def custom_print(*args, **kwargs):
    print(*args, **kwargs)

user_input = input("Enter something: ")
custom_print(user_input, end=" ", sep=":")
Enter something: 123a
123a 

의도한 결과는 1:2:3:a이었는데, 위와 같이 나왔다.

이유는 sep을 사용하기 위해 입력된 문자열을 분리해서 전달해야하기 때문이다.
(위의 입력값 자체가 하나의 값이기 때문)

그러므로 다음과 같이 수정한다.

def custom_print(*args, **kwargs):
    print(*args, **kwargs)

user_input = input("Enter something: ")
custom_print(*user_input, end=" ", sep=":")

단, 이때 def custom_print(**kwargs, *args)처럼

**kwargs*args 보다 앞쪽에 오면 안 된다.
매개변수 순서에서 **kwargs는 반드시 가장 뒤쪽에 와야 한다.

특히 고정 매개변수와 *args, **kwargs 를 함께 사용한다면

def custom_print(a, b, *args, **kwargs):

처럼 매개변수는 고정 매개변수, *args, **kwargs 순으로 지정해야 한다.

REFERENCE

wikidocs
코딩도장

profile
github blog 쓰다가 관리하기 귀찮아서 돌아왔다
post-custom-banner

0개의 댓글