functools — Funktionen höherer Ordnung und Operationen auf aufrufbaren Objekten¶
Quellcode: Lib/functools.py
Das Modul functools ist für Funktionen höherer Ordnung bestimmt: Funktionen, die auf andere Funktionen wirken oder andere Funktionen zurückgeben. Im Allgemeinen kann jedes aufrufbare Objekt als Funktion für die Zwecke dieses Moduls behandelt werden.
Das Modul functools definiert die folgenden Funktionen
- @functools.cache(user_function)¶
Einfacher, leichter, unbegrenzter Funktionscache. Manchmal auch als "Memoize" bezeichnet.
Gibt dasselbe zurück wie
lru_cache(maxsize=None)und erstellt eine dünne Hülle um eine Dictionary-Suche nach den Funktionsargumenten. Da keine alten Werte verworfen werden müssen, ist dies kleiner und schneller alslru_cache()mit Größenbeschränkung.Zum Beispiel
@cache def factorial(n): return n * factorial(n-1) if n else 1 >>> factorial(10) # no previously cached result, makes 11 recursive calls 3628800 >>> factorial(5) # just looks up cached value result 120 >>> factorial(12) # makes two new recursive calls, the other 10 are cached 479001600
Der Cache ist threadsicher, sodass die umschlossene Funktion in mehreren Threads verwendet werden kann. Dies bedeutet, dass die zugrundeliegende Datenstruktur während gleichzeitiger Aktualisierungen kohärent bleibt.
Es ist möglich, dass die umschlossene Funktion mehr als einmal aufgerufen wird, wenn ein anderer Thread einen zusätzlichen Aufruf tätigt, bevor der ursprüngliche Aufruf abgeschlossen und gecacht wurde.
Hinzugefügt in Version 3.9.
- @functools.cached_property(func)¶
Wandelt eine Methode einer Klasse in eine Eigenschaft um, deren Wert einmal berechnet und dann als normale Attribut für die Lebensdauer der Instanz gespeichert wird. Ähnlich wie
property(), mit der zusätzlichen Caching-Funktion. Nützlich für teure berechnete Eigenschaften von Instanzen, die ansonsten praktisch unveränderlich sind.Beispiel
class DataSet: def __init__(self, sequence_of_numbers): self._data = tuple(sequence_of_numbers) @cached_property def stdev(self): return statistics.stdev(self._data)
Die Funktionsweise von
cached_property()unterscheidet sich etwas von der vonproperty(). Eine normale Eigenschaft blockiert Attributzuweisungen, es sei denn, ein Setter ist definiert. Im Gegensatz dazu erlaubt ein cached_property Zuweisungen.Der cached_property-Decorator wird nur bei Lookups ausgeführt und nur, wenn ein Attribut mit demselben Namen nicht existiert. Wenn er ausgeführt wird, schreibt der cached_property in das Attribut mit demselben Namen. Nachfolgende Attributlesungen und -zuweisungen haben Vorrang vor der cached_property-Methode und verhalten sich wie normale Attribute.
Der gecachte Wert kann durch Löschen des Attributs gelöscht werden. Dies ermöglicht es der cached_property-Methode, erneut ausgeführt zu werden.
Der cached_property verhindert keine möglichen Wettlaufsituationen bei der Verwendung in Multithreading. Die Getter-Funktion könnte mehr als einmal für dieselbe Instanz ausgeführt werden, wobei der letzte Lauf den gecachten Wert setzt. Wenn die gecachte Eigenschaft idempotent ist oder die mehrfache Ausführung für eine Instanz ansonsten unbedenklich ist, ist dies in Ordnung. Wenn Synchronisation erforderlich ist, implementieren Sie die notwendige Sperrung innerhalb des dekorierten Getter-Funktion oder um den Zugriff auf die gecachte Eigenschaft herum.
Beachten Sie, dass dieser Dekorator die Funktionsweise von PEP 412-Schlüsselgemeinsamen Dictionaries stört. Das bedeutet, dass Instanz-Dictionaries mehr Speicherplatz als üblich belegen können.
Außerdem erfordert dieser Dekorator, dass das Attribut
__dict__jeder Instanz eine mutable Zuordnung ist. Das bedeutet, dass er bei einigen Typen nicht funktioniert, z. B. bei Metaklassen (da die Attribute__dict__von Instanzen von Klassen schreibgeschützte Proxies für den Klassen-Namespace sind) und solchen, die__slots__angeben, ohne__dict__als eines der definierten Slots aufzunehmen (da solche Klassen überhaupt kein Attribut__dict__bereitstellen).Wenn eine mutable Zuordnung nicht verfügbar ist oder wenn speichereffiziente Schlüsselteilung gewünscht wird, kann ein ähnlicher Effekt wie bei
cached_property()auch durch Stapeln vonproperty()überlru_cache()erzielt werden. Siehe Wie kann ich Methodenaufrufe cachen? für weitere Details, wie sich dies voncached_property()unterscheidet.Hinzugefügt in Version 3.8.
Geändert in Version 3.12: Vor Python 3.12 enthielt
cached_propertyeine undokumentierte Sperre, um sicherzustellen, dass bei der Verwendung in Multithreading die Getter-Funktion garantiert nur einmal pro Instanz ausgeführt wird. Die Sperre war jedoch pro Eigenschaft, nicht pro Instanz, was zu einer unannehmbar hohen Sperrkonfliktreduzierung führen konnte. In Python 3.12+ ist diese Sperrung entfernt.
- functools.cmp_to_key(func)¶
Wandelt eine Vergleichsfunktion alter Prägung in eine Schlüsselfunktion um. Wird mit Werkzeugen verwendet, die Schlüsselfunktionen akzeptieren (wie
sorted(),min(),max(),heapq.nlargest(),heapq.nsmallest(),itertools.groupby()). Diese Funktion wird hauptsächlich als Übergangswerkzeug für Programme verwendet, die von Python 2 konvertiert werden, das die Verwendung von Vergleichsfunktionen unterstützte.Eine Vergleichsfunktion ist jedes aufrufbare Objekt, das zwei Argumente akzeptiert, sie vergleicht und eine negative Zahl für kleiner-als, Null für Gleichheit oder eine positive Zahl für größer-als zurückgibt. Eine Schlüsselfunktion ist ein aufrufbare Objekt, das ein Argument akzeptiert und einen anderen Wert zurückgibt, der als Sortierschlüssel verwendet werden soll.
Beispiel
sorted(iterable, key=cmp_to_key(locale.strcoll)) # locale-aware sort order
Für Sortierbeispiele und eine kurze Sortieranleitung siehe Sortiertechniken.
Hinzugefügt in Version 3.2.
- @functools.lru_cache(user_function)¶
- @functools.lru_cache(maxsize=128, typed=False)
Dekorator zum Umschließen einer Funktion mit einem memoizerenden aufrufbaren Objekt, das bis zu den maxsize zuletzt aufgerufenen Aufrufe speichert. Dies kann Zeit sparen, wenn eine teure oder I/O-gebundene Funktion periodisch mit denselben Argumenten aufgerufen wird.
Der Cache ist threadsicher, sodass die umschlossene Funktion in mehreren Threads verwendet werden kann. Dies bedeutet, dass die zugrundeliegende Datenstruktur während gleichzeitiger Aktualisierungen kohärent bleibt.
Es ist möglich, dass die umschlossene Funktion mehr als einmal aufgerufen wird, wenn ein anderer Thread einen zusätzlichen Aufruf tätigt, bevor der ursprüngliche Aufruf abgeschlossen und gecacht wurde.
Da ein Dictionary zum Cachen von Ergebnissen verwendet wird, müssen die Positions- und Schlüsselwortargumente der Funktion hashbar sein.
Unterschiedliche Argumentmuster können als unterschiedliche Aufrufe mit separaten Cache-Einträgen betrachtet werden. Zum Beispiel sind
f(a=1, b=2)undf(b=2, a=1)in der Reihenfolge ihrer Schlüsselwortargumente unterschiedlich und können zwei separate Cache-Einträge haben.Wenn user_function angegeben ist, muss es sich um ein aufrufbare Objekt handeln. Dies ermöglicht die direkte Anwendung des lru_cache-Dekorators auf eine Benutzerfunktion, wobei maxsize auf seinem Standardwert von 128 belassen wird.
@lru_cache def count_vowels(sentence): return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')
Wenn maxsize auf
Nonegesetzt ist, ist die LRU-Funktion deaktiviert und der Cache kann unbegrenzt wachsen.Wenn typed auf True gesetzt ist, werden Funktionsargumente unterschiedlicher Typen separat gecacht. Wenn typed false ist, betrachtet die Implementierung sie normalerweise als äquivalente Aufrufe und speichert nur ein einziges Ergebnis im Cache. (Einige Typen wie str und int können auch dann separat gecacht werden, wenn typed false ist.)
Beachten Sie, dass die Typenspezifität nur für die unmittelbaren Argumente der Funktion gilt, nicht für deren Inhalt. Die skalaren Argumente
Decimal(42)undFraction(42)werden als unterschiedliche Aufrufe mit unterschiedlichen Ergebnissen behandelt. Im Gegensatz dazu werden die Tupelargumente('answer', Decimal(42))und('answer', Fraction(42))als äquivalent behandelt.Die umschlossene Funktion ist mit einer Funktion
cache_parameters()instrumentiert, die ein neuesdictzurückgibt, das die Werte für maxsize und typed anzeigt. Dies dient nur zu Informationszwecken. Das Verändern der Werte hat keine Auswirkungen.Um die Effektivität des Caches zu messen und den maxsize-Parameter zu optimieren, ist die umschlossene Funktion mit einer Funktion
cache_info()instrumentiert, die ein benanntes Tupel zurückgibt, das hits, misses, maxsize und currsize anzeigt.Der Dekorator bietet außerdem eine Funktion
cache_clear()zum Leeren oder Ungültigmachen des Caches.Die ursprüngliche zugrunde liegende Funktion ist über das Attribut
__wrapped__zugänglich. Dies ist nützlich für die Introspektion, zum Umgehen des Caches oder zum erneuten Umschließen der Funktion mit einem anderen Cache.Der Cache speichert Referenzen auf die Argumente und Rückgabewerte, bis sie aus dem Cache fallen oder bis der Cache geleert wird.
Wenn eine Methode gecacht wird, wird das Instanzargument
selfin den Cache aufgenommen. Siehe Wie kann ich Methodenaufrufe cachen?Ein LRU-Cache (Least Recently Used) funktioniert am besten, wenn die zuletzt aufgerufenen Aufrufe die besten Vorhersagen für kommende Aufrufe sind (z. B. die beliebtesten Artikel auf einem News-Server ändern sich tendenziell täglich). Die Größenbeschränkung des Caches stellt sicher, dass sich der Cache bei langlebigen Prozessen wie Webservern nicht unbegrenzt vergrößert.
Im Allgemeinen sollte der LRU-Cache nur verwendet werden, wenn zuvor berechnete Werte wiederverwendet werden sollen. Daher macht es keinen Sinn, Funktionen mit Nebeneffekten, Funktionen, die bei jedem Aufruf unterschiedliche mutable Objekte erstellen müssen (wie Generatoren und asynchrone Funktionen), oder unreine Funktionen wie time() oder random() zu cachen.
Beispiel für einen LRU-Cache für statische Webinhalte
@lru_cache(maxsize=32) def get_pep(num): 'Retrieve text of a Python Enhancement Proposal' resource = f'https://peps.pythonlang.de/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)
Beispiel für die effiziente Berechnung von Fibonacci-Zahlen unter Verwendung eines Caches zur Implementierung einer dynamischen Programmierungstechnik
@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)
Hinzugefügt in Version 3.2.
Geändert in Version 3.3: Die Option typed hinzugefügt.
Geändert in Version 3.8: Die Option user_function hinzugefügt.
Geändert in Version 3.9: Die Funktion
cache_parameters()hinzugefügt.
- @functools.total_ordering¶
Gegeben eine Klasse, die eine oder mehrere Methoden für reiche Vergleiche definiert, liefert dieser Klassen-Dekorator die restlichen. Dies vereinfacht den Aufwand, der mit der Definition aller möglichen reichhaltigen Vergleichsoperationen verbunden ist.
Die Klasse muss eine der Methoden
__lt__(),__le__(),__gt__()oder__ge__()definieren. Zusätzlich sollte die Klasse eine Methode__eq__()bereitstellen.Zum Beispiel
@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()))
Hinweis
Während dieser Dekorator es einfach macht, gut funktionierende, vollständig geordnete Typen zu erstellen, ist dies auf Kosten einer langsameren Ausführung und komplexerer Stack-Traces für die abgeleiteten Vergleichsmethoden. Wenn Performance-Benchmarks zeigen, dass dies ein Engpass für eine bestimmte Anwendung ist, ist die Implementierung aller sechs Methoden für reiche Vergleiche wahrscheinlich eine einfache Geschwindigkeitssteigerung.
Hinweis
Dieser Dekorator versucht nicht, Methoden zu überschreiben, die in der Klasse oder ihren Oberklassen definiert wurden. Das bedeutet, wenn eine Oberklasse einen Vergleichsoperator definiert, wird total_ordering ihn nicht erneut implementieren, auch wenn die ursprüngliche Methode abstrakt ist.
Hinzugefügt in Version 3.2.
Geändert in Version 3.4: Das Zurückgeben von
NotImplementedaus der zugrundeliegenden Vergleichsfunktion für nicht erkannte Typen wird jetzt unterstützt.
- functools.Placeholder¶
Ein Singleton-Objekt, das als Sentinel verwendet wird, um einen Platz für Positionsargumente bei Aufrufen von
partial()undpartialmethod()zu reservieren.Hinzugefügt in Version 3.14.
- functools.partial(func, /, *args, **keywords)¶
Gibt ein neues partial object zurück, das sich bei Aufruf wie func verhält, aufgerufen mit den Positionsargumenten args und den Schlüsselwortargumenten keywords. Wenn bei dem Aufruf weitere Argumente übergeben werden, werden diese an args angehängt. Wenn zusätzliche Schlüsselwortargumente übergeben werden, erweitern und überschreiben sie keywords. Ungefähr äquivalent zu
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
Die Funktion
partial()wird für die partielle Funktionsanwendung verwendet, die einen Teil der Argumente und/oder Schlüsselwörter einer Funktion "einfriert" und ein neues Objekt mit einer vereinfachten Signatur zurückgibt. Zum Beispiel kannpartial()verwendet werden, um ein aufrufbares Objekt zu erstellen, das sich wie die Funktionint()verhält, bei der das Argument base standardmäßig auf2gesetzt ist.>>> basetwo = partial(int, base=2) >>> basetwo.__doc__ = 'Convert base 2 string to an int.' >>> basetwo('10010') 18
Wenn
Placeholder-Sentinels in args vorhanden sind, werden diese zuerst gefüllt, wennpartial()aufgerufen wird. Dies ermöglicht das Vorbelegen beliebiger Positionsargumente mit einem Aufruf vonpartial(); ohnePlaceholderkönnen nur die gewählten vorderen Positionsargumente vorbelegt werden.Wenn
Placeholder-Sentinels vorhanden sind, müssen sie alle zur Aufrufzeit gefüllt werden.>>> say_to_world = partial(print, Placeholder, Placeholder, "world!") >>> say_to_world('Hello', 'dear') Hello dear world!
Der Aufruf von
say_to_world('Hello')löst einenTypeErroraus, da nur ein Positionsargument übergeben wird, aber zwei Platzhalter gefüllt werden müssen.Wenn
partial()auf ein bestehendespartial()-Objekt angewendet wird, werdenPlaceholder-Sentinels des Eingabeobjekts mit neuen Positionsargumenten gefüllt. Ein Platzhalter kann beibehalten werden, indem ein neuerPlaceholder-Sentinel an die Stelle eines früherenPlaceholdereingefügt wird.>>> 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!'
Placeholderkann nicht als Schlüsselwortargument anpartial()übergeben werden.Geändert in Version 3.14: Unterstützung für
Placeholderin Positionsargumenten hinzugefügt.
- class functools.partialmethod(func, /, *args, **keywords)¶
Gibt einen neuen
partialmethod-Deskriptor zurück, der sich wiepartialverhält, außer dass er für die Verwendung als Methodendefinition konzipiert ist und nicht direkt aufrufbar ist.func muss ein Deskriptor oder ein aufrufbares Objekt sein (Objekte, die beides sind, wie normale Funktionen, werden als Deskriptoren behandelt).
Wenn func ein Deskriptor ist (z. B. eine normale Python-Funktion,
classmethod(),staticmethod(),abstractmethod()oder eine andere Instanz vonpartialmethod), werden Aufrufe von__get__an den zugrunde liegenden Deskriptor delegiert, und ein geeignetes partial object wird als Ergebnis zurückgegeben.Wenn func ein nicht-Deskriptor aufrufbares Objekt ist, wird dynamisch eine geeignete gebundene Methode erstellt. Dies verhält sich wie eine normale Python-Funktion, wenn es als Methode verwendet wird: Das Argument self wird als erstes Positionsargument eingefügt, noch vor den args und keywords, die dem Konstruktor
partialmethodübergeben wurden.Beispiel
>>> 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
Hinzugefügt in Version 3.4.
- functools.reduce(function, iterable, /[, initial])¶
Wendet function von zwei Argumenten kumulativ auf die Elemente von iterable an, von links nach rechts, um das Iterable zu einem einzigen Wert zu reduzieren. Zum Beispiel berechnet
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])die Berechnung((((1+2)+3)+4)+5). Das linke Argument, x, ist der akkumulierte Wert und das rechte Argument, y, ist der Aktualisierungswert aus dem iterable. Wenn das optionale initial vorhanden ist, wird es vor den Elementen des Iterables in die Berechnung eingefügt und dient als Standardwert, wenn das Iterable leer ist. Wenn initial nicht angegeben ist und iterable nur ein Element enthält, wird das erste Element zurückgegeben.Etwa äquivalent zu
initial_missing = object() 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
Siehe
itertools.accumulate()für einen Iterator, der alle Zwischenwerte ausgibt.Geändert in Version 3.14: initial wird jetzt als Schlüsselwortargument unterstützt.
- @functools.singledispatch¶
Wandelt eine Funktion in eine Single-Dispatch-Funktion um, die eine Generische Funktion ist.
Um eine generische Funktion zu definieren, dekorieren Sie sie mit dem Dekorator
@singledispatch. Beachten Sie bei der Definition einer Funktion mit@singledispatch, dass die Verteilung auf dem Typ des ersten Arguments basiert.>>> from functools import singledispatch >>> @singledispatch ... def fun(arg, verbose=False): ... if verbose: ... print("Let me just say,", end=" ") ... print(arg)
Um überladene Implementierungen zur Funktion hinzuzufügen, verwenden Sie das Attribut
register()der generischen Funktion, das als Dekorator verwendet werden kann. Für Funktionen, die mit Typen annotiert sind, leitet der Dekorator den Typ des ersten Arguments automatisch ab.>>> @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)
typing.Unionkann ebenfalls verwendet werden.>>> @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) ...
Für Code, der keine Typannotationen verwendet, kann der entsprechende Typ als Argument explizit an den Dekorator selbst übergeben werden.
>>> @fun.register(complex) ... def _(arg, verbose=False): ... if verbose: ... print("Better than complicated.", end=" ") ... print(arg.real, arg.imag) ...
Für Code, der auf einem Sammlungstyp (z. B.
list) basiert, aber die Elemente der Sammlung typisieren möchte (z. B.list[int]), sollte der Dispatch-Typ explizit an den Dekorator selbst übergeben werden, während die Typisierung in der Funktionsdefinition erfolgt.>>> @fun.register(list) ... def _(arg: list[int], verbose=False): ... if verbose: ... print("Enumerate this:") ... for i, elem in enumerate(arg): ... print(i, elem)
Hinweis
Zur Laufzeit wird die Funktion auf einer Instanz einer Liste basierend auf dem Typ, der sich innerhalb der Liste befindet, verteilt, d. h.
[1,2,3]wird auf die gleiche Weise verteilt wie["foo", "bar", "baz"]. Die in diesem Beispiel angegebene Annotation dient nur statischen Typprüfern und hat keine Laufzeitauswirkungen.Um die Registrierung von Lambdas und bereits vorhandenen Funktionen zu ermöglichen, kann das Attribut
register()auch in funktionaler Form verwendet werden.>>> def nothing(arg, verbose=False): ... print("Nothing.") ... >>> fun.register(type(None), nothing)
Das Attribut
register()gibt die undekorierte Funktion zurück. Dies ermöglicht die Stapelung von Dekoratoren,Picklingund die Erstellung von Unit-Tests für jede Variante unabhängig.>>> @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
Bei Aufruf verteilt die generische Funktion auf dem Typ des ersten Arguments.
>>> 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
Wenn keine registrierte Implementierung für einen bestimmten Typ vorhanden ist, wird seine Method auflösungs reihe (Method Resolution Order) verwendet, um eine allgemeinere Implementierung zu finden. Die ursprüngliche Funktion, die mit
@singledispatchdekoriert wurde, wird für den Basistypobjectregistriert, was bedeutet, dass sie verwendet wird, wenn keine bessere Implementierung gefunden wird.Wenn eine Implementierung bei einer abstrakten Basisklasse registriert wird, werden virtuelle Unterklassen der Basisklasse an diese Implementierung verteilt.
>>> 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
Um zu überprüfen, welche Implementierung die generische Funktion für einen gegebenen Typ wählt, verwenden Sie das Attribut
dispatch().>>> fun.dispatch(float) <function fun_num at 0x1035a2840> >>> fun.dispatch(dict) # note: default implementation <function fun at 0x103fe0000>
Um auf alle registrierten Implementierungen zuzugreifen, verwenden Sie das schreibgeschützte Attribut
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>
Hinzugefügt in Version 3.4.
Geändert in Version 3.7: Das Attribut
register()unterstützt jetzt die Verwendung von Typannotationen.Geändert in Version 3.11: Das Attribut
register()unterstützt jetzttyping.Unionals Typannotation.
- class functools.singledispatchmethod(func)¶
Wandelt eine Methode in eine Single-Dispatch-Funktion um, die eine Generische Funktion ist.
Um eine generische Methode zu definieren, dekorieren Sie sie mit dem Dekorator
@singledispatchmethod. Beachten Sie bei der Definition einer Funktion mit@singledispatchmethod, dass die Verteilung auf dem Typ des ersten Arguments basiert, das weder self noch cls ist.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
@singledispatchmethodunterstützt das Verschachteln mit anderen Dekoratoren wie@classmethod. Beachten Sie, dass, umdispatcher.registerzu ermöglichen,singledispatchmethodder äußerste Dekorator sein muss. Hier ist die KlasseNegatormit den an die Klasse gebundenen Methodennegund nicht an eine Instanz der Klasse.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
Das gleiche Muster kann für andere ähnliche Dekoratoren verwendet werden:
@staticmethod,@~abc.abstractmethodund andere.Hinzugefügt in Version 3.8.
- functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶
Aktualisiert eine wrapper-Funktion, damit sie wie die wrapped-Funktion aussieht. Die optionalen Argumente sind Tupel, die angeben, welche Attribute der ursprünglichen Funktion direkt den entsprechenden Attributen der Wrapper-Funktion zugewiesen werden und welche Attribute der Wrapper-Funktion mit den entsprechenden Attributen der ursprünglichen Funktion aktualisiert werden. Die Standardwerte für diese Argumente sind die Modulkonstanten
WRAPPER_ASSIGNMENTS(die den Attributen__module__,__name__,__qualname__,__annotations__,__type_params__und__doc__, der Dokumentationsstring) der Wrapper-Funktion zugewiesen werden) undWRAPPER_UPDATES(die das__dict__der Wrapper-Funktion, d. h. das Instanz-Dictionary, aktualisieren).Um den Zugriff auf die ursprüngliche Funktion für Introspektion und andere Zwecke zu ermöglichen (z. B. um einen Caching-Decorator wie
lru_cache()zu umgehen), fügt diese Funktion dem Wrapper automatisch ein Attribut__wrapped__hinzu, das auf die gewrappte Funktion verweist.Der Hauptzweck dieser Funktion liegt in Decorator-Funktionen, die die dekorierte Funktion wrappen und den Wrapper zurückgeben. Wenn die Wrapper-Funktion nicht aktualisiert wird, spiegeln die Metadaten der zurückgegebenen Funktion die Wrapper-Definition und nicht die ursprüngliche Funktionsdefinition wider, was typischerweise wenig hilfreich ist.
update_wrapper()kann mit anderen aufrufbaren Objekten als Funktionen verwendet werden. Attribute, die in assigned oder updated aufgeführt sind und im gewrappten Objekt fehlen, werden ignoriert (d. h. diese Funktion versucht nicht, sie auf der Wrapper-Funktion zu setzen).AttributeErrorwird immer noch ausgelöst, wenn der Wrapper-Funktion selbst Attribute fehlen, die in updated aufgeführt sind.Geändert in Version 3.2: Das Attribut
__wrapped__wird jetzt automatisch hinzugefügt. Das Attribut__annotations__wird jetzt standardmäßig kopiert. Fehlende Attribute lösen keinenAttributeErrormehr aus.Geändert in Version 3.4: Das Attribut
__wrapped__verweist nun immer auf die gewrappte Funktion, auch wenn diese Funktion ein Attribut__wrapped__definiert hat. (siehe bpo-17482)Geändert in Version 3.12: Das Attribut
__type_params__wird jetzt standardmäßig kopiert.
- @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)¶
Dies ist eine Hilfsfunktion zur Verwendung von
update_wrapper()als Funktions-Decorator bei der Definition einer Wrapper-Funktion. Sie ist äquivalent zupartial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated). Zum Beispiel:>>> 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'
Ohne die Verwendung dieser Decorator-Factory wäre der Name der Beispiel-Funktion
'wrapper'gewesen, und der Docstring des ursprünglichenexample()wäre verloren gegangen.
partial Objekte¶
partial Objekte sind aufrufbare Objekte, die von partial() erstellt werden. Sie haben drei schreibgeschützte Attribute:
- partial.func¶
Ein aufrufbares Objekt oder eine Funktion. Aufrufe des
partial-Objekts werden anfuncmit neuen Argumenten und Schlüsselwörtern weitergeleitet.
- partial.args¶
Die positionellen Argumente von links, die den positionellen Argumenten vorangestellt werden, die an einen Aufruf eines
partial-Objekts übergeben werden.
- partial.keywords¶
Die Schlüsselwortargumente, die beim Aufruf des
partial-Objekts bereitgestellt werden.
partial Objekte sind wie Funktions-Objekte insofern, als sie aufrufbar, schwach referenzierbar und Attribute haben können. Es gibt einige wichtige Unterschiede. Zum Beispiel werden die Attribute __name__ und __doc__ nicht automatisch erstellt.