본문 바로가기
관리자

Programming-[Backend]/Python

파이썬 중급 - 5. 고위 함수(Higher Order Function), 클로저(Closure) 기본

728x90
반응형

 

파이썬 함수 특징

  1. 런타임 초기화
  2. 변수 할당 가능
  3. 함수 인수 전달 가능
  4. 함수 결과 반환 가능(return)

함수는 클래스가 갖는 매직메서드도 갖고 있다. 함수나 클래스에 포함된 함수 목록을 조회하는 dir 함수를 통해서 클래스 부분은 제외하고 함수 부분에만 있는 함수 목록을 확인해볼 수 있다. 강의에서는 factorial 함수를 예시로 들면서 설명한다. 다음 내용에서 배울 closure, call 등이 있는 것을 볼 수 있다.

def factorial(n):
    '''Factorial Function n: int'''
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

class A:
    pass

print(factorial(5))
print(factorial.__doc__)
print(type(factorial), type(A))
print(set(sorted(dir(factorial))) - set(sorted(dir(A))))

#{'__globals__', '__closure__', '__code__', '__get__', '__call__', '__name__', '__annotations__', '__defaults__', '__kwdefaults__', '__qualname__'}

함수를 변수에 할당하여 활용할 수 있다.

# 변수 할당
var_func = factorial

print(var_func)
print(var_func(10))
print(list(var_func(n) for n in range(1, 11)))
#[1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

 

 

 

 

고위 함수(Higher Order Function)

함수를 인수로 전달하거나 함수로 결과를 반환할 수 있다. 대표적인 함수로 map, filter, reduce, lambda 등이 있다.

다른 함수의 인자로 함수를 전달한다. 아래 코드에서 첫 번째 줄은 map 함수의 함수인자로 var_func가 전달된 것이고, 두 번째 줄은 list를 만드는 대괄호 [] 안에 var_func가 인자로 전달된 것이다.

print(list(map(var_func, filter(lambda x: x % 2, range(1,6)))))
print([var_func(i) for i in range(1, 6) if i % 2])

 

예시 : reduce, 익명함수(lambda)

from functools import reduce
from operator import add

# reduce
print(reduce(add, range(1, 11)))
print(sum(range(1,11))) #더 나음

# 익명함수(lambda)
# 가급적 주석 달기
# 가급적 일반 함수로 리팩토링 권장
print(reduce(lambda x, t: x + t, range(1, 11)))

 

 

 

 

callable과 partial

callable은 메소드 형태로 호출이 가능한지 확인하고, partial은 함수의 인수를 고정할 수 있는 함수이다. 아래 코드에서 다른 함수들의 인자로 사용되었다.

# Callable : 메소드 형태로 호출이 가능한지 확인한다.
print(callable(str), callable(list), callable(5))
#True True False

# Partial : 인수를 고정하여 콜백 함수로 사용한다.
from operator import mul
from functools import partial

print(mul(10, 10))

# 인수 고정
five = partial(mul, 5)
print(five(10)) # 50
print([five(i) for i in range(1, 11)]) # [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

# 고정 추가
six = partial(five, 6)
print(six()) # 30

 

 

 

 

클로저 기본

클로저는 함수의 실행이 끝나더라도 함수 내부의 지역 변수 값들을 기억한다는 개념이다. 웹 서버에서 하나의 값(메모리)에 여러 클라이언트가 접근하여 값을 동시에 변경하면 각 클라이언트에게 알맞는 값을 전달하지 못하는 동시성 이슈가 발생할 수 있다. 이런 이슈를 방지하기 위해서 ‘공유하되, 변경하지 않는(Immutable, Read-Only)’ 개념을 적용한 것이 클로저이다.

 

 

 

변수 선언 복습 예제

클로저의 개념을 이해하기 위해서 기본적인 변수 선언 룰을 복습한다.

b = 20

def func1(a):
	print(a)
	print(b)

func1(10) #10 #20 #func1 내부에서 지역 변수 b를 찾고, 없으면 전역 변수인 b값을 참조하여 출력
c = 30

def func2(a):
	print(a)
	print(c)
	c = 40

func2(10) # Error. 지역 변수 c값을 참조하는데, print문 아래에 c가 정의되었으므로 에러가 난다.
c = 30

def func3(a):
	global c
	print(a)
	print(c)
	c = 50

func3(10)
print(c)
#10
#30
#50
#global 키워드로 전역 변수를 참조하도록 하면, 함수 내부에서 c = 50으로 재할당 시 전역 변수가 초기화된다.

 

 

 

클로저 예제

중간에 선언된 값을 저장하는 예시들을 공부해본다. sum이나 reduce등은 값을 누적하므로 중간값들을 저장한다. 클로저의 개념을 활용하는 것이다. 평균을 구하는 Averager 클래스를 작성하면서 직접 클로저의 개념을 익혀본다.

 

class Averager():
    def __init__(self):
        self._series = []

    def __call__(self, v):
        self._series.append(v)
        print('inner >> {} / {}'.format(self._series, len(self._series)))
        return sum(self._series) / len(self._series)

# 인스턴스 생성
average_cls = Averager()

# 누적 실행
print(average_cls(10))
print(average_cls(30))
print(average_cls(50))
print(average_cls(123))

# 결과
# inner >> [10] / 1
# 10.0
# inner >> [10, 30] / 2
# 20.0
# inner >> [10, 30, 50] / 3
# 30.0
# inner >> [10, 30, 50, 123] / 4
# 53.25

 


참조

 

1. 인프런 강의 - 우리를 위한 프로그래밍 : 파이썬 중급 (Inflearn Original)

https://www.inflearn.com/course/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%A4%91%EA%B8%89-%EC%9D%B8%ED%94%84%EB%9F%B0-%EC%98%A4%EB%A6%AC%EC%A7%80%EB%84%90/dashboard

728x90
반응형