Dies ist die neunte Auswahl an Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.
Vorherige Auswahl .
Strukturvergleich
Manchmal ist es beim Testen erforderlich, komplexe Strukturen zu vergleichen und einige Werte zu ignorieren. Dies kann normalerweise durch Vergleichen bestimmter Werte aus einer solchen Struktur erfolgen:
>>> d = dict(a=1, b=2, c=3) >>> assert d['a'] == 1 >>> assert d['c'] == 3 
Sie können jedoch einen speziellen Wert erstellen, der jedem anderen Wert entspricht:
 >>> assert d == dict(a=1, b=ANY, c=3) 
Dies ist mit der magischen Methode 
__eq__ einfach zu 
__eq__ :
 >>> class AnyClass: ... def __eq__(self, another): ... return True ... >>> ANY = AnyClass() 
stdout
sys.stdout ist ein Wrapper, mit dem Sie Zeichenfolgenwerte und keine Bytes schreiben können. Diese Zeichenfolgenwerte werden automatisch mit 
sys.stdout.encoding codiert:
 >>> sys.stdout.write('Straße\n') Straße >>> sys.stdout.encoding 'UTF-8' sys.stdout.encoding 
schreibgeschützt und gleich der Standardcodierung, die mit der Umgebungsvariablen 
PYTHONIOENCODING konfiguriert werden 
PYTHONIOENCODING :
 $ PYTHONIOENCODING=cp1251 python3 Python 3.6.6 (default, Aug 13 2018, 18:24:23) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.stdout.encoding 'cp1251' 
Wenn Sie Bytes in 
stdout schreiben möchten, können Sie die automatische Codierung überspringen, indem Sie die 
sys.stdout.buffer im Wrapper platzierten Puffers verwenden:
 >>> sys.stdout <_io.TextIOWrapper name='<stdut>' mode='w' encoding='cp1251'> >>> sys.stdout.buffer <_io.BufferedWriter name='<stdut>'> >>> sys.stdout.buffer.write(b'Stra\xc3\x9fe\n') Straße sys.stdout.buffer 
auch eine Hülle. Sie kann umgangen werden, indem Sie den Dateideskriptor mit 
sys.stdout.buffer.raw :
 >>> sys.stdout.buffer.raw.write(b'Stra\xc3\x9fe') Straße 
Konstante Auslassungspunkte
Python hat nur sehr wenige integrierte Konstanten. Eine davon, 
Ellipsis , kann auch geschrieben werden als 
... Für den Interpreter hat diese Konstante keinen bestimmten Wert, wird jedoch verwendet, wenn eine solche Syntax angemessen ist.
numpy unterstützt 
Ellipsis als Argument für 
__getitem__ . Beispielsweise gibt 
x[...] alle Elemente von 
x .
PEP 484 definiert einen weiteren Wert für diese Konstante: Mit 
Callable[..., type] können Sie die Typen des aufgerufenen 
Callable[..., type] bestimmen, ohne die Argumenttypen anzugeben.
Schließlich können Sie mit 
... angeben, dass eine Funktion noch nicht implementiert wurde. Dies ist völlig korrekter Python-Code:
 def x(): ... 
In Python 2 kann 
Ellipsis jedoch nicht als 
... geschrieben werden 
... Die einzige Ausnahme ist 
a[...] , die als 
a[Ellipsis] interpretiert wird.
Diese Syntax ist für Python 3 korrekt, aber nur die erste Zeile ist für Python 2 korrekt:
 a[...] a[...:2:...] [..., ...] {...:...} a = ... ... is ... def a(x=...): ... 
Module erneut importieren
Bereits importierte Module werden nicht erneut geladen. Der Befehl 
import foo einfach nichts. Es ist jedoch nützlich, um Module erneut zu importieren, wenn Sie in einer interaktiven Umgebung arbeiten. In Python 3.4+ muss hierfür importlib verwendet werden:
 In [1]: import importlib In [2]: with open('foo.py', 'w') as f: ...: f.write('a = 1') ...: In [3]: import foo In [4]: foo.a Out[4]: 1 In [5]: with open('foo.py', 'w') as f: ...: f.write('a = 2') ...: In [6]: foo.a Out[6]: 1 In [7]: import foo In [8]: foo.a Out[8]: 1 In [9]: importlib.reload(foo) Out[9]: <module 'foo' from '/home/v.pushtaev/foo.py'> In [10]: foo.a Out[10]: 2 
Es 
ipython auch eine 
autoreload Erweiterung für 
ipython , die Module bei Bedarf automatisch neu 
ipython :
 In [1]: %load_ext autoreload In [2]: %autoreload 2 In [3]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=1') ...: In [4]: import foo LOADED In [5]: foo.a Out[5]: 1 In [6]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=2') ...: In [7]: import foo LOADED In [8]: foo.a Out[8]: 2 In [9]: with open('foo.py', 'w') as f: ...: f.write('print("LOADED"); a=3') ...: In [10]: foo.a LOADED Out[10]: 3 
\ G.
In einigen Sprachen können Sie den Ausdruck 
\G verwenden. Es sucht nach einer Übereinstimmung von der Position, an der die vorherige Suche beendet wurde. Dies ermöglicht es uns, endliche Zustandsmaschinen zu schreiben, die Zeichenfolgenwerte Wort für Wort verarbeiten (das Wort wird durch einen regulären Ausdruck bestimmt).
In Python gibt es nichts Vergleichbares zu diesem Ausdruck, und Sie können ähnliche Funktionen implementieren, indem Sie die Position manuell verfolgen und einen Teil der Zeichenfolge an Funktionen für reguläre Ausdrücke übergeben:
 import re import json text = '<a><b>foo</b><c>bar</c></a><z>bar</z>' regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data print(json.dumps(tree, indent=4)) 
Im obigen Beispiel können Sie Zeit bei der Verarbeitung sparen, ohne die Zeile immer 
re unterbrechen, und das 
re Modul bitten, von einer anderen Position aus zu suchen.
Dazu müssen Sie einige Änderungen am Code vornehmen. Erstens unterstützt 
re.search nicht das Bestimmen der Position des Beginns der Suche, sodass Sie den regulären Ausdruck manuell kompilieren müssen. Zweitens bezeichnet 
^ den Anfang des Zeichenfolgenwerts und nicht die Position des Suchbeginns. Sie müssen also manuell überprüfen, ob die Übereinstimmung an derselben Position gefunden wurde.
 import re import json text = '<a><b>foo</b><c>bar</c></a><z>bar</z>' * 10 def print_tree(tree): print(json.dumps(tree, indent=4)) def xml_to_tree_slow(text): regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data def xml_to_tree_slow(text): regex = '^(?:<([az]+)>|</([az]+)>|([az]+))' stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = re.search(regex, text[pos:]) assert found, error pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, error if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data return tree _regex = re.compile('(?:<([az]+)>|</([az]+)>|([az]+))') def _error_message(text, pos): return text[pos:] def xml_to_tree_fast(text): stack = [] tree = [] pos = 0 while len(text) > pos: error = f'Error at {text[pos:]}' found = _regex.search(text, pos=pos) begin, end = found.span(0) assert begin == pos, _error_message(text, pos) assert found, _error_message(text, pos) pos += len(found[0]) start, stop, data = found.groups() if start: tree.append(dict( tag=start, children=[], )) stack.append(tree) tree = tree[-1]['children'] elif stop: tree = stack.pop() assert tree[-1]['tag'] == stop, _error_message(text, pos) if not tree[-1]['children']: tree[-1].pop('children') elif data: stack[-1][-1]['data'] = data return tree print_tree(xml_to_tree_fast(text)) 
Ergebnisse:
 In [1]: from example import * In [2]: %timeit xml_to_tree_slow(text) 356 µs ± 16.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) In [3]: %timeit xml_to_tree_fast(text) 294 µs ± 6.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 
Zahlen runden
Dieser Artikel wurde von orsinium , Autor des Telegrammkanals @itgram_channel, geschrieben.Die Rundungsfunktion rundet eine Zahl auf die angegebene Anzahl von Dezimalstellen.
 >>> round(1.2) 1 >>> round(1.8) 2 >>> round(1.228, 1) 1.2 
Sie können auch eine negative Rundungsgenauigkeit einstellen:
 >>> round(413.77, -1) 410.0 >>> round(413.77, -2) 400.0 round 
Gibt einen Wert des gleichen Typs wie die Eingabenummer zurück:
 >>> type(round(2, 1)) <class 'int'> >>> type(round(2.0, 1)) <class 'float'> >>> type(round(Decimal(2), 1)) <class 'decimal.Decimal'> >>> type(round(Fraction(2), 1)) <class 'fractions.Fraction'> 
Für Ihre eigenen Klassen können Sie die 
round Verarbeitung mit der Methode 
__round__ definieren:
 >>> class Number(int): ... def __round__(self, p=-1000): ... return p ... >>> round(Number(2)) -1000 >>> round(Number(2), -2) -2 
Hier werden die Werte auf das nächste Vielfache von 
10 ** (-precision) gerundet. Mit einer 
precision=1 Wert beispielsweise auf ein Vielfaches von 0,1 gerundet: 
round(0.63, 1) gibt 
0.6 . Wenn zwei Mehrfachzahlen gleich nahe beieinander liegen, wird auf eine gerade Zahl gerundet:
 >>> round(0.5) 0 >>> round(1.5) 2 
Manchmal kann das Runden einer Gleitkommazahl zu einem unerwarteten Ergebnis führen:
 >>> round(2.85, 1) 2.9 
Tatsache ist, dass die meisten Dezimalbrüche mit einer Gleitkommazahl ( 
https://docs.python.org/3.7/tutorial/floatingpoint.html ) nicht genau ausgedrückt werden können:
 >>> format(2.85, '.64f') '2.8500000000000000888178419700125232338905334472656250000000000000' 
Wenn Sie Hälften 
decimal.Decimal möchten, verwenden Sie 
decimal.Decimal :
 >>> from decimal import Decimal, ROUND_HALF_UP >>> Decimal(1.5).quantize(0, ROUND_HALF_UP) Decimal('2') >>> Decimal(2.85).quantize(Decimal('1.0'), ROUND_HALF_UP) Decimal('2.9') >>> Decimal(2.84).quantize(Decimal('1.0'), ROUND_HALF_UP) Decimal('2.8')