# `functools` — 고차 함수와 콜러블 객체에 대한 연산

소스 코드: Lib/functools.py

`functools` 모듈은 고차 함수를 위한 것입니다: 다른 함수에 작용하거나 다른 함수를 반환하는 함수. 일반적으로, 모든 콜러블 객체는 이 모듈의 목적상 함수로 취급될 수 있습니다.

`functools` 모듈은 다음 함수를 정의합니다:

`@``functools.``cached_property`(func)

  클래스의 메서드를 값이 한 번 계산된 다음 인스턴스 수명 동안 일반 어트리뷰트로 캐시 되는 프로퍼티로 변환합니다. `property()`와 유사하고, 캐싱이 추가되었습니다. 비싸게 계산되고 그 외에는 사실상 불변인 인스턴스의 프로퍼티에 유용합니다.

  예:

      class DataSet:
          def __init__(self, sequence_of_numbers):
              self._data = sequence_of_numbers

          @cached_property
          def stdev(self):
              return statistics.stdev(self._data)

          @cached_property
          def variance(self):
              return statistics.variance(self._data)

  버전 3.8에 추가.

  참고

  이 데코레이터는 각 인스턴스의 `__dict__` 어트리뷰트가 가변 매핑일 것을 요구합니다. 이는 메타 클래스(형 인스턴스의 `__dict__` 어트리뷰트가 클래스 이름 공간에 대한 읽기 전용 프락시이기 때문에)와 `__dict__`를 정의된 슬롯 중 하나로 포함하지 않고 `__slots__`를 지정하는 것(이러한 클래스는 `__dict__` 어트리뷰트를 전혀 제공하지 않기 때문에)과 같은 일부 형에서 작동하지 않음을 의미합니다.

`functools.``cmp_to_key`(func)

  구식 비교 함수를 키 함수로 변환합니다. (`sorted()`, `min()`, `max()`, `heapq.nlargest()`, `heapq.nsmallest()`, `itertools.groupby()`와 같은) 키 함수를 받아들이는 도구와 함께 사용됩니다. 이 함수는 주로 비교 함수 사용을 지원하는 파이썬 2에서 변환되는 프로그램의 전이 도구로 사용됩니다.

  비교 함수는 두 개의 인자를 받아들이고, 그들을 비교하여, 작으면 음수, 같으면 0, 크면 양수를 반환하는 콜러블입니다. 키 함수는 하나의 인자를 받아들이고 정렬 키로 사용할 다른 값을 반환하는 콜러블입니다.

  예:

      sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order

  정렬 예제와 간략한 정렬 자습서는 정렬 HOW TO를 참조하십시오.

  버전 3.2에 추가.

`@``functools.``lru_cache`(user_function)
`@``functools.``lru_cache`(maxsize=128, typed=False)

  가장 최근의 maxsize 호출까지 저장하는 기억하는(memoizing) 콜러블 함수를 감싸는 데코레이터. 비싸거나 I/O 병목 함수가 같은 인자로 주기적으로 호출될 때 시간을 절약할 수 있습니다.

  결과를 캐시 하는 데 딕셔너리가 사용되기 때문에, 함수에 대한 위치와 키워드 인자는 해시 가능해야 합니다.

  서도 다른 인자 패턴은 별도의 캐시 항목을 갖는 별개의 호출로 간주할 수 있습니다. 예를 들어, f(a=1, b=2)와 f(b=2, a=1)은 키워드 인자 순서가 다르며 두 개의 개별 캐시 항목을 가질 수 있습니다.

  user_function이 지정되면, 콜러블이어야 합니다. 이는 lru_cache 데코레이터를 사용자 함수에 직접 적용 할 수 있도록 하며, maxsize를 기본값 128로 유지합니다:

      @lru_cache
      def count_vowels(sentence):
          sentence = sentence.casefold()
          return sum(sentence.count(vowel) for vowel in 'aeiou')

  maxsize가 `None`으로 설정되면, LRU 기능이 비활성화되고 캐시가 제한 없이 커질 수 있습니다.

  typed가 참으로 설정되면, 다른 형의 함수 인자가 별도로 캐시 됩니다. 예를 들어, `f(3)`과 `f(3.0)`은 별개의 결과를 가진 별개의 호출로 취급됩니다.

  캐시의 효과를 측정하고 maxsize 매개 변수를 조정하는 것을 돕기 위해, 래핑 된 함수는 hits, misses, maxsize 및 currsize를 표시하는 네임드 튜플을 반환하는 `cache_info()` 함수로 인스트루먼트 됩니다. 다중 스레드 환경에서, hits와 misses는 근사적(approximate)입니다.

  데코레이터는 캐시를 지우거나 무효로 하기 위한 `cache_clear()` 함수도 제공합니다.

  원래의 하부 함수는 `__wrapped__` 어트리뷰트를 통해 액세스 할 수 있습니다. 이것은 인트로스펙션, 캐시 우회 또는 다른 캐시로 함수를 다시 래핑하는 데 유용합니다.

  LRU (least recently used) 캐시는 가장 최근 호출이 향후 호출에 대한 최상의 예측일 때 가장 잘 작동합니다 (예를 들어, 뉴스 서버에서 가장 인기 있는 기사는 매일 바뀌는 경향이 있습니다). 캐시의 크기 제한은 웹 서버와 같은 오래 실행되는 프로세스에서 제한 없이 캐시가 커지지 않도록 합니다.

  일반적으로, LRU 캐시는 이전에 계산된 값을 재사용하려고 할 때만 사용해야 합니다. 따라서, 부작용이 있는 함수, 각 호출에서 고유한 가변 객체를 만들어야 하는 함수, time()이나 random()과 같은 비순수(impure) 함수를 캐시 하는 것은 의미가 없습니다.

  정적 웹 콘텐츠를 위한 LRU 캐시의 예:

      @lru_cache(maxsize=32)
      def get_pep(num):
          'Retrieve text of a Python Enhancement Proposal'
          resource = 'http://www.python.org/dev/peps/pep-%04d/' % num
          try:
              with urllib.request.urlopen(resource) as s:
                  return s.read()
          except urllib.error.HTTPError:
              return 'Not Found'

      >>> for n in 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
      ...     pep = get_pep(n)
      ...     print(n, len(pep))

      >>> get_pep.cache_info()
      CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

  동적 프로그래밍(dynamic programming) 기법을 구현하기 위해 캐시를 사용하여 피보나치 수를 효율적으로 계산하는 예:

      @lru_cache(maxsize=None)
      def fib(n):
          if n < 2:
              return n
          return fib(n-1) + fib(n-2)

      >>> [fib(n) for n in range(16)]
      [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

      >>> fib.cache_info()
      CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

  버전 3.2에 추가.

  버전 3.3에서 변경: typed 옵션을 추가했습니다.

  버전 3.8에서 변경: user_function 옵션을 추가했습니다.

`@``functools.``total_ordering`

  하나 이상의 풍부한 비교(rich comparison) 순서 메서드를 정의하는 클래스를 주면, 이 클래스 데코레이터가 나머지를 제공합니다. 가능한 모든 풍부한 비교 연산을 지정하는 데 드는 노력이 단순화됩니다:

  클래스는 `__lt__()`, `__le__()`, `__gt__()` 또는 `__ge__()` 중 하나를 정의해야 합니다. 또한, 클래스는 `__eq__()` 메서드를 제공해야 합니다.

  예를 들면:

      @total_ordering
      class Student:
          def _is_valid_operand(self, other):
              return (hasattr(other, "lastname") and
                      hasattr(other, "firstname"))
          def __eq__(self, other):
              if not self._is_valid_operand(other):
                  return NotImplemented
              return ((self.lastname.lower(), self.firstname.lower()) ==
                      (other.lastname.lower(), other.firstname.lower()))
          def __lt__(self, other):
              if not self._is_valid_operand(other):
                  return NotImplemented
              return ((self.lastname.lower(), self.firstname.lower()) <
                      (other.lastname.lower(), other.firstname.lower()))

  참고

  이 데코레이터를 사용하면 올바르게 동작하는 전 순서(totally ordered) 형을 쉽게 만들 수 있지만, 파생된 비교 메서드에서 실행 속도가 느려지고 스택 트레이스가 더 복잡해지는 대가를 지불합니다. 성능 벤치마킹이 이것이 특정 응용 프로그램의 병목임을 가리키면, 6가지의 풍부한 비교 메서드를 모두 구현하여 속도를 쉽게 높일 수 있습니다.

  버전 3.2에 추가.

  버전 3.4에서 변경: 인식할 수 없는 형에 대해 하부 비교 함수에서 NotImplemented를 반환하는 것이 이제 지원됩니다.

`functools.``partial`(func, /, *args, **keywords)

  호출될 때 위치 인자 args와 키워드 인자 keywords로 호출된 func처럼 동작하는 새 partial 객체를 반환합니다. 더 많은 인자가 호출에 제공되면, args에 추가됩니다. 추가 키워드 인자가 제공되면, keywords를 확장하고 대체합니다. 대략 다음과 동등합니다:

      def partial(func, /, *args, **keywords):
          def newfunc(*fargs, **fkeywords):
              newkeywords = {**keywords, **fkeywords}
              return func(*args, *fargs, **newkeywords)
          newfunc.func = func
          newfunc.args = args
          newfunc.keywords = keywords
          return newfunc

  `partial()`은 함수의 인자 및/또는 키워드의 일부를 “고정”하여 서명이 단순화된 새 객체를 생성하는 부분 함수 응용에 사용됩니다. 예를 들어, `partial()`을 사용하여 base 인자의 기본값이 2이면서 `int()` 함수 같은 동작을 하는 콜러블을 만들 수 있습니다:

      >>> from functools import partial
      >>> basetwo = partial(int, base=2)
      >>> basetwo.__doc__ = 'Convert base 2 string to an int.'
      >>> basetwo('10010')
      18

class `functools.``partialmethod`(func, /, *args, **keywords)

  직접 호출하기보다는 메서드 정의로 사용되도록 설계된 것을 제외하고는 `partial`과 같이 동작하는 새 `partialmethod` 디스크립터를 반환합니다.

  func는 디스크립터나 콜러블이어야 합니다 (일반 함수처럼 둘 모두인 객체는 디스크립터로 처리됩니다).

  func가 디스크립터(가령 일반 파이썬 함수, `classmethod()`, `staticmethod()`, `abstractmethod()` 또는 `partialmethod`의 다른 인스턴스)이면, `__get__`에 대한 호출은 하부 디스크립터에 위임되고, 적절한 partial 객체가 결과로 반환됩니다.

  func가 디스크립터가 아닌 콜러블이면, 적절한 연결된 메서드가 동적으로 만들어집니다. 이것은 메서드로 사용될 때 일반 파이썬 함수처럼 작동합니다: `partialmethod` 생성자에 제공된 args와 keywords보다도 전에 self 인자가 첫 번째 위치 인자로 삽입됩니다.

  예:

      >>> class Cell(object):
      ...     def __init__(self):
      ...         self._alive = False
      ...     @property
      ...     def alive(self):
      ...         return self._alive
      ...     def set_state(self, state):
      ...         self._alive = bool(state)
      ...     set_alive = partialmethod(set_state, True)
      ...     set_dead = partialmethod(set_state, False)
      ...
      >>> c = Cell()
      >>> c.alive
      False
      >>> c.set_alive()
      >>> c.alive
      True

  버전 3.4에 추가.

`functools.``reduce`(function, iterable[, initializer])

  두 인자의 function을 왼쪽에서 오른쪽으로 iterable의 항목에 누적적으로 적용해서, 이터러블을 단일 값으로 줄입니다. 예를 들어, `reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])`는 `((((1+2)+3)+4)+5)`를 계산합니다. 왼쪽 인자 x는 누적값이고 오른쪽 인자 y는 iterable에서 온 갱신 값입니다. 선택적 initializer가 있으면, 계산에서 이터러블의 항목 앞에 배치되고, 이터러블이 비어있을 때 기본값의 역할을 합니다. initializer가 제공되지 않고 iterable에 하나의 항목만 포함되면, 첫 번째 항목이 반환됩니다.

  대략 다음과 동등합니다:

      def reduce(function, iterable, initializer=None):
          it = iter(iterable)
          if initializer is None:
              value = next(it)
          else:
              value = initializer
          for element in it:
              value = function(value, element)
          return value

  모든 중간값을 산출하는 이터레이터는 `itertools.accumulate()`를 참조하십시오.

`@``functools.``singledispatch`

  함수를 싱글 디스패치 제네릭 함수로 변환합니다.

  제네릭 함수를 정의하려면, `@singledispatch` 데코레이터로 데코레이트 하십시오. 디스패치는 첫 번째 인자의 형으로 일어납니다, 그에 따라 함수를 작성하십시오:

      >>> from functools import singledispatch
      >>> @singledispatch
      ... def fun(arg, verbose=False):
      ...     if verbose:
      ...         print("Let me just say,", end=" ")
      ...     print(arg)

  오버로드 된 구현을 함수에 추가하려면, 제네릭 함수의 `register()` 어트리뷰트를 사용하십시오. 데코레이터입니다. 형으로 어노테이트 된 함수의 경우, 데코레이터는 첫 번째 인자의 형을 자동으로 유추합니다:

      >>> @fun.register
      ... def _(arg: int, verbose=False):
      ...     if verbose:
      ...         print("Strength in numbers, eh?", end=" ")
      ...     print(arg)
      ...
      >>> @fun.register
      ... def _(arg: list, verbose=False):
      ...     if verbose:
      ...         print("Enumerate this:")
      ...     for i, elem in enumerate(arg):
      ...         print(i, elem)

  형 어노테이션을 사용하지 않는 코드의 경우, 적절한 형 인자를 데코레이터 자체에 명시적으로 전달할 수 있습니다:

      >>> @fun.register(complex)
      ... def _(arg, verbose=False):
      ...     if verbose:
      ...         print("Better than complicated.", end=" ")
      ...     print(arg.real, arg.imag)
      ...

  람다와 사전에 존재하는 함수를 등록할 수 있도록, `register()` 어트리뷰트를 다음과 같은 함수적인 형태로 사용할 수 있습니다:

      >>> def nothing(arg, verbose=False):
      ...     print("Nothing.")
      ...
      >>> fun.register(type(None), nothing)

  `register()` 어트리뷰트는 데코레이트 되지 않은 함수를 반환해서 데코레이터 스태킹, 피클링 뿐만 아니라 각 변형에 대한 단위 테스트를 독립적으로 만드는 것을 가능하게 합니다:

      >>> @fun.register(float)
      ... @fun.register(Decimal)
      ... def fun_num(arg, verbose=False):
      ...     if verbose:
      ...         print("Half of your number:", end=" ")
      ...     print(arg / 2)
      ...
      >>> fun_num is fun
      False

  호출되면, 제네릭 함수는 첫 번째 인자의 형에 따라 디스패치 합니다:

      >>> fun("Hello, world.")
      Hello, world.
      >>> fun("test.", verbose=True)
      Let me just say, test.
      >>> fun(42, verbose=True)
      Strength in numbers, eh? 42
      >>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
      Enumerate this:
      0 spam
      1 spam
      2 eggs
      3 spam
      >>> fun(None)
      Nothing.
      >>> fun(1.23)
      0.615

  특정 형에 대해 등록된 구현이 없으면, 더 일반적인 구현을 찾는 데 메서드 결정 순서가 사용됩니다. `@singledispatch`로 데코레이트 된 원래 함수는 베이스 `object` 형으로 등록되어서, 더 나은 구현이 발견되지 않으면 사용됩니다.

  제네릭 함수가 주어진 형에 대해 어떤 구현을 선택하는지 확인하려면 `dispatch()` 어트리뷰트를 사용하십시오:

      >>> fun.dispatch(float)
      <function fun_num at 0x1035a2840>
      >>> fun.dispatch(dict)    # note: default implementation
      <function fun at 0x103fe0000>

  등록된 모든 구현에 액세스하려면, 읽기 전용 `registry` 어트리뷰트를 사용하십시오:

      >>> fun.registry.keys()
      dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
                <class 'decimal.Decimal'>, <class 'list'>,
                <class 'float'>])
      >>> fun.registry[float]
      <function fun_num at 0x1035a2840>
      >>> fun.registry[object]
      <function fun at 0x103fe0000>

  버전 3.4에 추가.

  버전 3.7에서 변경: `register()` 어트리뷰트는 형 어노테이션 사용을 지원합니다.

class `functools.``singledispatchmethod`(func)

  메서드를 싱글 디스패치 제네릭 함수로 변환합니다.

  제네릭 메서드를 정의하려면, `@singledispatchmethod` 데코레이터로 장식하십시오. 디스패치는 첫 번째 self가 아니거나 cls가 아닌 인자의 형에 따라 일어납니다, 그에 따라 함수를 작성하십시오:

      class Negator:
          @singledispatchmethod
          def neg(self, arg):
              raise NotImplementedError("Cannot negate a")

          @neg.register
          def _(self, arg: int):
              return -arg

          @neg.register
          def _(self, arg: bool):
              return not arg

  `@singledispatchmethod`는 `@classmethod`와 같은 다른 데코레이터와의 중첩을 지원합니다. `dispatcher.register`를 허용하기 위해, `singledispatchmethod`는 가장 바깥 데코레이터이어야 함에 유의하십시오. 다음은 `neg` 메서드가 클래스에 연결되는 `Negator` 클래스입니다:

      class Negator:
          @singledispatchmethod
          @classmethod
          def neg(cls, arg):
              raise NotImplementedError("Cannot negate a")

          @neg.register
          @classmethod
          def _(cls, arg: int):
              return -arg

          @neg.register
          @classmethod
          def _(cls, arg: bool):
              return not arg

  다른 유사한 데코레이터에도 같은 패턴을 사용할 수 있습니다: `staticmethod`, `abstractmethod` 및 기타.

  버전 3.8에 추가.

`functools.``update_wrapper`(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

  wrapped 함수처럼 보이도록 wrapper 함수를 갱신합니다. 선택적 인자는 원래 함수의 어떤 어트리뷰트가 wrapper 함수의 일치하는 어트리뷰트에 직접 대입되고 wrapper 함수의 어떤 어트리뷰트가 원래 함수의 해당 어트리뷰트로 갱신되는지 지정하는 튜플입니다. 이 인자들의 기본값은 모듈 수준 상수 `WRAPPER_ASSIGNMENTS`(wrapper 함수의 `__module__`, `__name__`, `__qualname__`, `__annotations__` 및 `__doc__` 독스트링에 대입합니다)와 `WRAPPER_UPDATES`(wrapper 함수의 `__dict__`, 즉 인스턴스 딕셔너리를 갱신합니다)입니다.

  내부 검사와 기타 목적(예를 들어 `lru_cache()`와 같은 캐싱 데코레이터 우회)을 위해 원래 함수에 액세스 할 수 있도록, 이 함수는 래핑 되는 함수를 가리키는 `__wrapped__` 어트리뷰트를 wrapper에 자동으로 추가합니다.

  이 함수의 주요 용도는 데코레이트 된 함수를 래핑하고 wrapper를 반환하는 데코레이터 함수에서 사용하는 것입니다. wrapper 함수가 갱신되지 않으면, 반환된 함수의 메타 데이터는 원래 함수 정의가 아닌 wrapper 정의를 반영하게 되어 일반적으로 도움이 되지 않습니다.

  `update_wrapper()`는 함수 이외의 콜러블과 함께 사용할 수 있습니다. 래핑 되는 객체에서 누락된 assigned나 updated로 이름 지정된 어트리뷰트는 무시됩니다 (즉, 이 함수는 wrapper 함수에서 그 어트리뷰트를 설정하려고 시도하지 않습니다). wrapper 함수 자체에 updated에 이름 지정된 어트리뷰트가 없으면 여전히 `AttributeError`가 발생합니다.

  버전 3.2에 추가: `__wrapped__` 어트리뷰트 자동 추가.

  버전 3.2에 추가: 기본적으로 `__annotations__` 어트리뷰트의 복사.

  버전 3.2에서 변경: 누락된 어트리뷰트는 더는 `AttributeError`를 발생시키지 않습니다.

  버전 3.4에서 변경: `__wrapped__` 어트리뷰트는 이제 해당 함수가 `__wrapped__` 어트리뷰트를 정의한 경우에도 항상 래핑 된 함수를 참조합니다. (bpo-17482를 참조하십시오)

`@``functools.``wraps`(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

  래퍼 함수를 정의할 때 함수 데코레이터로 `update_wrapper()`를 호출하기 위한 편의 함수입니다. `partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`와 동등합니다. 예를 들면:

      >>> from functools import wraps
      >>> def my_decorator(f):
      ...     @wraps(f)
      ...     def wrapper(*args, **kwds):
      ...         print('Calling decorated function')
      ...         return f(*args, **kwds)
      ...     return wrapper
      ...
      >>> @my_decorator
      ... def example():
      ...     """Docstring"""
      ...     print('Called example function')
      ...
      >>> example()
      Calling decorated function
      Called example function
      >>> example.__name__
      'example'
      >>> example.__doc__
      'Docstring'

  이 데코레이터 팩토리를 사용하지 않으면, example 함수의 이름은 `'wrapper'`가 되고, 원래 `example()`의 독스트링은 잃어버리게 됩니다.

## `partial` 객체

`partial` 객체는 `partial()`이 만든 콜러블 객체입니다. 세 가지 읽기 전용 어트리뷰트가 있습니다:

`partial.``func`

  콜러블 객체나 함수. `partial` 객체에 대한 호출은 새로운 인자와 키워드와 함께 `func`로 전달됩니다.

`partial.``args`

  `partial` 객체 호출에 제공되는 위치 인자 앞에 추가될 가장 왼쪽 위치 인자들입니다.

`partial.``keywords`

  `partial` 객체가 호출될 때 제공될 키워드 인자들입니다.

`partial` 객체는 콜러블이고, 약한 참조 가능하며, 어트리뷰트를 가질 수 있다는 점에서 `function` 객체와 같습니다. 몇 가지 중요한 차이점이 있습니다. 예를 들어, `__name__`과 `__doc__` 어트리뷰트는 자동으로 만들어지지 않습니다. 또한, 클래스에 정의된 `partial` 객체는 정적 메서드처럼 동작하며 인스턴스 어트리뷰트 조회 중에 연결된 메서드로 변환되지 않습니다.