파이썬 뿐만 아니라 자바스크립트, Swift 를 공부하다보면 일급객체나 일급함수에 대해 자주 들어보았을 것이다.
이는 클로저(Closure) 와 데코레이터(Decorator) 를 이해하는데 핵심적인 개념이기 때문에 한 번쯤 정리하고 넘어가야겠다는 생각을 거듭 해왔었다.
따라서, 이번 포스팅에서는 일급객체와 일급함수의 개념을 살펴보고, 이후 클로저와 데코레이터에 대해 포스팅해볼 예정이다.
📌 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체
여기서, 일반적으로 적용 가능한 연산이란 데이터를 할당하거나 반환하고 전달하는 것을 의미한다.
즉, 변수나 데이터 구조에 할당이 가능하며, 리턴 값으로 반환할 수 있거나, 파라미터로 전달할 수 있다면,
그 객체는 일급객체이다.
이제 일급객체의 조건을 정리해볼 수 있다.
- 변수 혹은 데이터 구조 안에 담을 수 있다.
- 파라미터로 전달할 수 있다.
Return
값으로 사용할 수 있다.
위 세가지 조건을 만족한다면, 그 객체는 일급객체라고 부를 수 있을 것이다.
우리가 언어를 배울 때, 가장 먼저 학습하는 int
float
str
list
와 같은 기본적인 객체들도 사실은 모두 일급객체인 것이다.
그렇다면 일급함수란 무엇일까?
앞서 살펴보았던 일급객체의 조건들을 만족하는 함수가 바로 일급함수이다.
사실, 파이썬이나 자바스크립트에서는 함수마저도 int
나 float
str
등과 같은 기본적인 변수들처럼 일급객체로 취급한다.
즉, 함수도 변수 혹은 데이터 구조 안에 담길 수 있으며, 파라미터로 전달될 수 있고, Return
값으로 사용될 수 있는 것이다.
아래에서 파이썬 예제들을 통해 함수가 실제로 각각의 조건들을 만족하는지 살펴보도록 하자.
# add 함수 생성
def add(x, y):
return x + y
# func 변수에 할당
func = add
# 함수를 변수에 할당할 수 있다.
print(add(1, 2))
print(func(1, 2))
print(add)
print(func)
# Result
3
3
<function add at 0x0000015CC35DF040>
<function add at 0x0000015CC35DF040>
위 코드블럭에서는 add
함수를 생성하고, func
에 담아주었다. 이후, func
를 호출해 파라미터를 넣고 결과를 확인해보니 add
와 동일하게 동작하는 모습을 확인할 수 있었다.
⛔ 메모리 주소를 확인해본 결과, 서로 같은 주소값을 가리키고 있다.
즉, 함수는 변수에 할당이 가능하다는 것을 알 수 있다.
# add 함수 생성
def add(x, y):
return x + y
# sub 함수 생성
def sub(x, y):
return x - y
# list 에 할당
calculator = [add, sub]
for i in calculator:
print(i(3, 1))
# Result
4
2
여기서는 add
와 sub
함수를 생성하고 calculator
리스트에 담아 반복문으로 호출해보았다.
함수는 변수 뿐만 아니라, 리스트와 같은 자료구조에도 할당이 가능하다는 것을 확인할 수 있었다.
# square 함수 생성
def square(x):
return x * x
# first_class 함수 생성
def first_class(func, nums):
return [func(num) for num in nums]
nums = [1, 2, 3, 4, 5]
# first_class 함수 호출
print(first_class(square, nums))
# Result
[1, 4, 9, 16, 25]
위 코드블럭에서는 square
함수와 first_class
함수를 생성해주었다. 이후, first_class
함수의 파라미터로 square
함수와 리스트를 전달해 결과값을 확인해보았다.
결론적으로, square
함수는 리스트 내의 각 원소들에 대해 정상적으로 동작하였고, 함수는 다른 함수의 파라미터로 전달 가능하다는 것을 알 수 있었다.
# greetings 함수 생성
def greetings(name):
# inner 내부 함수 생성
def inner():
print("Hello {}!".format(name))
return inner
# greetings 함수 호출
# func 에 할당
func = greetings("Python")
# 객체 출력
print(func)
# func 호출
func()
# Result
<function greetings.<locals>.inner at 0x000001E6B18C1040>
Hello Python!
위 코드블럭에서는 greetings
함수를 생성하고, 그 안에서 inner
함수를 생성해주었다. 이렇게 함수 안에서 정의된 함수를 내부함수라 한다.
greetings
함수는 내부 함수인 inner
함수를 반환한다.
greetings
함수에 문자열을 파라미터로 넣고 호출 한 뒤 반환값을 func
변수에 할당해주었다. 이 때, func
를 출력해보면 greetings
함수 안의 inner
함수를 가리키고 있는 것을 확인할 수 있다.
즉, inner
함수는 greetings
함수의 반환값이므로, func
는 inner
함수 객체를 가리키고 있다고 볼 수 있다. 이는 앞서 1번 예제에서 살펴본 변수에 함수 객체를 할당할 수 있는 원리와 같다.
마지막 줄에서는 func
를 함수로 호출하고 inner
함수가 정상적으로 동작하는 모습을 확인할 수 있다.
결론적으로, 함수는 다른 함수의 Return
값으로 사용 가능하다는 것을 알 수 있었다.
지금까지 파이썬에서의 함수가 일급객체의 세 가지 조건을 모두 만족한다는 것을 예제를 통해 확인하였고, 이를 통해 파이썬은 일급함수를 지원하는 언어라는 사실을 알 수 있었다.
즉, 파이썬에서 함수는 일반적인 변수나 자료구조들과 같이 하나의 일급객체로 취급되는 것이다.
⛔ 일급객체와 일급함수를 독립적인 개념으로 생각할 필요는 없다. 일급함수도 결국 일급객체일 뿐이다.
다음 포스팅에서는 클로저(Closure) 에 대해 다룰 예정이다.
사실, 우리는 3 번 예제에서 이미 클로저를 경험했다.
3 번 예제를 다시 살펴보면,
func = greetings("Python")
위 구문에서 greetings
함수는 이미 수행되고 실행이 종료되었는데,
func()
func()
를 통해 정상적으로 "Python" 이 다시 불러와진 것을 알 수 있다.
과연 어떻게 "Python" 이라는 파라미터를 계속 기억할 수 있었을까?
이 해답은 클로저에서 찾을 수 있다.