[Python] 함수(function) - 인라인 함수, callback, 람다 함수 그리고 클로져


Study/Python  2019. 12. 7. 09:00

안녕하세요. 명월입니다.


이 글은 Python에서 함수(function) - 인라인 함수, callback, 람다 함수 그리고 클로져에 대한 글입니다.


프로그램에서 변수와 제어문을 공부하면 항상 제일 먼저 배우는 것이 이 함수일 듯싶습니다.

함수는 프로그램 단위에서 가장 작은 단위고 코드 재사용을 할 수 있게 하는 문법입니다.


함수의 기본구조는 다음과 같습니다.

# 함수 선언의 키워드는 def로 시작한다. 그 다음은 함수명이고 소괄호로 파라미터를 지정한다.
def print_data(param):
  print(param)

print_data("hello world")

함수는 def로 정의해서 끝에는 콜론(:)을 넣습니다. 함수 영역의 시작은 들여쓰기로 구분을 합니다. 들여쓰기가 끝나는 곳이 함수의 끝입니다.

파라미터는 소괄호 안에 정의를 하는데 함수를 호출하는 곳에서 값을 받고 사용하는 영역입니다.

Python에서 함수명을 작성하는 법은 딱히 정해진 것은 없습니다만, 코드 표준에는 동사+명사의 형식으로 작성하길 권장하고 다른 언어와 동일합니다만, 대부분 대소문자로 동사+명사를 구분을합니다.

예로 자바는 printData가 될꺼고 C#은 PrintData로 함수명을 작성합니다만, Python은 언더바로 구분을 합니다.


Python에서는 이 파라미터 사용하는 방법이 두가지가 있습니다. 하나는 위치로서 파리미터 값을 전달하는 방법(일반적인 방법)이 있고, 키워드로서 파라미터 값을 전달하는 방법이 있습니다.

def print_data(param1, param2):
  print(param1 + ' ' +param2)
# 위치로서 파라미터를 전달하는 방식
print_data("hello","world")
# 키워드로서 파라미터를 전달하는 방식
print_data(param2 = "everyone", param1 = "hi,")

보통의 언어는 위치로서 파라미터를 전달하는 방식이 보통입니다만, 키워드는 생소하네요. 쓸일이 있을까 싶습니다.

그리고 파라미터의 기본값을 설정할 수도 있습니다.

# 파라미터가 없이 호출이 되면 Not Data를 param1에 설정한다.
def print_data(param1="Not data"):
  print(param1)
# 이것은 param1이 Not Data로 설정됩니다.
print_data()
# 이것은 param1이 hey로 설정됩니다.
print_data("hey")

그리고 파라미터를 가변으로 설정할 수도 있습니다.

# *표시를 넣으므로 해서 param의 파라미터는 가변이 된다.
def print_data(*param):
  print(param)
  for p in param:
    print(p)
	
# 파라미터를 4개를 넣어도 된다.
print_data(1,2,3,4)
# 파라미터를 2개를 넣어도 된다.
print_data("a","b")

결과를 보시면 *param은 튜플로 변환이 되서 받습니다.


파라미터를 딕셔너리 형태로도 받을 수 있습니다.

# *표시를 두개 넣으면 이는 키워드로써 파라미터를 넘기면 딕셔너리로 변환된다.
def print_data(**param):
  print(param)
  for p in param:
    print("key - " + p + " value = " + str(param[p]))
print_data(a=1,b=2)

Python은 Javascript와 비슷한 성질이 많습니다.

먼저 인라인 함수로써 함수안에 함수를 설정할 수 있습니다.

def get_function():
  # 함수안에 함수를 선언했다.
  def exec_function():
    print("hello world")
  # 그리고 호출을 했다.
  exec_function()

#get_function함수를 호출하면 exec_function함수를 만들고 호출한다.
get_function()

함수를 비슷하게 오브젝트 형식으로 설정할 수 있습니다.

def get_function(param):
  def exec_function1():
    print("exec_function1")
  def exec_function2():
    print("exec_function2")
  ## param이 1이 오면 ret 변수에 exec_function1 함수를 넣는다.(함수가 오브젝트처럼 사용됩니다.)
  if param == 1:
    ret = exec_function1
  ## param이 1이 아닌 값이 오면 ret 변수에 exec_function2 함수를 넣는다.(함수가 오브젝트처럼 사용됩니다.)
  else:
    ret = exec_function2
  return ret

func = get_function(1)
func()
func = get_function(2)
func()

get_function안에서 함수 자체를 리턴합니다. 자바스크립트를 잘하시는 분이라면 여기서부터 어느 정도 감이 오실 것입니다.


콜백(callback)은 visit 패턴으로 함수를 집어 넣는 것을 뜻합니다.

def get_function(param, cb = None):
  print(param)
  # 사실 isinstance함수로 확인할 수 있는 방법이 있습니다만 import를 알아야 사용할 수 있기 때문에 나중에 다루겠습니다.
  # cb가 None아니고 타입이 function인 경우는 cb를 호출한다.
  if cb != None and str(type(cb)) == "<class 'function'>":
    cb()

def callback():
  print("callback")

get_function(1)
get_function(2,callback)

Javascript처럼 callback이 만들어 졌습니다. 역시 스크립트 언어라 비슷한 점이 많이 있는 것 같네요.

참고로 None이 나왔는데 다른 프로그램에서 null과 같은 의미로 값이 없다라는 의미입니다.


이쯤되면 Javascript처럼 클로져 기능과 람다식이 있는지 궁금해집니다. 역시 Python에도 클로져 기능이 있고 람다식이 구현 가능합니다.


먼저 클로져 기능이란 함수 안에서 선언한 값이 아닌 외부에서 선언한 값을 사용하는 방법을 말합니다.

def call_function():
  data = "closure"
  def inline_function():
	# inline_function안에는 data를 선언하지 않고 상위 call_function에 data를 선언했습니다. 그러나 클로져 기능으로 가져와 사용할 수 있습니다.
    print(data)
  inline_function()

call_function()

람다식은 어떻게 구현될까요?

def call_function(param, func):
  if func != None and str(type(func)) == "<class 'function'>":
    func(param)
		
# lambda라는 키워드를 사용해서 파라미터를 설정하고 콜론(:)뒤에 실행식을 작성합니다.
call_function("hello world", lambda p: print(p))

lambda식 작성법은 컴프리헨션과 비슷합니다. if else를 작성할 때 if는 앞의 값, else는 뒤의 값이 실행됩니다.

def call_function(param, func):
  if func != None and str(type(func)) == "<class 'function'>":
    func(param)

# p의 값이 None이면 None을 출력하고 아니면 파라미터의 값을 출력하라는 의미입니다.
Anonymous = lambda p: print(p) if p != None else print("None")

call_function("hello world", Anonymous)
call_function(None, Anonymous)

여기까지 Python에서 함수(function) - 인라인 함수, callback, 람다 함수 그리고 클로져에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.