본문 바로가기

파이썬 (python)

[python 17] 함수 - Part 3 : return

 

 

 

함수가 호출되면, 함수 실행이 완료된 후에 호출의 반대방향으로 반환(return)이 이루어진다. 이번 글에서는 이 return 에 대해 자세히 알아보도록 하자.

 

 

 

○ 함수는 control flow 이다

 

보통 절차 또는 과정으로 정의되는 프로세스(process) 라는 것을 "무엇인가의 흐름 (flow of something)" 으로 설명한다. 즉, 과정을 통해서 무엇인가가 흘러간다. 결국 흘러가는 무엇인가를 캐치하게 되면 그 과정을 파악할 수 있다.

 

여기에서 "무엇인가"에 해당하는 것으로 control, data, message, material 등이 사용되고 있고, 이를 각각 control flow, data flow, message flow, material flow 라고 부른다. 최근에 기계학습 분야에 사용되고 있는 tensor flow 라는 것도 있다. 

 

우리가 공부하고 있는 함수도 콘트롤 플로우(control flow)로 설명되는 하나의 과정이다. 이와 관련해서, [python 8 / control structure] 제목의 글에서 "control 이란 token의 흐름이다" 라는 소제목으로, 실행될 권리를 뜻하는 토큰의 흐름을 통해 명령어의 실행제어가 이루어진다는 것을 설명한 적이 있다. 

 

우선, 프로그램 실행 과정을 이해하기 위한 가장 기본적인 전제는, 프로그램을 구성하는 명령어들 중에 어느 하나의 시점에서는 단 하나의 명령어만 실행이 된다는 것이다. 즉, 우리가 사용하고 있는 컴퓨터의 구조의 제약상 2개 이상의 명령어가 동시에 실행될 수는 없다.

 

이런 관점에서, 아래와 같이 f ( ) 함수에서 g( ) 함수를 호출하는 상황을 가정해 보자.

 

 

 

함수 f( ) 가 실행을 시작하면서 ① 과정을 통해 토큰이 흘러간다. 토큰은 하나의 권리를 나타내며, 하나의 프로그램에는 오직 하나의 토큰만 존재한다. 토큰을 가지고 있는 명령어는 실행될 권리를 가진다. 어떤 명령어에 토큰이 도달하면, 그 명령어가 실행될 기회를 가진다. 실제 실행이 완료되면 순서상으로 그 다음에 실행될 명령어에게 토큰을 넘겨 주게 된다. 이런 로직으로 ①의 과정을 통해 토큰이 흘러와서 드디어 y=g( ) 명령어에 토큰이 도달한 상황이라고 생각해 보자. 그러면, g( ) 함수가 호출되면서 f( )가 가지고 있던 토큰을 g( )함수에게 넘겨주게 된다. 이제 실행될 권한인 토큰을 잃어 버린 f( ) 함수는 실행을 중단하게 된다. 다음에 토큰을 되찾게 되면, 중단된 지점 부터 다시 실행이 재개될 것이다. 

 

y=g( ) 명령어를 살펴보면, assignment 기호(=) 좌변에는 변수 y 가 존재하고 있고, 우변에는 그냥 일반적인 데이터가 아닌 함수가 존재하고 있다. 우리가 알고 있는 assignment는, 예를 들어 a=3, "우변의 값 3을 좌변의 변수 a에 저장하라"는 의미이다. 따라서, assignment의 기본 문법은 "변수 = 값"이 된다. 

 

그런데, 우리가 보고 있는 y=g( ) 에서 우변은 값이 아니라 함수이다. 근데, 우변은 "값"이어야 하니 우변의 g( ) 는 어떤 값을 만드는 과정이 되는 것이다. 실제로 g( )가 실행이 완료되면 그 자체가 어떤 값으로 변환되는 것이다. 우리가 수학 공부하면서 어떤 1차함수 y= g(x) =2x+1 이라고 했을 때, g(3) = 2*3+1=7 이란 값이 된다. 즉, 실제 함수를 실행하면 그 자체가 어떤 값을 나타내게 된다. (항상 그러한 것은 아니다. 아래에서 살펴보자.)

 

함수가 실행을 완료하면 함수 호출 자체가 하나의 값을 나타낸다고 했는데, 그 값은 함수가 반환(return)한 값이 되며 이를 위해 return 이라는 키워드가 사용된다. 

 

아래의 g( ) 함수를 보자. In[1]에서 정의된 g( ) 함수는 하나의 명령어, return 문장 만을 가지고 있다. 여기서, return 3 이란 문장이 실행되면, 이 함수를 호출한 클라이언트에게 3이란 값을 되돌려 주게 된다.  실제로 In[2]에서 g( )함수를 호출 (또는 실행) 했더니 Out[2]으로 3이란 값이 나타난다. 여기서 Out[ ] 값은 셀에서 실행된 명령어에서 return 된 값이 나타나게 된다. 즉, In[ ] 셀에서 호출된 함수가 어떤 값을 리턴하게 되면, Out[ ] 에서 그 값이 출력으로 나타나게 된다. 

 

함수가 호출되고 나서 함수가 실행이 되고 최종적으로 return 문장이 실행되게 되면 그 함수 호출 자체가 반환값이 된다. 따라서, In[4] 와 같은 표현이 가능하다. In[4]에서 y=g( ) 는 "g( ) 함수를 호출하고, 반환된 값을 y 변수에 저장하라는 뜻이 된다."

 

 

 

원래대로 다시 돌아가서, 

 

 

 

f( ) 함수에서 y = g( ) 명령문이 실행되면, g( ) 함수가 호출되면서, f( ) 함수가 가지고 있던 토큰은 g( ) 함수의 첫번째 명령어로 전달되게 된다. 그 토큰은 g( ) 함수의 실행과정인 ③ 과정을 통해 flow 하다가 g( )함수의 마지막 명령어가 실행되면 자신을 호출했던 클라이언트에게 토큰을 반환하게 된다. 즉, 그림에서 ④의 방향으로 토큰이 흘러 콘트롤(프로그램의 통제권) 은 f( ) 함수에게 반환되게 된다. 반환이 이루어지면 y=g( )에서 g( )부분은 g( )함수가 반환한 값으로 대체되어 y변수에 반환값이 저장되게 된다. 그 후에 남아있는 ⑤ 의 과정으로 토큰이 흘러가게 된다. 

 

함수는 반환값이 있는 경우와 반환값이 없는 경우가 있다.  즉, 위의 그림에서 ④를 따라 토큰의 반환이 이루어질때 데이터가 함께 반환되는 경우도 있고 아닌 경우도 있다. 반환값이 없는 경우에는, 아래 그림과 같이, assignment 없이 함수를 호출한다. 물론, 반환값이 있는 경우에도, 그 값을 저장할 필요가 없을 경우에는 아래 그림과 같이 함수를 호출할 수 있다. 

 

[그림] 반환값이 없는 함수의 호출

 

 

 return 은 콘트롤 (토큰) 의 반환이다

 

return 은 "반환하라"는 명령어이다. 실제로 return 문장이 실행되면 그 함수가 가지고 있던 토큰 (control) 을, 자기를 호출한 클라이언트 (함수를 호출한 사람, 함수, 프로그램, 시스템 등을 caller 라고 부른다. 동일한 맥락으로, 호출된 함수를 callee라고 부르기도 한다) 에게 리턴하게 된다. 만약에 return 이 혼자 사용되면 토큰을 리턴하는 것으로 끝나지만, return 이 어떤 값과 같이 사용될 경우에는 control을 caller에게 반환하면서 그 값을 같이 반환하게 된다. 

 

호출된 함수는 return 문으로 종료된다라고 생각하면 좋겠다. 단, 함수의 마지막 명령어 뒤에 사용되는 return 문은, 반환값이 없이 혼자 쓰인 경우에, 생략해도 좋다. 아래 In[8]의 함수 g( )는 3개의 print( )로 구성된 함수이다. 마지막 명령어는 print('3')인데 실제로 이 문장이 실행되면 g( ) 함수에는 더 이상 실행할 문장이 존재하지 않는다. 그러면, 자동적으로 return 이 실행되게 된다. 

 

[그림] return 문이 생략되어 있는 함수

 

함수의 마지막 문장으로서 return을 명시적으로 밝혀줘도 된다. 

 

[그림] return 문이 명시되어 있는 함수

 

함수의 코드 중간에서 반환이 이루어지는 경우에는 return 문장이 반드시 명시되어야 한다. 아래의 코드를 참고하기 바란다. 

 

 

 

○ 리턴 값이 있는 경우 vs. 리턴 값이 없는 경우

 

우리가 어떤 연산을 정의하고자 한다. 우리 함수는 인자로 전달된 3개의 값을 더해주는 함수이다. 함수의 이름을 add( )라고 한다면 다음의 In[12]와 같이 정의할 수 있겠다. 3개의 인자 a, b, c 를 통해 전달된 값을 더해서 s 변수에 저장한 후에 return s 라고 했다. 여기서, return s 는 콘트롤을 caller에게 리턴하면서 s 값도 같이 전달하라는 뜻으로 해석하면 된다.  리턴 값이 있는 함수는 In[13] 에서 보는 것과 같이 함수이름과 인자의 전달로 실행시킬 수도 있고, In[14]에서와 같이 그 결과를 어떤 변수, y, 에 저장할 수도 있다. 

 

 

만약에 우리가 3개의 인자 값, 10, 20, 30 을 넘겨주면서 그 값들을 사용해서 "10+20+30=60"의 형태처럼 출력하는 것이 목적이라면 함수의 정의는 아래와 같을 것이다. 함수의 마지막 문장인 return 문은 반환값이 없는 경우로 생략하였다.

 

 

 

○ 리턴 값이 여러개인 경우

 

함수에 전달하는 인자의 경우에는 개수에 제한이 없지만, 반환되는 값은 항상 2개 이상일 수 없다. 반환값은 없거나 오직 1개만 가능할 뿐이다. 그러다 보니, 여러개의 값을 반환하는 경우에는 그 데이터를 묶어서 하나로 만든 후에 반환해야 한다. 우리가 알고있는 모든 데이터 구조가 리턴 값으로 사용될 수 있다.

 

아래는 튜플을 이용해서 리턴하는 경우이다. 우리가 데이터구조에서 살펴봤듯이, 쉼표( , )로 구분된 데이터는 튜플로 선언된다. 

 

 

 

물론, 명시적으로 튜플로 선언해 주어도 된다. 아래의 예에서 확인해 보자.

 

 

당연히, 리스트로 묶어서 리턴하는 것도 가능하다. 아래의 예에서 확인해 보자.