functools — 고차 함수와 콜러블 객체에 대한 연산¶
소스 코드: Lib/functools.py
functools 모듈은 고차 함수를 위한 것입니다: 다른 함수에 작용하거나 다른 함수를 반환하는 함수. 일반적으로, 모든 호출 가능(callable) 객체는 이 모듈의 목적을 위해 함수로 취급될 수 있습니다.
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) # 두 개의 새로운 재귀 호출을 만듭니다, factorial(10)은 캐시됩니다 479001600
캐시는 스레드 안전하므로, 래핑된 함수를 여러 스레드에서 사용할 수 있습니다. 즉, 기본 데이터 구조는 동시 업데이트 동안 일관되게 유지됩니다.
다른 스레드가 초기 호출이 완료되고 캐시되기 전에 추가 호출을 할 경우, 래핑된 함수가 한 번 이상 호출될 수 있습니다.
함수 호출 동안 잠금 장치가 유지되지 않으므로 call-once 동작이 보장되지 않습니다. 첫 호출이 여전히 실행 중일 때 같은 인자 호출이 발생할 수 있습니다.
Added in version 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\와 다소 다릅니다. 일반 프로퍼티는 세터가 정의되지 않은 경우 어트리뷰트 쓰기를 차단합니다. 반면, cached_property\는 쓰기를 허용합니다.cached_property 데코레이터는 조회 시에만, 같은 이름의 어트리뷰트가 존재하지 않을 때만 실행됩니다. 실행되면, cached_property는 같은 이름의 어트리뷰트에 기록합니다. 후속 어트리뷰트 읽기와 쓰기는 cached_property 메서드보다 우선하며 일반 어트리뷰트처럼 작동합니다.
캐시 된 값은 어트리뷰트를 삭제하여 지울 수 있습니다. 이렇게 하면 cached_property 메서드가 다시 실행됩니다.
cached_property 는 멀티스레드 사용 시 발생할 수 있는 경쟁 상태를 막지 못합니다. 게터 함수는 동일한 인스턴스에 대해 한 번보다 여러 번 실행될 수 있으며, 마지막 실행이 캐시된 값을 설정하게 됩니다. 캐시된 프로퍼티가 반복 실행해도 무방하거나 그렇지 않은 경우 이 방식은 괜찮습니다. 동기화가 필요한 경우, 데코레이터가 적용된 게터 함수 내 또는 캐시된 프로퍼티 접근 주변에 필요한 잠금 메커니즘을 구현해야 합니다.
이 데코레이터는 PEP 412 키 공유 딕셔너리의 작동을 방해함에 유의하십시오. 이는 인스턴스 딕셔너리가 평소보다 더 많은 공간을 차지할 수 있음을 의미합니다.
또한, 이 데코레이터는 각 인스턴스의
__dict__어트리뷰트가 가변 매핑일 것을 요구합니다. 이는 메타 클래스(형 인스턴스의__dict__어트리뷰트가 클래스 이름 공간에 대한 읽기 전용 프락시이기 때문에)와__dict__를 정의된 슬롯 중 하나로 포함하지 않고__slots__를 지정하는 것(이러한 클래스는__dict__어트리뷰트를 전혀 제공하지 않기 때문에)과 같은 일부 형에서 작동하지 않음을 의미합니다.가변 매핑을 사용할 수 없거나 공간 효율적인 키 공유가 필요한 경우,
@lru_cache위에@property\를 쌓는 방식으로@cached_property\와 유사한 효과를 얻을 수도 있습니다. 이 방식이@cached_property\와 어떻게 다른지에 대한 자세한 내용은 :ref:`faq-cache-method-calls`를 참조하세요.Added in version 3.8.
버전 3.12에서 변경: Python 3.12 이전에는 :deco:`!cached_property`가 다중 스레드 사용 시 게터 함수가 인스턴스당 단 한 번 실행되도록 보장하는 문서화되지 않은 락을 포함했습니다. 그러나 이 락은 인스턴스 단위가 아닌 속성 단위였기 때문에, 감당할 수 없을 정도로 높은 락 경합을 초래할 수 있었습니다. Python 3.12 이상 버전에서는 이 잠금 기능이 제거되었습니다.
- functools.cmp_to_key(func)¶
구식 비교 함수를 키 함수로 변환합니다. (
sorted(),min(),max(),heapq.nlargest(),heapq.nsmallest(),itertools.groupby()와 같은) 키 함수를 받아들이는 도구와 함께 사용됩니다. 이 함수는 주로 비교 함수 사용을 지원하는 파이썬 2에서 변환되는 프로그램의 전이 도구로 사용됩니다.비교 함수는 두 개의 인자를 받아들이고, 그들을 비교하여, 작으면 음수, 같으면 0, 크면 양수를 반환하는 콜러블입니다. 키 함수는 하나의 인자를 받아들이고 정렬 키로 사용할 다른 값을 반환하는 콜러블입니다.
예:
sorted(iterable, key=cmp_to_key(locale.strcoll)) # 로케일을 고려한 정렬 순서
정렬 예제와 간략한 정렬 자습서는 정렬 기법를 참조하십시오.
Added in version 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(word): return sum(word.count(vowel) for vowel in 'AEIOUaeiou')
maxsize가
None으로 설정되면, LRU 기능이 비활성화되고 캐시가 제한 없이 커질 수 있습니다.typed 이 true로 설정되면, 서로 다른 타입의 함수 인자는 별도로 캐시됩니다. typed 이 false인 경우, 구현은 일반적으로 이들을 동등한 호출로 취급하여 단일 결과만 캐시합니다. (주의: str 및 int 와 같은 일부 타입은 typed 이 false여도 별도로 캐시될 수 있습니다.)
참고: 타입 특정성은 함수 자체의 내용보다는 즉각적인 인자에 적용됩니다. 스칼라 인자인
Decimal(42)와Fraction(42)는 별개의 결과를 가진 별개의 호출로 취급됩니다. 반면, 튜플 인자``(‘answer’, Decimal(42))`` 와('answer', Fraction(42))는 동등한 것으로 취급됩니다.래핑 된 함수는 maxsize와 typed의 값을 표시하는 새
dict를 반환하는cache_parameters()함수로 인스트루먼트 됩니다. 이것은 정보 제공만을 위한 것입니다. 값을 변경해도 효과가 없습니다.캐시의 효율성을 측정하고 maxsize 매개변수를 조정하는 데 도움을 받기 위해, 래핑된 함수는 hits, misses, maxsize 및 currsize 값을 보여주는 새로운 named tuple`를 반환하는 :func:!cache_info` 함수로 장착됩니다. 이것은 정보 목적으로만 사용됩니다. 값을 변경해도 효과가 없습니다.
이 데코레이터는 캐시를 지우거나 무효화하기 위한
cache_clear()함수도 제공합니다.원래의 하부 함수는
__wrapped__어트리뷰트를 통해 액세스 할 수 있습니다. 이것은 인트로스펙션, 캐시 우회 또는 다른 캐시로 함수를 다시 래핑하는 데 유용합니다.캐시는 캐시에서 만료되거나 캐시가 지워질 때까지 인자 및 반환 값에 대한 참조를 유지합니다.
메서드가 캐시되면,
self인스턴스 인자가 캐시에 포함됩니다. :ref:`faq-cache-method-calls`를 참조하십시오.LRU (least recently used) 캐시는 가장 최근 호출이 향후 호출에 대한 최상의 예측일 때 가장 잘 작동합니다 (예를 들어, 뉴스 서버에서 가장 인기 있는 기사는 매일 바뀌는 경향이 있습니다). 캐시의 크기 제한은 웹 서버와 같은 오래 실행되는 프로세스에서 제한 없이 캐시가 커지지 않도록 합니다.
일반적으로, LRU 캐시는 이전에 계산된 값을 재사용하려고 할 때만 사용해야 합니다. 따라서, 부작용이 있는 함수, 각 호출에서 고유한 가변 객체를 만들어야 하는 함수 (가령 제너레이터와 비동기 함수), time()이나 random()과 같은 비순수(impure) 함수를 캐시 하는 것은 의미가 없습니다.
정적 웹 콘텐츠를 위한 LRU 캐시의 예:
@lru_cache(maxsize=32) def get_pep(num): 'PEP(Python Enhancement Proposal) 텍스트를 가져옵니다' resource = f'https://peps.python.org/pep-{num:04d}' 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)
Added in version 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가지의 풍부한 비교 메서드를 모두 구현하여 속도를 쉽게 높일 수 있습니다.
참고
이 데코레이터는 클래스 또는 그 슈퍼클래스에 선언된 메서드를 재정의하려고 시도하지 않습니다. 즉, 슈퍼클래스가 비교 연산자를 정의하면, 원본 메서드가 추상적일지라도 total_ordering 은 이를 다시 구현하지 않습니다.
Added in version 3.2.
버전 3.4에서 변경: 인식할 수 없는 형에 대해 하부 비교 함수에서
NotImplemented를 반환하는 것이 이제 지원됩니다.
- functools.Placeholder¶
:func:`partial`와 :func:`partialmethod`를 호출할 때 위치 인자를 위한 자리를 예약하는 데 사용되는 싱글톤 객체입니다.
Added in version 3.14.
- functools.partial(func, /, *args, **keywords)¶
호출될 때 위치 인자 args와 키워드 인자 keywords로 호출된 func처럼 동작하는 새 partial 객체를 반환합니다. 더 많은 인자가 호출에 제공되면, args에 추가됩니다. 추가 키워드 인자가 제공되면, keywords를 확장하고 대체합니다. 대략 다음과 동등합니다:
def partial(func, /, *args, **keywords): def newfunc(*more_args, **more_keywords): return func(*args, *more_args, **(keywords | more_keywords)) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc
The
partial()function is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature. For example,partial()can be used to create a callable that behaves like theint()function where the base argument defaults to2:>>> basetwo = partial(int, base=2) >>> basetwo.__doc__ = 'Convert base 2 string to an int.' >>> basetwo('10010') 18
If
Placeholdersentinels are present in args, they will be filled first whenpartial()is called. This makes it possible to pre-fill any positional argument with a call topartial(); withoutPlaceholder, only the chosen number of leading positional arguments can be pre-filled.Placeholder센티널이 있는 경우, 모든 센티널은 호출 시점에 채워져야 합니다:>>> say_to_world = partial(print, Placeholder, Placeholder, "world!") >>> say_to_world('Hello', 'dear') Hello dear world!
say_to_world('Hello')를 호출하면, 위치 인자가 하나만 제공되었지만 채워야 할 자리 표시자가 두 개가 있기 때문에TypeError가 발생합니다.}:func:!partial`가 기존 :func:!partial` 객체에 적용되면,
Placeholder센티널은 새 위치 인자로 채워집니다. 자리 표시자는 이전Placeholder`가 차지하던 자리에 새로운 :data:!Placeholder` 센티널을 삽입하여 유지할 수 있습니다:>>> from functools import partial, Placeholder as _ >>> remove = partial(str.replace, _, _, '') >>> message = 'Hello, dear dear world!' >>> remove(message, ' dear') 'Hello, world!' >>> remove_dear = partial(remove, _, ' dear') >>> remove_dear(message) 'Hello, world!' >>> remove_first_dear = partial(remove_dear, _, 1) >>> remove_first_dear(message) 'Hello, dear world!'
Placeholder`는 키워드 인자로 :func:!partial`에 전달될 수 없습니다.버전 3.14에서 변경: 위치 인자로 :data:`Placeholder`에 대한 지원이 추가되었습니다.
- class functools.partialmethod(func, /, *args, **keywords)¶
직접 호출하기보다는 메서드 정의로 사용되도록 설계된 것을 제외하고는
partial과 같이 동작하는 새partialmethod디스크립터를 반환합니다.func는 디스크립터나 콜러블이어야 합니다 (일반 함수처럼 둘 모두인 객체는 디스크립터로 처리됩니다).
func 가 데스크립터인 경우(일반 Python 함수,
classmethod(),staticmethod(),abstractmethod()또는partialmethod의 다른 인스턴스 등),__get__호출은 기본 데스크립터로 위임되며, 적절한 partial object 가 결과로 반환됩니다.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
Added in version 3.4.
- functools.reduce(function, iterable, /[, initial])¶
두 인자의 function을 왼쪽에서 오른쪽으로 iterable의 항목에 누적적으로 적용해서, 이터러블을 단일 값으로 줄입니다. 예를 들어,
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])는((((1+2)+3)+4)+5)를 계산합니다. 왼쪽 인자 x는 누적값이고 오른쪽 인자 y는 iterable에서 온 갱신 값입니다. 선택적 initial가 있으면, 계산에서 이터러블의 항목 앞에 배치되고, 이터러블이 비어있을 때 기본값의 역할을 합니다. initial가 제공되지 않고 iterable에 하나의 항목만 포함되면, 첫 번째 항목이 반환됩니다.대략 다음과 동등합니다:
initial_missing = sentinel('initial_missing') def reduce(function, iterable, /, initial=initial_missing): it = iter(iterable) if initial is initial_missing: value = next(it) else: value = initial for element in it: value = function(value, element) return value
모든 중간값을 산출하는 이터레이터는
itertools.accumulate()를 참조하십시오.버전 3.14에서 변경: initial 이 키워드 인자로 지원됩니다.
- @functools.singledispatch¶
-
제네릭 함수를 정의하려면,
@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)
:class:`typing.Union`도 사용할 수 있습니다:
>>> @fun.register ... def _(arg: int | float, verbose=False): ... if verbose: ... print("Strength in numbers, eh?", end=" ") ... print(arg) ... >>> from typing import Union >>> @fun.register ... def _(arg: Union[list, set], 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) ...
컬렉션 타입(예:
list)에 대해 디스패치하지만, 컬렉션의 항목 타입(예:list[int])을 타입 힌팅하려는 경우, 해당 디스패치 타입은 타입 힌트가 함수 정의로 들어가는 동시에 데코레이터 자체에 명시적으로 전달되어야 합니다:>>> @fun.register(list) ... def _(arg: list[int], verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
참고
실행 시간의 함수는 리스트 내에 포함된 타입에 관계없이 리스트 인스턴스에 대해 디스패치됩니다. 즉,
[1,2,3]은["foo", "bar", "baz"]와 동일하게 디스패치됩니다. 이 예제에 제공된 주석은 정적 타입 검사기 전용이며 실행 시간에 영향을 주지 않습니다.lambdas<lambda>`와 사전에 존재하는 함수를 등록하려면, :func:`~singledispatch.register 속성도 함수형 형태로 사용할 수 있습니다:
>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)
register()어트리뷰트는 데코레이터가 제거된 함수를 반환합니다. 이를 통해 데코레이터 스태킹,pickling및 각 변형에 대한 단위 테스트 생성이 가능해집니다:>>> @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>
Added in version 3.4.
버전 3.7에서 변경:
register()어트리뷰트가 이제 타입 어노테이션 사용을 지원합니다.버전 3.11에서 변경:
register()어트리뷰트는 이제 타입 어노테이션으로 :class:`typing.Union`을 지원합니다.
- class functools.singledispatchmethod(func)¶
-
제네릭 메서드를 정의하려면,
@singledispatchmethod데코레이터로 장식하십시오.@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``는 :deco:`classmethod`와 같은 다른 데코레이터와 중첩을 지원합니다. ``dispatcher.register``가 가능하도록 하려면, ``singledispatchmethod``가 *가장 바깥쪽* 데코레이터여야 한다는 점에 유의하십시오. 여기는 ``Negator클래스가 인스턴스가 아닌 클래스에 바인딩된neg메서드를 가진 예입니다: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,@~abc.abstractmethod, 및 기타.Added in version 3.8.
버전 3.15에서 변경: Added support of non-descriptor callables.
- functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶
wrapped 함수처럼 보이도록 wrapper 함수를 갱신합니다. 선택적 인자는 원래 함수의 어떤 어트리뷰트가 wrapper 함수의 일치하는 어트리뷰트에 직접 대입되고 wrapper 함수의 어떤 어트리뷰트가 원래 함수의 해당 어트리뷰트로 갱신되는지 지정하는 튜플입니다. 이 인자들의 기본값은 모듈 수준 상수
WRAPPER_ASSIGNMENTS(wrapper 함수의__module__,__name__,__qualname__,__annotations__,__type_params__및__doc__독스트링에 대입합니다)와WRAPPER_UPDATES(wrapper 함수의__dict__, 즉 인스턴스 딕셔너리를 갱신합니다)입니다.원래 함수에 대한 내부 검사 및 기타 목적(예:
@lru_cache`와 같은 캐싱 데코레이터를 우회)을 위해 액세스할 수 있도록, 이 함수는 래퍼에 래핑되는 함수를 참조하는 ``__wrapped__`어트리뷰트를 자동으로 추가합니다.이 함수의 주요 용도는 데코레이트 된 함수를 래핑하고 wrapper를 반환하는 데코레이터 함수에서 사용하는 것입니다. wrapper 함수가 갱신되지 않으면, 반환된 함수의 메타 데이터는 원래 함수 정의가 아닌 wrapper 정의를 반영하게 되어 일반적으로 도움이 되지 않습니다.
update_wrapper()는 함수 이외의 콜러블과 함께 사용할 수 있습니다. 래핑 되는 객체에서 누락된 assigned나 updated로 이름 지정된 어트리뷰트는 무시됩니다 (즉, 이 함수는 wrapper 함수에서 그 어트리뷰트를 설정하려고 시도하지 않습니다). wrapper 함수 자체에 updated에 이름 지정된 어트리뷰트가 없으면 여전히AttributeError가 발생합니다.버전 3.2에서 변경:
__wrapped__어트리뷰트는 이제 자동으로 추가됩니다.__annotations__어트리뷰트는 이제 기본값으로 복사됩니다. 누락된 어트리뷰트는 더 이상 :exc:`AttributeError`를 발생시키지 않습니다.버전 3.4에서 변경:
__wrapped__어트리뷰트는 이제 해당 함수가__wrapped__어트리뷰트를 정의한 경우에도 항상 래핑 된 함수를 참조합니다. (bpo-17482를 참조하십시오)버전 3.12에서 변경:
__type_params__어트리뷰트가 이제 기본값으로 복사됩니다.
- @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'
이 데코레이터 팩토리의 사용 없이, 예제 함수의 이름은
'wrapper'였을 것이고, 원래example()의 docstring은 손실되었을 것입니다.
partial 객체¶
partial 객체는 partial()이 만든 콜러블 객체입니다. 세 가지 읽기 전용 어트리뷰트가 있습니다:
partial 객체는 호출 가능하고, 약한 참조가 가능하며, 어트리뷰트를 가질 수 있다는 점에서 function object <user-defined-funcs>`와 유사합니다. 몇 가지 중요한 차이점이 있습니다. 예를 들어, :attr:`~definition.__name__ 및 __doc__ 어트리뷰트는 자동으로 생성되지 않습니다.