Victoree's Blog

[4] 객체로서의 함수 - 일급 함수와 고위함수, 데코레이터와 클로저 본문

Python/Fluent Python

[4] 객체로서의 함수 - 일급 함수와 고위함수, 데코레이터와 클로저

victoree 2023. 6. 20. 18:48
728x90

1. 일급 객체와 고위 함수

파이썬에서는 함수도 일급 객체이다. 일급 객체란 다른 객체들에 일반적으로 적용가능한 연산을 제공하는 객체이다.

  • 런타임에 생성 가능
  • 데이터 구조체의 변수나 요소에 할당 가능
  • 함수 인수로 전달 가능
  • 함수 결과로 반환 가능

고위 함수란 함수를 인수로 전달하거나 함수로 결과를 반환할 수 있다. 데코레이터도 고위함수에 속한다.
대표적인 함수로 map, filter, reduce, lambda 등이 있다. map, filter, reduce 같은 함수들은 사실 제너레이터 표현식과 지능형 리스트 이후 중요도가 좀 떨어졌다. reduce는 합계용 함수로, 다른 reduction 함수로는 all(), any() 등이 있다. 이런 reduction 함수는 특정 결과를 하나의 값에 누적한다. sorted() 함수도 일급함수에 포함된다. sorted()함수는 key 파라미터로 함수를 전달받아 정렬할 각 원소에 적용한다. 

>>> fruits = ['apple', 'banana', 'strawberry', 'blueberry', 'melon']
>>> sorted(fruits, key=len)
['apple', 'melon', 'banana', 'blueberry', 'strawberry']

2. 데코레이터와 클로저

데코레이터는 다른 함수를 인수로 받는 callable 함수이다. 반드시 함수를 return하며, 데코레이트된 함수를 다른 함수로 대처하는 능력이 있다. 로깅, 프레임워크, 유효성 체크)을 갖는 공통 코드를 함수화할 수 있고, 여러 곳에서 재사용 할 수 있다. 하지만 로직이 캡슐화되므로 가독성이 감소하고 디버깅이 불편해질 수 있다.
데코레이터에는 다양한 종류가 있는데, 그중에 하나는 등록 데코레이터다. 등록 데코레이터는 특정 함수나 객체를 어떤 종류라고 등록하는 용도로 목적이 명확하고, 특정 함수들은 등록 데코레이터를 제거함으로써 역할을 제거할 수 있다.
내가 실무에서 사용해본 데코레이터들이 뭐가 있을지 코드들을 살펴봤는데,

  • @abstractmethod 로 클래스 내에 추상메소드를 선언
  • @pytest -> pytest 실행 시 돌아가야하는 등록 데코레이터
  • @router.get() 과 같은 api 인터페이스에 붙이는 데코레이터
  • @property 객체 내 엘리먼트에 요소로 접근할 수 있도록 정의하는 데코레이터
  • @lru_cache 로 최근 사용했던 함수의 결과값을 기억하는 메모제이션용 데코레이터
  • @classmethod 로 클래스 메소드임을 선언
  • @staticmethod 로 정적 메소드 선언, 잘 사용할 일이 없음

bold체로 해놓은 데코레이터들은 python 표준 데코레이터이다.

클로저는 함수의 실행이 끝나더라도 함수 내부의 지역 변수 값들을 기억한다는 개념이다. 함수 정의 시, 자유 변수에 대한 바인딩을 유지하는 함수이다. 클로저에서는 함수 내부에 다른 함수를 두어 자유 변수(Free variable)을 기억하도록 만들 수 있다.

def closure_ex1():
    # Free variable
    # 클로저 영역
    series = []

    def averager(v):
        series.append(v)
        print("inner >>> {} / {}".format(series, len(series)))
        return sum(series) / len(series)

    return averager

nonlocal은 클로저내에서 숫자나 튜플, 문자열 등을 갱신할 때 지역변수로 사용하지 않도록 하기위한 키워드이다.

def closure_ex2():
    #Free variable
    cnt = 0
    total = 0
    def averager(v):
        nonlocal cnt, total
        cnt += 1
        total += v
        return total / cnt
    return averager

avg_closure2 = closure_ex2()
print(avg_closure2(10))
print(avg_closure2(30))

averager 함수 내에서 cnt,와 total을 지역변수로 착각하여 UnboundLocalError가 발생할 수 있어서, nonlocal 키워드로 지역변수를 해제해주는 것이다. 

728x90
Comments