functools
--- 고차 함수와 콜러블 객체에 대한 연산¶
소스 코드: Lib/functools.py
functools
모듈은 고차 함수를 위한 것입니다: 다른 함수에 작용하거나 다른 함수를 반환하는 함수. 일반적으로, 모든 콜러블 객체는 이 모듈의 목적상 함수로 취급될 수 있습니다.
functools
모듈은 다음 함수를 정의합니다:
-
@
functools.
cache
(user_function)¶ 단순하고 가벼운 무제한 함수 캐시. 때때로 "memoize"라고도 합니다.
lru_cache(maxsize=None)
와 같은 것을 반환하여, 함수 인자의 딕셔너리 조회 주위로 얇은 래퍼를 만듭니다. 이전 값을 제거할 필요가 없어서, 크기 제한이 있는lru_cache()
보다 작고 빠릅니다.예를 들면:
@cache def factorial(n): return n * factorial(n-1) if n else 1 >>> factorial(10) # 이전에 캐시 된 결과가 없습니다, 11번의 재귀 호출을 만듭니다 3628800 >>> factorial(5) # 캐시 된 값 결과를 조회만 합니다 120 >>> factorial(12) # 두 개의 새로운 재귀 호출을 만듭니다, 다른 10개는 캐시 된 것입니다 479001600
버전 3.9에 추가.
-
@
functools.
cached_property
(func)¶ 클래스의 메서드를 값이 한 번 계산된 다음 인스턴스 수명 동안 일반 어트리뷰트로 캐시 되는 프로퍼티로 변환합니다.
property()
와 유사하고, 캐싱이 추가되었습니다. 비싸게 계산되고 그 외에는 사실상 불변인 인스턴스의 프로퍼티에 유용합니다.예:
class DataSet: def __init__(self, sequence_of_numbers): self._data = tuple(sequence_of_numbers) @cached_property def stdev(self): return statistics.stdev(self._data)
cached_property()
의 메커니즘은property()
와 다소 다릅니다. 일반 프로퍼티는 setter가 정의되지 않은 경우 어트리뷰트 쓰기를 차단합니다. 이와는 달리, cached_property는 쓰기를 허용합니다.cached_property 데코레이터는 조회 시에만, 같은 이름의 어트리뷰트가 존재하지 않을 때만 실행됩니다. 실행되면, cached_property는 같은 이름의 어트리뷰트에 기록합니다. 후속 어트리뷰트 읽기와 쓰기는 cached_property 메서드보다 우선하며 일반 어트리뷰트처럼 작동합니다.
캐시 된 값은 어트리뷰트를 삭제하여 지울 수 있습니다. 이렇게 하면 cached_property 메서드가 다시 실행됩니다.
이 데코레이터는 PEP 412 키 공유 딕셔너리의 작동을 방해함에 유의하십시오. 이는 인스턴스 딕셔너리가 평소보다 더 많은 공간을 차지할 수 있음을 의미합니다.
또한, 이 데코레이터는 각 인스턴스의
__dict__
어트리뷰트가 가변 매핑일 것을 요구합니다. 이는 메타 클래스(형 인스턴스의__dict__
어트리뷰트가 클래스 이름 공간에 대한 읽기 전용 프락시이기 때문에)와__dict__
를 정의된 슬롯 중 하나로 포함하지 않고__slots__
를 지정하는 것(이러한 클래스는__dict__
어트리뷰트를 전혀 제공하지 않기 때문에)과 같은 일부 형에서 작동하지 않음을 의미합니다.가변 매핑을 사용할 수 없거나 공간 효율적인 키 공유가 필요하면,
cache()
위에property()
를 쌓아서cached_property()
와 유사한 효과를 얻을 수 있습니다:class DataSet: def __init__(self, sequence_of_numbers): self._data = sequence_of_numbers @property @cache def stdev(self): return statistics.stdev(self._data)
버전 3.8에 추가.
-
functools.
cmp_to_key
(func)¶ 구식 비교 함수를 키 함수로 변환합니다. (
sorted()
,min()
,max()
,heapq.nlargest()
,heapq.nsmallest()
,itertools.groupby()
와 같은) 키 함수를 받아들이는 도구와 함께 사용됩니다. 이 함수는 주로 비교 함수 사용을 지원하는 파이썬 2에서 변환되는 프로그램의 전이 도구로 사용됩니다.비교 함수는 두 개의 인자를 받아들이고, 그들을 비교하여, 작으면 음수, 같으면 0, 크면 양수를 반환하는 콜러블입니다. 키 함수는 하나의 인자를 받아들이고 정렬 키로 사용할 다른 값을 반환하는 콜러블입니다.
예:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # 로케일을 고려한 정렬 순서
정렬 예제와 간략한 정렬 자습서는 정렬 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와 typed의 값을 표시하는 새
dict
를 반환하는cache_parameters()
함수로 인스트루먼트 됩니다. 이것은 정보 제공만을 위한 것입니다. 값을 변경해도 효과가 없습니다.캐시의 효과를 측정하고 maxsize 매개 변수를 조정하는 것을 돕기 위해, 래핑 된 함수는 hits, misses, maxsize 및 currsize를 표시하는 네임드 튜플을 반환하는
cache_info()
함수로 인스트루먼트 됩니다. 다중 스레드 환경에서, hits와 misses는 근사적(approximate)입니다.flowdas
데코레이트 된 함수에
cache_parameters()
와cache_info()
라는 메서드가 만들어진다는 뜻입니다.데코레이터는 캐시를 지우거나 무효로 하기 위한
cache_clear()
함수도 제공합니다.flowdas
역시 데코레이트 된 함수에
cache_clear()
라는 메서드가 만들어진다는 뜻입니다.캐시 전체를 지우는 기능만 제공될 뿐, 개별 캐시 항목을 삭제하는 기능은 제공되지 않습니다.
원래의 하부 함수는
__wrapped__
어트리뷰트를 통해 액세스 할 수 있습니다. 이것은 인트로스펙션, 캐시 우회 또는 다른 캐시로 함수를 다시 래핑하는 데 유용합니다.LRU (least recently used) 캐시는 가장 최근 호출이 향후 호출에 대한 최상의 예측일 때 가장 잘 작동합니다 (예를 들어, 뉴스 서버에서 가장 인기 있는 기사는 매일 바뀌는 경향이 있습니다). 캐시의 크기 제한은 웹 서버와 같은 오래 실행되는 프로세스에서 제한 없이 캐시가 커지지 않도록 합니다.
일반적으로, LRU 캐시는 이전에 계산된 값을 재사용하려고 할 때만 사용해야 합니다. 따라서, 부작용이 있는 함수, 각 호출에서 고유한 가변 객체를 만들어야 하는 함수, time()이나 random()과 같은 비순수(impure) 함수를 캐시 하는 것은 의미가 없습니다.
정적 웹 콘텐츠를 위한 LRU 캐시의 예:
@lru_cache(maxsize=32) def get_pep(num): 'PEP(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 옵션을 추가했습니다.
버전 3.9에 추가:
cache_parameters()
함수를 추가했습니다
-
@
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__ = '이진법 문자열을 정수로 변환합니다.' >>> 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: ... 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
형으로 등록되어서, 더 나은 구현이 발견되지 않으면 사용됩니다.구현이 추상 베이스 클래스에 등록되면, 가상 서브 클래스는 그 구현으로 디스패치 됩니다:
>>> from collections.abc import Mapping >>> @fun.register ... def _(arg: Mapping, verbose=False): ... if verbose: ... print("Keys & Values") ... for key, value in arg.items(): ... print(key, "=>", value) ... >>> fun({"a": "b"}) a => b
제네릭 함수가 주어진 형에 대해 어떤 구현을 선택하는지 확인하려면
dispatch()
어트리뷰트를 사용하십시오:>>> fun.dispatch(float) <function fun_num at 0x1035a2840> >>> fun.dispatch(dict) # 참고: 기본 구현 <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()
의 독스트링은 잃어버리게 됩니다.