Python 함수와 parameter(인자)

solee·2022년 1월 26일
1
post-thumbnail

함수란 무엇인가?
함수는 코드의 집합체다.

그냥 실행해도 똑같은 결과가 나오지만, 함수로 만들면 재사용이 가능하다. 몇 번이고!

a = 10
if a > 5 :
  print("크다!")
else :
  print("작다!")

a가 5보다 큰지 확인하는 코드를 함수로 만들면 :

a = 10
def nameofDef(aaa) :
  if aaa > 5 :
    print("크다!")
  else :
    print("작다!")
    
nameofDef(a)

이렇게 된다. 함수를 정의한 후에, a를 인자에 담아 호출했다.
함수의 기본 형태는 다음과 같다.

def 함수명(인자) :
  실행할 코드

우와! 간단해!
여기서 인자는 아예 없을 수도 있고 여러 개가 있을 수도 있다.
또 함수는 return을 가질 수도 있고 없을 수도 있다.

위와 아래에 조금 다른 부분을 인식했는가? 인자로 주어진 변수 aaa는 바깥에 있는 변수 a가 저장된 값이다. 함수 내부에서는 a가 아니라 aaa를 사용한다.... 전역 변수와 지역 변수에 대해서는 나중에 더 정리할 것이다.





parameter

지금은 인자parameter에 대해 정리하고자 한다.

함수는 인자를 다양한 형태로 가질 수 있다.

def myPet(name, age):
  print(f"{name}는 {age}살")

myPet("바둑이","6")

출력하면 바둑이는 6살이라고 출력될 것이다. 기본적으로 함수의 인자는 순서대로 움직인다. 만약

myPet("6", "바둑이")

이라고 호출한다면 6는 바둑이살 이라고 출력될 것이다. 호출한 "6"이 함수의 인자 name으로 들어가서 name으로 출력된다.



- parameter의 순서를 다르게 하고 싶다면

그러고 싶지 않을 수 있다!
내가 6을 먼저 입력하고 싶다면? 함수에게 키워드를 줘서 이 인자 age가 6이라고 알려줄 수 있다.

def myPet(name, age):
  print(f"{name}는 {age}살")

myPet(age="6", name="바둑이")

함수를 호출할 때에 각 인자가 어떤 인자인지 알려줌으로써 순서에 상관 없이 함수를 호출할 수 있다. 또 이렇게 하면 인자가 많거나 비슷한 경우에 가독성이 좋아진다는 장점도 있다(eg. 인자 다섯 개가 전부 사람의 이름인 경우 등).



- 두 가지를 다 쓰고 싶다면

둘을 섞는 것? 가능하다.

def myPet(name, age):
  print(f"{name}는 {age}살")

myPet("바둑이", age="6")

문제 없이 출력될 것이다. 다만, 순서가 바뀔 경우 문제가 된다.

def myPet(name, age):
  print(f"{name}는 {age}살")

myPet(age="6", "바둑이")

앞서는 name이 첫 번째 인자로 들어왔기 때문에 문제 없이 실행되었지만, 이번에는 age는 인식하지만 name에서는 에러가 생긴다. 그러므로 키워드가 있는 인자를 쓰고 싶다면 순서대로 오는 인자의 뒤에 써서는 안 된다.



그러면 인자가 있을 때도 있고 없을 때도 있으면 어떡하지?

- parameter가 있을 수도 없을 수도 있다면

def myPet(name, age="4"):
  print(f"{name}는 {age}살")

myPet("바둑이")

인자를 하나만 보내줬지만, age는 디폴트 값을 가지고 있으므로 오류가 발생하지 않는다. age는 자연스럽게 "4"라는 값을 가지고 함수가 실행될 것이다.
물론 이 때에도 순서가 잘못된다면 오류가 발생한다.

- default value parameter와 non-default value parameter

디폴트 값을 가지고 있는 인자를 default value parameter,
가지지 않은 인자를 non-default value parameter라고 부른다면

default value parameter는 반드시 non-default value parameter 뒤에 있어야 한다.

바로 확인할 수 있다. 먼저 name에 "바둑이"라는 디폴트 값을 넣어주면, 함수를 어떻게 호출하더라도 두 번째 인자인 age는 "4"를 받을 수 없다. 호출부에서 "4"는 두 번째 인자가 될 수 없기 때문이다! 그러므로 실행 결과에 오류가 있는 것이 아니라, 함수 선언 자체에서 오류가 발생하는 것이다.

default value parameternon-default value parameter 앞에서 정의해서는 안 된다.





variable length arguments 가변 인수

아까까지는, 호출할 때에 parameter를 주건 주지 않건 같은 결과를 내었다. 호출할 때 주지 않더라도 default 값을 가지고 있기 때문에 함수 내부에서는 인자를 2개 다 가지고 있기 때문이다.

가변 인수는 다르다.

가변이란 말 그대로 변할 수 있다는 것이다. 가변 인수는 인수가 몇 개인지 신경쓰지 않는다.
가변 인수는 *args 처럼 사용한다.

def menu(*args) :
  print(*args)

menu("짜장", "짬뽕", "탕수육")

출력하면 짜장 짬뽕 탕수육이라고 출력된다. 몇 개를 넣어도 상관없다.
또 예시를 들었을 뿐, *args*뒤에 어떤 단어를 사용해도 상관 없다.

def menu(*yummy) :
  print(*yummy)

menu("짜장", "짬뽕", "탕수육", "팔보채", "지삼선")

짜장 짬뽕 탕수육 팔보채 지삼선이라고 출력된다. 냠냠!
가변 인수로 들어오면 리스트로 받아져 함수 내부에서 리스트로 작동한다.

def menu(*yummy) :
  print(yummy)

menu("짜장", "짬뽕", "탕수육", "팔보채", "지삼선")

*을 빼고 호출한다면 결과가 조금 다르다.
('짜장', '짬뽕', '탕수육', '팔보채', '양장피')라고 출력된다.





앗, 그러면 가변 인자를 포함해 인자가 여럿이라면 어떻게 되는 거지?

- 가변 인자 뒤에도 인자가 있다면?

파티를 열려는데, 장소와 음료는 정해져 있지만 참석자가 몇명이 될지는 알 수 없다. 참석자를 가변 인자*args 로 받는다.

def party(location, *args, drink):
    print("location=",end=""), print(location)
    print("args=",end=""), print(args)
    print("drink=",end=""), print(drink)

party("수민이네", "유라", "다은", "다은이네 강아지", "샴페인")

개행되는 것을 막기 위해 end=""를 사용한 것을 확인할 수 있다.
이렇게 호출할 경우, 함수에는 오류가 나지 않지만 호출할 때에 오류가 발생한다. 컴퓨터는 샴페인이 사람인지 음료인지 모르니까, 가변 인자 *args가 어디부터 어디까지인지 모르니까 오류가 나는 거겠지?

오류가 나는 이유는 알겠는데 궁금한 게 2개 생긴다.

  1. 어? 아까는 문제가 있으면 함수 자체에서 오류가 났는데?
  2. 어? 함수에 문제가 없는데 왜 오류가 나지?

친절하고 상냥한 에러 메시지를 잘 읽어 보면, 확실하게 보이는 게 있다.

keyword-only argument: 'drink'

어?

keyword-only argument: 'drink'

어?

그렇다. 키워드로 얘가 drink예요! 하고 알려준다면 이 함수는 오류 없이 실행될 수 있는 것이다.

def party(location, *args, drink):
    print("location=",end=""), print(location)
    print("args=",end=""), print(args)
    print("drink=",end=""), print(drink)

party("수민이네", "유라", "다은", "다은이네 강아지", drink="샴페인")

이렇게 호출하면, 문제 없이 실행된다.

# 결과
location=수민이네
args=('유라', '다은', '다은이네 강아지')
drink=샴페인
+ 출력할 때 ```print(*args)```로 한다면 (' ') 들이 사라질 것이다.

위에서 언급한 오류와 같이 순서대로 와야 할 인자 위치에 다른 인자가 있고 다른 위치에서 해당 인자의 키워드를 넣어 호출하면 에러가 발생한다.
어렵게 말하는 것 같은데, 다음 예시를 보면 알 수 있다.

def party(location, *arg):
    print("location=",end=""), print(location)
    print("arg=",end=""), print(arg)

party("유라", "다은", location="수민이네")

인자는 location과 가변 인자뿐이다. 맨 처음에 들어온 "유라"가 location의 자리에 있기 때문에, 함수는 자연스럽게 location="유라"라고 인식한다. 그런데 뒤에 들어온 키워드가 또다시 location을 호출하는 것이다. 한 변수에 여러 값을 집어넣었기 때문에 오류가 발생하는 것이다.




결론은 이렇다.

함수를 선언할 때에는, 가변 인자 **args의 뒤에 다른 인자를 넣을 수 있다.
함수를 호출할 때에는, 가변 인자가 뒤에 다른 인자를 넣되 키워드를 붙여야 호출할 수 있다.





variable length keyword arguments 가변 키워드 인자

가변 키워드 인자는 일반적으로 **kwarg라고 사용되며, **가 붙어있다면 마찬가지로 어떤 단어든 상관없다. 이름 그대로 가변이자 키워드인 인자다. 이 인자는 "키워드"를 받는다.

def party(location, **kwarg):
    print("location=",end=""), print(location)
    print("kwarg=",end=""), print(kwarg)

party("수민이네", first="유라", second="다은", third="다은이네 강아지")

# 출력:
location=수민이네
kwarg={'first': '유라', 'second': '다은', 'third': '다은이네 강아지'}
가변 키워드 인자는 {'키':'값'}이라는 dictionary의 형태로 저장된 것을 볼 수 있다.

이번에는 party()라는 함수에서 장소를 제외한 모든 정보를 가변 키워드 인자로 받았다.
호출 구문을 보면, 모든 단어를 키워드를 사용해 지정해준 것을 볼 수 있다.

키워드 인자에 location="수민이네"와 같은 방식으로 값을 넣어주었던 것을 기억할 것이다. 가변 키워드 인자는 이런 키워드를 사용해 모든 키워드가 있는 인자를 받아주는 것이다.


그렇다면 여기서도 location에 키워드를 주면 어떻게 될까 하는 궁금증이 생길 것이다.

def party(location, **kwarg):
    print("location=",end=""), print(location)
    print("kwarg=",end=""), print(kwarg)

party(location="수민이네", first="유라", second="다은", third="다은이네 강아지")

가변 키워드 인자가 아니라 다른 인자에도 키워드를 넣어준다면?
아무 문제 없이 작동한다.

# 출력
location=수민이네
kwarg={'first': '유라', 'second': '다은', 'third': '다은이네 강아지'}

똑같은 키워드 방식으로 들어간 location이 kwarg가 아니라 location 자리에 멀쩡히 들어가는 것을 볼 수 있다. 아래처럼 location="수민이네"를 세 번째, 네 번째 인자로 넣어 호출했을 때에도 동일한 결과가 나왔다.

party(first="유라", second="다은", location="수민이네", third="다은이네 강아지")

결론은 이렇다.

함수를 선언할 때에는, 가변 키워드 인자 **kwarg의 뒤에 다른 인자가 들어갈 수 없다.
함수를 호출할 때에는, 가변 키워드 인자와 관련없이 키워드로 다른 인자를 넣어 호출할 수 있다.
왜냐하면, 다른 인자를 키워드로 호출해 인식시킬 수 있기 때문이다. 그러나 이 경우 키워드가 없는 다른 인자가 자리에 있으면 안 된다.

어려운 문제는 아니고,

def party(location, **arg):
    print("location=",end=""), print(location)
    print("arg=",end=""), print(arg)

이런 함수가 있을 때에 party(location="집", host="유라", guest="다은", dog="코코") 이렇게 호출하는 것은 아무 문제 없다.
하지만 party("집", host="유라", guest="다은", location="코코") 이렇게 호출하는 것은 문제가 된다. 첫 번째 인자가 location의 자리에 위치해 값이 들어갔는데, 뒤에서 location을 키워드로 사용해 또다시 값을 넣었기 때문이다.





종합 문제!

def wow(name="김개발", *args, age, **kwargs, hobby):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)
    print("hobby=",end=""), print(hobby)

wow(20, "비룡", "01012341234", "male" ,mobile="01012341234", hobby="요리")

이런 혼종이 있다면 어떨까.
일단 함수 선언에서부터 오류가 나기 시작한다. 가변 키워드 인자 뒤에 또다시 인자가 오기 때문이다. 뒤에 오는 hobby와 print(hobby)들을 삭제한다.

def wow(name="김개발", *args, age, **kwargs):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)

wow(20, "비룡", "01012341234", "male" ,mobile="01012341234", hobby="요리")

아는 오류가 생기기 시작한다. 메시지는 다음과 같다.

age라는 인자가 없다는 거다. 정확하게는 20이라는 값이 있지만 찾지를 못하고 있는 것이다. 컴퓨터를 위해 20에 age=20하고 키워드를 추가해서 호출해 주자. 그러면 호출하려는 문장에 바로 에러가 난다.

아까 수없이 있었던 문장이다. 키워드 인자는 그 자체로 순서에 상관 없이 값이 반영되므로 그 뒤에 키워드가 없는 인자가 오면 컴퓨터는 어디에 값을 적용해야 하는지 알지 못하게 된다.

그래서 모든 인자에 키워드를 입력한다면, 드디어 에러가 발생하지 않는다.

wow(age=20, name="비룡", phone="01012341234", sex="male" ,mobile="01012341234", hobby="요리")

# 출력
name=비룡
args=()
age=20
kwargs={'phone': '01012341234', 'sex': 'male', 'mobile': '01012341234', 'hobby': '요리'}

그러면 에러 없는 결과가 나온다. 이게 원하던 결과인가? 아니다!
일단 args가 비어 있고, 키워드가 없던 핸드폰 번호를 가리키는 인자가 phone과 mobile 두 개가 된다.
맨 앞에 넣는다면 이름까지는 키워드 없이 적용할 수 있겠다. 키워드를 넣고 빼고 다른 인자를 넣어주면 드디어 원하는 결과가 나온다. 호출문을 보면 다음과 같다:

def wow(name="김개발", *args, age, **kwargs):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)

wow("비룡", "볶음밥", "팔보채", age=20, sex="male", mobile="01012341234", hobby="요리")

키워드를 순서대로 넣어주고, *args가 가져가는 인자 다음에 나오는 age는 키워드를 적용해 주었고, 그 뒤로는 가변 키워드 인자가 가져갈 수 있도록 키워드를 전부 적용하였다.
그리고 결과물이다:

name=비룡
args=('볶음밥', '팔보채')
age=20
kwargs={'sex': 'male', 'mobile': '01012341234', 'hobby': '요리'}



profile
DA DA DA

0개의 댓글