TIL (2020.05.26)

Awesome·2020년 5월 28일
0

TIL

목록 보기
2/46

Python

함수(function)

함수는 정의된 parameter(매개변수) 에 input arguments(전달인자)를 받아 output을 return하는 구문이다.

함수를 호출할 때, 어떤 방식으로 인자(Argument)를 전달하느냐에 따라서 다음과 같이 구분된다.

1) Positonal Arguments (위치 인자)
2) Keyword Arguments (키워드 인자)

또한 함수를 정의할 때, 인자의 성격에 따라 나열 순서가 정해져 있다.

원본 이미지 : https://getkt.com/wp-content/uploads/2019/02/python-function-definition-arguments-kind-and-order.jpg

순서는 아래와 같다.

  1. Regular Positional Arguments : 일반적인 위치 인자

  2. Default Arguments : Default 인자

  3. Variable Length Positional Arguments : 가변 위치 인자

  4. Non-Default Keyword-Only Arguments : Default가 지정되지 않으면서 반드시 keyword arguments

  5. Default Keyword-Only Arguments : Default가 지정된 keyword arguments

  6. Variable Length Keyword Arguments : 가변 키워드 인자

실제 해당 순서로 함수를 정의하여 호출하면 다음과 같이 나온다.

def fun(a, b, c="c_default", *d, e, f, g="g_default", **h):
    print("가장 첫번째에 위치하는 parameter는 regular-positional-arguments ->",a,b)
    print("그 다음 두 번째로는 default-arguments ->",c)
    print("Variable Length Positional-arguments ->",d)
    print("여기서 부터는 무조건 keyword-arguments ->",e,f)
    print("keyword-arguments 이면서 default ->",g)
    print("가장 마지막에 Variable Length Keyword Positional-arguments ->",h)

fun("a",
"b",
"이 경우에는 default 대신 positional 하게 적용",
"d_arg1","d_arg2","d_arg3",
e="e:key_only_arg1",
f="e:key_arg2",
g="이 경우에는 default 대신 keyword argument로 적용",
h1="karg1",h2="karg2")


코드를 해석에 앞서서 너무 많은 parameter가 나열되어 있으므로, 특정 기준에 따라 나눠서 살펴보겠다.

그 기준은 * (asterisk) 이다. * 의 전후에 따라서 keyword only argument, 즉 반드시 keyword arguments 를 parameter로 전달해야 하는 구간과 그렇지 않은 구간으로 나뉜다.

여기서 두 가지 의문이 생긴다.

Q1 : Keyword Only Arguments는 무엇이고 왜 사용하는가?

Q2 : *(asterisk) 의 역할은 무엇인가?

각각의 질문에 답을 찾아으면서 function definition 규칙에 대해 알아보겠다.

Q1 : Keyword Only Arguments는 무엇이고 왜 사용하는가?
A : 말 그대로 키워드 인자만을 사용할 수 있는 parameter 구간이다. 앞선 함수를 호출하는 여러가지 방법에서 parameter에 직접 키워드를 전달하여 함수를 호출하는 방식이 있었다. 하지만 상황에 따라서 키워드와 위치 인자를 혼용하여 사용할 수 있었는데, 이 구간에서는 키워드 인자만을 사용해야 한다.

그럼 왜 굳이?

Keyword Only Arguments 를 사용함으로써 얻을 수 있는 효용이 발생하기 때문이다.

  1. 가독성을 높여준다. 함수를 호출할 때, 키워드를 지정해주기 때문에 상대적으로 함수가 복잡해질수록 그 내용을 파악하는 데에 있어서 효과적이다.

  2. 키워드를 직접 전달한다는 것은 때에 따라서 해당 argument가 상대적으로 중요함을 의미할 수 있다. 따라서 함수 사용에 있어서 모호성을 줄여준다.

  3. 함수가 굉장히 많은 argument를 input 값으로 받게 되는 경우 위치 인자보다 함수 호출에 용이하다.

이러한 이유로 함수의 내용이 복잡해지고, 다양한 argument를 전달할수록 Keyword Only Arguments를 사용하는 이점이 커진다고 볼 수 있다.

Q2 : *(asterisk) 의 역할은 무엇인가?
A : *(asterisk)는 가변 위치 인자(Variable Length Positional Arguments)를 parameter로 받는 동시에, keyword only arguments 를 인자로 받는 구간과 그렇지 않은 구간으로 나눠주는 역할을 한다.

먼저 가변 위치 인자에 대해서 알아보자.

가변 위치 인자는 파이썬 데이터 자료형 중, 리스트와 튜플 혹은 문자열 같은 시퀀스 자료형(순서가 있고, 반복 가능한 자료형) 을 인자로 받아서 해당 값을 하나하나 언패킹(unpacking)한다. 용어가 어려우니 예시를 보자.

def func(a,b,*c):
    print("parameter a:",a)
    print("parameter b:",b)
    print("parameter c:",c)

func(1,2,3,4,5,6)


위의 예시에서 함수 func에 parameter로 위치 인자 a,b와 가변 위치인자 c를 부여하고 호출해보았다.

a,b는 함수 호출 시에 앞에서부터 차례로 1과 2가 전달되었다. 하지만 4~7 까지는 순서에 따라서 전부 parameter c에 전달되어 튜플 형태로 출력되었다. 이렇듯 두 개 이상의 인자를 순서대로 전달받는 것이 바로 가변 위치 인자다.

주로 위치 가변 인자는 *args 로 표현하며, 아래와 같이 리스트나 튜플과 같은 시퀀스 자료형과 같이 사용한다.

def func(a,b,*c):
    print("parameter a:",a)
    print("parameter b:",b)
    print("parameter c:",c)

list_ = [3,4,5]

func(1,2,list_)

이렇듯 *args 를 통해 원하는 만큼의 개수만큼 위치 인자를 전달할 수 있다.

가변 위치 인자뒤에 위치 인자를 놓는다면 에러가 발생한다.

def func(a,b,*c,d):
    print("parameter a:",a)
    print("parameter b:",b)
    print("parameter c:",c)
    print("parameter d:",d)

list_ = [3,4,5]

func(1,2,list_,6)


앞서 계속 말한 바와 같이, * 뒤에는 keyword only arguments 가 위치해야 한다.

만약 * 만을 인자로 주면 어떻게 될까?

def func(a,b,*):
    print("parameter a:",a)
    print("parameter b:",b)
    print("parameter c:",c)

list_ = [3,4,5]

func(1,2,list_)


* 에 argument 이름을 지정해줘야 한다는 에러메시지가 뜬다.
그렇다면, * 만을 인자로 준 상태에서 keyword only argument 를 인자로 주면 어떻게 될까?

에러가 발생하지 않고 키워드 인자를 호출한다. 즉, * 자체로는 인자를 받을 수는 없지만 keyword only-arguments 구간을 나눠주는 역할을 하는 것은 확실하다는 것을 알 수 있다.

그럼 다시 처음 코드로 돌아가서, 코드해석을 해보자.

def fun(a, b, c="c_default", *d, e, f, g="g_default", **h):
    print("가장 첫번째에 위치하는 parameter는 regular-positional-arguments ->",a,b)
    print("그 다음 두 번째로는 default-arguments ->",c)
    print("Variable Length Positional-arguments ->",d)
    print("여기서 부터는 무조건 keyword-arguments ->",e,f)
    print("keyword-arguments 이면서 default ->",g)
    print("가장 마지막에 Variable Length Keyword Positional-arguments ->",h)

fun("a",
"b",
"이 경우에는 default 대신 positional 하게 적용",
"d_arg1","d_arg2","d_arg3",
e="e:key_only_arg1",
f="e:key_arg2",
g="이 경우에는 default 대신 keyword argument로 적용",
h1="karg1",h2="karg2")

  1. a,b 는 기본 위치 인자다.

  2. c 는 default value argument다. 즉, 함수 호출 시 해당 parameter에 값이 전달되지 않으면 default 값이 자동으로 전달된다. 근데 위의 코드에서는 default 값이 아닌 positional 하게 값이 전달되었다. default 값을 프린트하지 못한 이유는 뭘까?

아래의 예시를 보자.

def func(a,b,c="default_c",*d):
	print("parameter a:",a)
	print("parameter b:",b)
	print("parameter c:",c, type(c))
	print("parameter d:",d, type(d))

list_ = [3,4,5]
func(1,2,list_)


의도는 a,b에 1,2가 출력되고 c는 default 값, 가변 위치 인자인 d에 list_에 있는 3,4,5가 출력되는 것이었다. 그러나 의도와는 다르게 default value를 부여한 c에 3,4,5 가 전부 들어갔다. 하지만 이는 가변 위치 인자로서가 아니라, 일반적인 위치 인자의 형태로 list_ 가 하나의 인자로 들어간 것이다. 가변 위치 인자로 전달되는 경우 데이터 타입은 튜플 형태이기 때문이다.

parameter c 는 default value 이기 전에 위치 인자이다. 따라서, 함수 호출 시에 해당 위치에 값을 전달하면 default 값이 아닌 사용자가 할당한 값이 부여된다.

그렇다면 default 값을 출력하기 위해서는 어떻게 해야할까?

def func(a,b,*d,c="default_c"):
	print("parameter a:",a)
	print("parameter b:",b)
	print("parameter c:",c)
	print("parameter d:",d)

list_ = [3,4,5]
func(1,2,list_)


default value를 부여한 parameter를 맨 뒤로 위치시키면 된다.

방금 예시에서 c 는 둘 중 어떤 인자에 해당할까?

1️⃣ Default Argument

2️⃣ Keyword-Only Arguments with Defaults

답은 2️⃣ 이다. 위에서 언급한 바와 같이 가변 위치 인자 뒤에는 keyword only argument가 위치하기 때문이다.
위치 인자이면서 default 값이 출력되기 위해서는 아래의 예시와 같이 가장 마지막에 위치하되, 가변 위치 인자가 없어야 한다.

지금까지의 내용을 정리하면 아래와 같다.

⓵ *(asterisk)는 가변 위치 인자를 받는 동시에, keyword only arguments 구간을 나눠주는 역할을 한다.

⓶ * 뒤로는 keyword only arguments 구간이다.

⓷ Default 값이 출력되기 위해서는 함수를 정의할 때, 가장 뒤에 위치해야 한다.

마지막으로 가변 키워드 인자 (Variable Length Keyword Arguments) 에 대해서 살펴보겠다.

가변 위치 인자와 마찬가지로 2개 이상의 키워드 인자를 하나의 parameter에 전달받는 역할을 하며 parameter 이름 앞에 **를 붙인다. 일반적으로 kargs 라는 이름으로 표현한다.

규칙 상, kargs 는 인자 중에서 가장 마지막에 위치한다. 따라서 kargs 뒤에 어떤 인자가 오든 에러가 발생한다.

def func(a,b,*c,**d):
	print("parameter a:",a)
	print("parameter b:",b)
	print("parameter c:",c)
	print("parameter d:",d)

list_ = [3,4,5]
func(1,2,list_,num1=6,num2=7)

parameter d 에 2개의 키워드 인자를 전달하였다.

def func(a,b,*c,**d,e):
	print("parameter a:",a)
	print("parameter b:",b)
	print("parameter c:",c)
	print("parameter d:",d)
	print("parameter e:",e)

list_ = [3,4,5]
func(1,2,list_,num1=6,num2=7,8)

가변 키워드 인자 뒤에는 어떠한 인자도 위치할 수 없다. 따라서 위의 위치 인자인 e 는 잘못된 위치다. 함수 정의 자체에 문제가 발생하기 때문에 인자의 속성이 위치 인자인지 키워드 인자인지는 중요하지 않다.

최종적으로 정리하면 다음과 같다.

  • 함수는 정의할 때 parameter를 어떤 방식으로 나열하느냐에 대한 규칙이 있다.

  • 그 규칙은 다음과 같다.

    ⓵ Regular Positonal Arguments

    ⓶ Postional Arguments with default (맨 끝인 경우)

    ⓷ Variable Length Postional Arguments (*args)

    ⓸ Keyword Only Argument (or with default : 가장 마지막인 경우; 가변 키워드 인자가 없는 경우)

    ⓹ Variable Length Keyword Arguments (가장 마지막 위치)

  • *(asterisk) 는 가변 위치 인자를 받는 동시에, Keyword only arguments 구간을 나눠주는 역할을 한다.

  • 가변 키워드 인자는 ** 로 받으며, 위치상 가장 마지막에 위치한다.

profile
keep calm and carry on

0개의 댓글