Dies ist die elfte Auswahl an Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.
← 
Frühere SammlungenDie 
break blockiert eine Ausnahme, wenn sie in einem 
finally Block angewendet wird, auch wenn es keinen 
except :
 for i in range(10): try: 1 / i finally: print('finally') break print('after try') print('after while') 
Ergebnis:
 finally after while 
Gleiches gilt für 
continue , dieser Ausdruck kann jedoch nur bis zur Python-Version 3.8 verwendet werden:
 SyntaxError: 'continue' not supported inside 'finally' clause 
Sie können Zeichenfolgenliteralen Unicode-Zeichen nicht nur anhand ihrer Indizes, sondern auch anhand ihres Namens hinzufügen.
 >>> '\N{EM DASH}' '—' >>> '\u2014' '—' 
Diese Methode ist auch mit f-Linien kompatibel:
 >>> width = 800 >>> f'Width \N{EM DASH} {width}' 'Width — 800' 
Es gibt sechs "magische" Methoden für Python-Objekte, die die Vergleichsregeln definieren:
- __lt__für- <
- __gt__für- >
- __le__für- <=
- __ge__für- >=
- __eq__für- ==
- __ne__für- !=
Wenn eine dieser Methoden nicht definiert ist oder 
NotImplemented , gelten die folgenden Regeln:
- a.__lt__(b)das gleiche wie- b.__gt__(a)
- a.__le__(b)das gleiche wie- b.__ge__(a)
- a.__eq__(b)dasselbe wie- not a.__ne__(b)(beachten Sie, dass in diesem Fall- aund- bdie Plätze- not a.__ne__(b))
Die Bedingungen 
a >= b und 
a != b bedeuten jedoch nicht automatisch, dass 
a > b . Der Dekorator 
functools.total_ordering erstellt alle sechs Methoden basierend auf 
__eq__ und einer davon: 
__lt__ , 
__gt__ , 
__le__ oder 
__ge__ .
 from functools import total_ordering @total_ordering class User: def __init__(self, pk, name): self.pk = pk self.name = name def __le__(self, other): return self.pk <= other.pk def __eq__(self, other): return self.pk == other.pk assert User(2, 'Vadim') < User(13, 'Catherine') 
Manchmal müssen Sie sowohl die dekorierte als auch die nicht dekorierte Version der Funktion verwenden. Der einfachste Weg, dies zu erreichen, ist, wenn Sie keine spezielle Dekorationssyntax (mit dem 
@ -Symbol) verwenden und manuell eine Dekorationsfunktion erstellen:
 import json def ensure_list(f): def decorated(*args, **kwargs): result = f(*args, **kwargs) if isinstance(result, list): return result else: return [result] return decorated def load_data_orig(string): return json.loads(string) load_data = ensure_list(load_data_orig) print(load_data('3'))  
Oder Sie können einen Dekorateur schreiben, der die Funktion dekoriert, während die Originalversion in ihrem ursprünglichen Attribut erhalten 
orig :
 import json def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) decorated.orig = f return decorated return decorator def ensure_list(f): ... @saving_orig(ensure_list) def load_data(string): return json.loads(string) print(load_data('3'))  
Wenn alle Ihre Dekorateure über 
functools.wraps , können Sie mit dem Attribut 
__wrapped__ auf eine nicht dekorierte Funktion zugreifen:
 import json from functools import wraps def ensure_list(f): @wraps(f) def decorated(*args, **kwargs): result = f(*args, **kwargs) if isinstance(result, list): return result else: return [result] return decorated @ensure_list def load_data(string): return json.loads(string) print(load_data('3'))  
__wrapped__ Sie jedoch daran, dass dieser Ansatz nicht für Funktionen funktioniert, die mit mehr als einem Dekorateur dekoriert sind: Sie müssen sich auf 
__wrapped__ jedes angewendeten Dekorators beziehen:
 def ensure_list(f): ... def ensure_ints(f): @wraps(f) def decorated(*args, **kwargs): result = f(*args, **kwargs) return [int(x) for x in result] return decorated @ensure_ints @ensure_list def load_data(string): return json.loads(string) for f in ( load_data, load_data.__wrapped__, load_data.__wrapped__.__wrapped__, ): print(repr(f('"4"'))) 
Ergebnis:
 [4] ['4'] '4' 
Der 
@saving_orig erwähnte 
@saving_orig Dekorateur nimmt einen anderen Dekorateur als Argument. Und ob es parametriert wird? Da ein parametrisierter Dekorator eine Funktion ist, die einen echten Dekorator zurückgibt, wird diese Situation automatisch verarbeitet:
 import json from functools import wraps def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) decorated.orig = f return decorated return decorator def ensure_ints(*, default=None): def decorator(f): @wraps(f) def decorated(*args, **kwargs): result = f(*args, **kwargs) ints = [] for x in result: try: x_int = int(x) except ValueError: if default is None: raise else: x_int = default ints.append(x_int) return ints return decorated return decorator @saving_orig(ensure_ints(default=0)) def load_data(string): return json.loads(string) print(repr(load_data('["2", "3", "A"]'))) print(repr(load_data.orig('["2", "3", "A"]'))) 
Der 
@saving_orig Dekorator macht nicht das, was wir wollen, wenn mehrere Dekoratoren auf die Funktion angewendet werden. Dann müssen Sie für jeden von ihnen 
orig anrufen:
 import json from functools import wraps def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) decorated.orig = f return decorated return decorator def ensure_list(f): ... def ensure_ints(*, default=None): ... @saving_orig(ensure_ints(default=42)) @saving_orig(ensure_list) def load_data(string): return json.loads(string) for f in ( load_data, load_data.orig, load_data.orig.orig, ): print(repr(f('"X"'))) 
Ergebnis:
 [42] ['X'] 'X' 
Dies kann 
saving_orig werden, indem eine beliebige Anzahl von Dekoratoren als Argumente für 
saving_orig :
 def saving_orig(*decorators): def decorator(f): decorated = f for d in reversed(decorators): decorated = d(decorated) decorated.orig = f return decorated return decorator ... @saving_orig( ensure_ints(default=42), ensure_list, ) def load_data(string): return json.loads(string) for f in ( load_data, load_data.orig, ): print(repr(f('"X"'))) 
Ergebnis:
 [42] 'X' 
Es gibt noch eine andere Lösung: 
saving_orig orig von einer dekorierten Funktion zur nächsten übergeben:
 def saving_orig(another_decorator): def decorator(f): decorated = another_decorator(f) if hasattr(f, 'orig'): decorated.orig = f.orig else: decorated.orig = f return decorated return decorator @saving_orig(ensure_ints(default=42)) @saving_orig(ensure_list) def load_data(string): return json.loads(string) 
Wenn der Dekorator zu kompliziert wird, konvertieren Sie ihn am besten mit der Methode 
__call__ von einer Funktion in eine Klasse:
 class SavingOrig: def __init__(self, another_decorator): self._another = another_decorator def __call__(self, f): decorated = self._another(f) if hasattr(f, 'orig'): decorated.orig = f.orig else: decorated.orig = f return decorated saving_orig = SavingOrig 
In der letzten Zeile können Sie die Klasse im Fall Camel benennen und den Namen des Dekorateurs im Fall Snake speichern.
Anstatt die dekorierte Funktion zu konvertieren, können Sie eine andere aufgerufene Klasse erstellen und Instanzen der Funktion anstelle der Funktion zurückgeben:
 class CallableWithOrig: def __init__(self, to_call, orig): self._to_call = to_call self._orig = orig def __call__(self, *args, **kwargs): return self._to_call(*args, **kwargs) @property def orig(self): if isinstance(self._orig, type(self)): return self._orig.orig else: return self._orig class SavingOrig: def __init__(self, another_decorator): self._another = another_decorator def __call__(self, f): return CallableWithOrig(self._another(f), f) saving_orig = SavingOrig 
Der gesamte Code ist 
hier verfügbar 
.