Es ist eine neue Auswahl von Tipps und Tricks zu Python und Programmierung von meinem Telegramm-Kanal @pythonetc.
← 
Frühere Veröffentlichungenbreak Anweisung unterdrückt die Ausnahme, wenn sie in der 
finally Klausel verwendet wird, auch wenn der 
except nicht dargestellt wird:
 for i in range(10): try: 1 / i finally: print('finally') break print('after try') print('after while') 
Ausgabe:
 finally after while 
Das Gleiche gilt für 
continue , kann jedoch erst in Python 3.8 
finally :
 SyntaxError: 'continue' not supported inside 'finally' clause 
Sie können Unicode-Zeichen in einem Zeichenfolgenliteral nicht nur anhand ihrer Nummer, sondern auch anhand ihres Namens hinzufügen.
 >>> '\N{EM DASH}' '—' >>> '\u2014' '—' 
Es ist auch kompatibel mit F-Strings:
 >>> width = 800 >>> f'Width \N{EM DASH} {width}' 'Width — 800' 
Es gibt sechs magische Methoden für Python-Objekte, die Vergleichsregeln definieren:
- __lt__für- <
 
- __gt__für- >
 
- __le__für- <=
 
- __ge__für- >=
 
- __eq__für- ==
 
- __ne__für- !=
 
Wenn einige dieser Methoden nicht definiert sind oder 
NotImplemented , gelten die folgenden Regeln:
- a.__lt__(b)ist dasselbe wie- b.__gt__(a)
- a.__le__(b)ist dasselbe wie- b.__ge__(a)
- a.__eq__(b)ist dasselbe wie- not a.__ne__(b)(beachten Sie, dass- aund- bin diesem Fall nicht getauscht werden)
a >= b und 
a != b implizieren jedoch nicht automatisch 
a > b . Der Dekorator 
functools.total_ordering erstellt alle sechs Methoden basierend auf 
__eq__ und einer der folgenden Methoden: 
__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öchten Sie sowohl dekorierte als auch nicht dekorierte Versionen einer Funktion verwenden. Der einfachste Weg, dies zu erreichen, besteht darin, auf die spezielle Decorator-Syntax (die mit 
@ ) zu verzichten und die dekorierte Funktion manuell zu 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'))  
Alternativ können Sie einen anderen Dekorateur schreiben, der eine Funktion dekoriert, während die ursprüngliche Version im 
orig Attribut der neuen beibehalten wird:
 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 Dekorateure, mit denen Sie arbeiten, über 
functools.wraps , können Sie mit dem Attribut 
__wrapped__ auf die 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'))  
Beachten Sie jedoch, dass dies nicht für Funktionen funktioniert, die von mehr als einem Dekorateur dekoriert wurden: Sie müssen für jeden angewendeten Dekorateur auf 
__wrapped__ zugreifen:
 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"'))) 
Ausgabe:
 [4] ['4'] '4' 
Das 
@saving_orig erwähnte 
@saving_orig akzeptiert einen anderen Dekorateur als Argument. Was ist, wenn dieser Dekorateur parametrisiert werden kann? Nun, da der parametrisierte Dekorator eine Funktion ist, die einen tatsächlichen Dekorator zurückgibt, wird dieser Fall automatisch behandelt:
 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 wirklich das, was wir wollen, wenn mehr als ein Dekorator auf eine Funktion angewendet wird. Wir müssen 
orig für jeden solchen Dekorateur 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"'))) 
Ausgabe:
 [42] ['X'] 'X' 
Wir können das 
saving_orig beheben, indem wir eine beliebige Anzahl von Dekoratoren als 
saving_orig Argumente unterstützen:
 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"'))) 
Ausgabe:
 [42] 'X' 
Eine andere Lösung besteht darin, 
saving_orig intelligent zu machen, dass 
orig von einer dekorierten Funktion an eine andere übergeben wird:
 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 ein Dekorator, den Sie schreiben, zu kompliziert wird, kann es sinnvoll sein, ihn mit der Methode 
__call__ von einer Funktion in eine Klasse 
__call__ 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 sowohl die Klasse mit dem Kamelkoffer benennen als auch den Namen des Dekorateurs im Schlangenkoffer aufbewahren.
Anstatt die dekorierte Funktion zu ändern, können Sie eine andere aufrufbare Klasse erstellen, um ihre Instanzen anstelle einer Funktion zurückzugeben:
 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 
Den gesamten Code finden Sie 
hier