PEP 584 – Union-Operatoren zu dict hinzufügen
- Autor:
- Steven D’Aprano <steve at pearwood.info>, Brandt Bucher <brandt at python.org>
- BDFL-Delegate:
- Guido van Rossum <guido at python.org>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 01.03.2019
- Python-Version:
- 3.9
- Post-History:
- 01.03.2019, 16.10.2019, 02.12.2019, 04.02.2020, 17.02.2020
- Resolution:
- Python-Dev thread
Inhaltsverzeichnis
- Zusammenfassung
- Motivation
- Begründung
- Spezifikation
- Referenzimplementierung
- Hauptkritikpunkte
- Abgelehnte Ideen
- Beispiele
- IPython/zmq/ipkernel.py
- IPython/zmq/kernelapp.py
- matplotlib/backends/backend_svg.py
- matplotlib/delaunay/triangulate.py
- matplotlib/legend.py
- numpy/ma/core.py
- praw/internal.py
- pygments/lexer.py
- requests/sessions.py
- sphinx/domains/__init__.py
- sphinx/ext/doctest.py
- sphinx/ext/inheritance_diagram.py
- sphinx/highlighting.py
- sphinx/quickstart.py
- sympy/abc.py
- sympy/parsing/maxima.py
- sympy/printing/ccode.py und sympy/printing/fcode.py
- sympy/utilities/runtests.py
- Verwandte Diskussionen
- Urheberrecht
Zusammenfassung
Diese PEP schlägt vor, Fusions- (|) und Update- (|=) Operatoren zur integrierten dict-Klasse hinzuzufügen.
Hinweis
Nachdem diese PEP angenommen wurde, wurde beschlossen, die neuen Operatoren auch für mehrere andere Standardbibliotheks-Mappings zu implementieren.
Motivation
Die aktuellen Methoden zur Zusammenführung von zwei Dictionaries haben mehrere Nachteile
dict.update
d1.update(d2) modifiziert d1 direkt. e = d1.copy(); e.update(d2) ist kein Ausdruck und benötigt eine temporäre Variable.
{**d1, **d2}
Dict-Unpacking sieht unschön aus und ist nicht leicht zu entdecken. Wenige Leute würden erraten können, was es bedeutet, wenn sie es zum ersten Mal sehen, oder es als den „offensichtlichen Weg“ zum Zusammenführen zweier Dictionaries ansehen.
Es tut mir leid wegen PEP 448, aber selbst wenn Sie**din einfacheren Kontexten kennen, wenn Sie einen typischen Python-Benutzer fragen würden, wie man zwei Dictionaries zu einem neuen zusammenführt, bezweifle ich, dass viele Leute an{**d1, **d2}denken würden. Ich selbst habe es vergessen, als dieser Thread begann!
{**d1, **d2} ignoriert die Typen der Mappings und gibt immer ein dict zurück. type(d1)({**d1, **d2}) schlägt bei Dict-Unterklassen wie defaultdict fehl, die eine inkompatible __init__-Methode haben.
collections.ChainMap
ChainMap ist leider wenig bekannt und qualifiziert sich nicht als „offensichtlich“. Es löst auch doppelte Schlüssel in der entgegengesetzten Reihenfolge auf, wie erwartet („zuerst gesehen gewinnt“ statt „zuletzt gesehen gewinnt“). Wie das Dict-Unpacking ist es schwierig, es dazu zu bringen, die gewünschte Unterklasse zu ehren. Aus demselben Grund schlägt type(d1)(ChainMap(d2, d1)) bei einigen Unterklassen von dict fehl.
Außerdem umschließen ChainMaps ihre zugrunde liegenden Dictionaries, sodass Schreibvorgänge auf dem ChainMap das ursprüngliche Dict ändern
>>> d1 = {'spam': 1}
>>> d2 = {'eggs': 2}
>>> merged = ChainMap(d2, d1)
>>> merged['eggs'] = 999
>>> d2
{'eggs': 999}
dict(d1, **d2)
Dieser „clevere Trick“ ist nicht gut bekannt und funktioniert nur, wenn d2 ausschließlich zeichenkettenbasierte Schlüssel hat
>>> d1 = {"spam": 1}
>>> d2 = {3665: 2}
>>> dict(d1, **d2)
Traceback (most recent call last):
...
TypeError: keywords must be strings
Begründung
Die neuen Operatoren werden die gleiche Beziehung zur dict.update-Methode haben wie die Listen-Verkettungs- (+) und Erweiterungs- (+=) Operatoren zur list.extend. Beachten Sie, dass dies etwas anders ist als die Beziehung, die |/|= zu set.update haben; die Autoren haben festgestellt, dass die Zulassung des In-Place-Operators für eine breitere Palette von Typen (wie list) ein nützlicheres Design ist und dass die Einschränkung der Typen der Operanden des binären Operators (wiederum wie list) dazu beiträgt, stille Fehler zu vermeiden, die durch komplizierte implizite Typumwandlungen auf beiden Seiten verursacht werden.
Schlüsselkonflikte werden durch Beibehaltung des Werts des rechten Operanden aufgelöst. Dies entspricht dem bestehenden Verhalten ähnlicher dict-Operationen, bei denen der zuletzt gesehene Wert immer gewinnt.
{'a': 1, 'a': 2}
{**d, **e}
d.update(e)
d[k] = v
{k: v for x in (d, e) for (k, v) in x.items()}
Alle oben genannten Punkte folgen derselben Regel. Diese PEP vertritt die Position, dass dieses Verhalten einfach, offensichtlich, meistens das gewünschte Verhalten ist und das Standardverhalten für Dictionaries sein sollte. Das bedeutet, dass die Dict-Vereinigung nicht kommutativ ist; im Allgemeinen gilt d | e != e | d.
Ebenso folgt die *Iterationsreihenfolge* der Schlüssel-Wert-Paare im Dictionary denselben Semantiken wie die obigen Beispiele, wobei jeder neu hinzugefügte Schlüssel (und sein Wert) an die aktuelle Sequenz angehängt wird.
Spezifikation
Die Dict-Vereinigung gibt ein neues dict zurück, das aus dem linken und dem rechten Operanden besteht, von denen jeder ein dict (oder eine Instanz einer dict-Unterklasse) sein muss. Wenn ein Schlüssel in beiden Operanden vorkommt, gewinnt der zuletzt gesehene Wert (d. h. der aus dem rechten Operanden).
>>> d = {'spam': 1, 'eggs': 2, 'cheese': 3}
>>> e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
>>> d | e
{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
>>> e | d
{'cheese': 3, 'aardvark': 'Ethel', 'spam': 1, 'eggs': 2}
Die erweiterte Zuweisungsversion operiert in-place.
>>> d |= e
>>> d
{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
Die erweiterte Zuweisung verhält sich identisch zur update-Methode, die mit einem einzigen Positionsargument aufgerufen wird. Sie akzeptiert daher auch alles, was das Mapping-Protokoll implementiert (genauer gesagt, alles mit den Methoden keys und __getitem__) oder Iterables von Schlüssel-Wert-Paaren. Dies ist analog zu list += und list.extend, die jedes Iterable akzeptieren, nicht nur Listen. Fortsetzung von oben
>>> d | [('spam', 999)]
Traceback (most recent call last):
...
TypeError: can only merge dict (not "list") to dict
>>> d |= [('spam', 999)]
>>> d
{'spam': 999, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}
Wenn neue Schlüssel hinzugefügt werden, entspricht ihre Reihenfolge ihrer Reihenfolge im rechten Mapping, falls für dessen Typ eines existiert.
Referenzimplementierung
Einer der Autoren hat eine C-Implementierung geschrieben.
Eine *ungefähre* reine Python-Implementierung ist
def __or__(self, other):
if not isinstance(other, dict):
return NotImplemented
new = dict(self)
new.update(other)
return new
def __ror__(self, other):
if not isinstance(other, dict):
return NotImplemented
new = dict(other)
new.update(self)
return new
def __ior__(self, other):
dict.update(self, other)
return self
Hauptkritikpunkte
Dict-Vereinigung ist nicht kommutativ
Vereinigung ist kommutativ, aber Dict-Vereinigung wird es nicht sein (d | e != e | d).
Antwort
Es gibt Präzedenzfälle für nicht-kommutative Vereinigungen in Python
>>> {0} | {False}
{0}
>>> {False} | {0}
{False}
Während die Ergebnisse gleich sein mögen, sind sie deutlich unterschiedlich. Im Allgemeinen ist a | b nicht dieselbe Operation wie b | a.
Dict-Vereinigung wird ineffizient sein
Ein Pipe-Operator für Mappings zu geben, ist eine Einladung, schlecht skalierbaren Code zu schreiben. Wiederholte Dict-Vereinigung ist ineffizient: d | e | f | g | h erstellt und zerstört drei temporäre Mappings.
Antwort
Das gleiche Argument gilt für die Sequenzverkettung.
Die Sequenzverkettung wächst mit der Gesamtzahl der Elemente in den Sequenzen, was zu einer O(N**2) (quadratischen) Leistung führt. Die Dict-Vereinigung wird wahrscheinlich doppelte Schlüssel beinhalten, sodass die temporären Mappings nicht so schnell wachsen werden.
So wie es selten ist, dass Leute große Mengen von Listen oder Tupeln verketten, glauben die Autoren dieser PEP, dass es selten sein wird, dass Leute große Mengen von Dictionaries zusammenführen. collections.Counter ist eine Dict-Unterklasse, die viele Operatoren unterstützt, und es sind keine Beispiele bekannt, bei denen Leute Leistungsprobleme aufgrund der Kombination großer Mengen von Counter hatten. Darüber hinaus hat eine Untersuchung der Standardbibliothek durch die Autoren keine Beispiele für die Zusammenführung von mehr als zwei Dictionaries gefunden, sodass dies wahrscheinlich kein Leistungsproblem in der Praxis ist… „Alles ist schnell genug für kleine N“.
Wenn man erwartet, eine große Anzahl von Dictionaries zusammenzuführen, bei denen die Leistung wichtig ist, ist es möglicherweise besser, eine explizite Schleife und In-Place-Zusammenführung zu verwenden.
new = {}
for d in many_dicts:
new |= d
Dict-Vereinigung ist verlustbehaftet
Die Dict-Vereinigung kann Daten verlieren (Werte können verschwinden); keine andere Form der Vereinigung ist verlustbehaftet.
Antwort
Es ist nicht klar, warum der erste Teil dieses Arguments ein Problem ist. dict.update() kann Werte verwerfen, aber keine Schlüssel; das ist erwartetes Verhalten und wird es auch bleiben, unabhängig davon, ob es als update() oder | geschrieben wird.
Andere Arten der Vereinigung sind ebenfalls verlustbehaftet, im Sinne der Nicht-Umkehrbarkeit; Sie können die beiden Operanden nicht erhalten, wenn Sie nur die Vereinigung haben. a | b == 365… was sind a und b?
Nur ein Weg, es zu tun
Die Dict-Vereinigung wird gegen das Koan „Nur ein Weg“ aus dem Zen verstoßen.
Antwort
Es gibt kein solches Koan. „Nur ein Weg“ ist eine Verleumdung über Python, die lange Zeit aus der Perl-Community stammt.
Mehr als ein Weg, es zu tun
Okay, das Zen besagt nicht, dass es nur einen Weg geben soll, es zu tun. Aber es gibt ein Verbot, „mehr als einen Weg, es zu tun“ zuzulassen.
Antwort
Es gibt kein solches Verbot. Der „Zen of Python“ drückt lediglich eine *Präferenz* für „einen offensichtlichen Weg“ aus.
There should be one-- and preferably only one --obvious way to do
it.
Die Betonung liegt hier darauf, dass es einen offensichtlichen Weg geben sollte, „es“ zu tun. Im Falle von Dict-Update-Operationen gibt es mindestens zwei verschiedene Operationen, die wir möglicherweise durchführen möchten
- Ein Dict direkt aktualisieren: Der offensichtliche Weg ist die Verwendung der
update()-Methode. Wenn dieser Vorschlag angenommen wird, funktioniert auch der erweiterte Zuweisungsoperator|=, aber das ist ein Nebeneffekt, wie erweiterte Zuweisungen definiert sind. Welchen Sie wählen, ist Geschmackssache. - Zwei vorhandene Dictionaries zu einem dritten, neuen Dictionary zusammenführen: Diese PEP schlägt vor, dass der offensichtliche Weg die Verwendung des Fusionsoperators
|ist.
In der Praxis wird diese Präferenz für „nur einen Weg“ in Python häufig verletzt. Zum Beispiel könnte jede for-Schleife als while-Schleife neu geschrieben werden; jeder if-Block könnte als if/else-Block geschrieben werden. Listen-, Mengen- und Dict-Comprehensions könnten alle durch Generatorausdrücke ersetzt werden. Listen bieten nicht weniger als fünf Möglichkeiten zur Durchführung von Verkettungen.
- Verkettungsoperator:
a + b - In-Place-Verkettungsoperator:
a += b - Slice-Zuweisung:
a[len(a):] = b - Sequenz-Unpacking:
[*a, *b] - Extend-Methode:
a.extend(b)
Wir sollten nicht zu streng sein, wenn es darum geht, nützliche Funktionalität abzulehnen, nur weil sie gegen „nur einen Weg“ verstößt.
Dict-Vereinigung macht Code schwerer verständlich
Die Dict-Vereinigung erschwert die Interpretation von Code. Um den Einwand zu paraphrasieren, anstatt jemanden speziell zu zitieren: „Wenn ich spam | eggs sehe, kann ich nicht sagen, was es tut, es sei denn, ich weiß, was spam und eggs sind.“
Antwort
Das ist sehr wahr. Aber es gilt heute genauso, wo die Verwendung des Operators | bedeuten könnte
- Bitweise OR für
int/bool - Vereinigung von
set/frozenset - jede andere überladene Operation
Das Hinzufügen der Dict-Vereinigung zur Menge der Möglichkeiten macht das Verstehen von Code nicht *schwieriger*. Es ist nicht mehr Arbeit erforderlich, um festzustellen, dass spam und eggs Mappings sind, als es dauern würde, festzustellen, dass sie Mengen oder ganze Zahlen sind. Und gute Namenskonventionen helfen.
flags |= WRITEABLE # Probably numeric bitwise-or.
DO_NOT_RUN = WEEKENDS | HOLIDAYS # Probably set union.
settings = DEFAULT_SETTINGS | user_settings | workspace_settings # Probably dict union.
Was ist mit der vollständigen set-API?
Dictionaries sind „mengenähnlich“ und sollten die volle Sammlung von Mengenoperatoren unterstützen: |, &, ^ und -.
Antwort
Diese PEP nimmt keine Stellung dazu, ob Dictionaries die volle Sammlung von Mengenoperatoren unterstützen sollten, und würde dies lieber für eine spätere PEP aufheben (einer der Autoren ist daran interessiert, eine solche PEP zu entwerfen). Zum Nutzen jeder späteren PEP folgt eine kurze Zusammenfassung.
Die symmetrische Mengendifferenz (^) ist offensichtlich und natürlich. Zum Beispiel, gegeben zwei Dictionaries
d1 = {"spam": 1, "eggs": 2}
d2 = {"ham": 3, "eggs": 4}
wäre die symmetrische Differenz d1 ^ d2 {"spam": 1, "ham": 3}.
Die Mengendifferenz (-) ist ebenfalls offensichtlich und natürlich, und eine frühere Version dieser PEP enthielt sie im Vorschlag. Bei den obigen Dictionaries hätten wir d1 - d2 als {"spam": 1} und d2 - d1 als {"ham": 3}.
Die Mengen-Schnittmenge (&) ist etwas problematischer. Während es einfach ist, die Schnittmenge der *Schlüssel* in zwei Dictionaries zu ermitteln, ist nicht klar, was mit den *Werten* geschehen soll. Bei den beiden obigen Dictionaries ist offensichtlich, dass der einzige Schlüssel von d1 & d2 "eggs" sein muss. „Zuletzt gesehen gewinnt“ hat jedoch den Vorteil der Konsistenz mit anderen Dict-Operationen (und den vorgeschlagenen Union-Operatoren).
Was ist mit Mapping und MutableMapping?
collections.abc.Mapping und collections.abc.MutableMapping sollten | und |= definieren, sodass Unterklassen die neuen Operatoren einfach erben könnten, anstatt sie definieren zu müssen.
Antwort
Es gibt zwei Hauptgründe, warum das Hinzufügen der neuen Operatoren zu diesen Klassen problematisch wäre
- Derzeit definiert keine von beiden eine
copy-Methode, die für|erforderlich wäre, um eine neue Instanz zu erstellen. - Das Hinzufügen von
|=zuMutableMapping(oder einercopy-Methode zuMapping) würde Kompatibilitätsprobleme für virtuelle Unterklassen verursachen.
Abgelehnte Ideen
Abgelehnte Semantik
Es gab mindestens vier weitere vorgeschlagene Lösungen für den Umgang mit widersprüchlichen Schlüsseln. Diese Alternativen sind Unterklassen von dict überlassen.
Fehler auslösen
Es ist nicht klar, ob dieses Verhalten viele Anwendungsfälle hat oder oft nützlich sein wird, aber es wird wahrscheinlich ärgerlich sein, da jede Verwendung des Dict-Union-Operators mit einer try/except-Klausel abgesichert werden müsste.
Werte addieren (wie Counter mit +)
Zu spezialisiert, um als Standardverhalten verwendet zu werden.
Der linke Wert (zuerst gesehen) gewinnt
Es ist nicht klar, ob dieses Verhalten viele Anwendungsfälle hat. Tatsächlich kann man einfach die Reihenfolge der Argumente umkehren
d2 | d1 # d1 merged with d2, keeping existing values in d1
Werte in einer Liste verketten
Dies ist wahrscheinlich zu spezialisiert, um das Standardverhalten zu sein. Es ist nicht klar, was zu tun ist, wenn die Werte bereits Listen sind.
{'a': [1, 2]} | {'a': [3, 4]}
Sollte dies ergeben {'a': [1, 2, 3, 4]} oder {'a': [[1, 2], [3, 4]]}?
Abgelehnte Alternativen
Additionsoperator verwenden
Diese PEP begann ursprünglich als Vorschlag für die Dict-Addition unter Verwendung der Operatoren + und +=. Diese Wahl erwies sich als äußerst kontrovers, da viele Leute ernsthafte Einwände gegen die Wahl des Operators hatten. Details finden Sie unter früheren Versionen der PEP und den Diskussionen in der Mailingliste unter Diskussionen.
Links-Shift-Operator verwenden
Der Operator << erhielt auf Python-Ideas nicht viel Unterstützung, aber auch keine größeren Einwände. Vielleicht der stärkste Einwand war Chris Angelicos Kommentar
Der „Niedlichkeitswert“ des Missbrauchs des Operators zur Anzeige von Informationsfluss wurde kurz nachdem C++ es getan hatte, alt.
Neuen Links-Pfeil-Operator verwenden
Ein weiterer Vorschlag war, einen neuen Operator <- zu erstellen. Leider wäre dies mehrdeutig, d <- e könnte d zusammenführen e oder d weniger-als minus e bedeuten.
Eine Methode verwenden
Eine Methode dict.merged() würde die Notwendigkeit eines Operators ganz vermeiden. Eine Feinheit ist, dass sie wahrscheinlich leicht unterschiedliche Implementierungen benötigen würde, wenn sie als ungebundene Methode und als gebundene Methode aufgerufen wird.
Als ungebundene Methode könnte das Verhalten ähnlich sein wie
def merged(cls, *mappings, **kw):
new = cls() # Will this work for defaultdict?
for m in mappings:
new.update(m)
new.update(kw)
return new
Als gebundene Methode könnte das Verhalten ähnlich sein wie
def merged(self, *mappings, **kw):
new = self.copy()
for m in mappings:
new.update(m)
new.update(kw)
return new
Vorteile
- Man könnte argumentieren, dass Methoden auffindbarer sind als Operatoren.
- Die Methode könnte beliebig viele Positions- und Schlüsselwortargumente akzeptieren und so die Ineffizienz der Erstellung temporärer Dictionaries vermeiden.
- Akzeptiert Sequenzen von
(key, value)-Paaren wie dieupdate-Methode. - Da es sich um eine Methode handelt, kann sie in einer Unterklasse leicht überschrieben werden, wenn Sie alternative Verhaltensweisen wie „zuerst gewinnt“, „eindeutige Schlüssel“ usw. benötigen.
Nachteile
- Würde wahrscheinlich eine neue Art von Methoden-Decorator benötigen, der das Verhalten regulärer Instanzmethoden und
classmethodkombiniert. Er müsste öffentlich sein (aber nicht unbedingt ein integriertes), für diejenigen, die die Methode überschreiben müssen. Es gibt einen Proof of Concept. - Es ist kein Operator. Guido diskutiert warum Operatoren nützlich sind. Eine andere Perspektive finden Sie in Alyssa Coghlans Blogbeitrag.
Eine Funktion verwenden
Anstatt einer Methode, verwenden Sie eine neue eingebaute Funktion merged(). Eine mögliche Implementierung könnte etwa so aussehen
def merged(*mappings, **kw):
if mappings and isinstance(mappings[0], dict):
# If the first argument is a dict, use its type.
new = mappings[0].copy()
mappings = mappings[1:]
else:
# No positional arguments, or the first argument is a
# sequence of (key, value) pairs.
new = dict()
for m in mappings:
new.update(m)
new.update(kw)
return new
Eine Alternative könnte sein, auf beliebige Schlüsselwörter zu verzichten und einen einzelnen Schlüsselwortparameter zu verwenden, der das Verhalten bei Kollisionen angibt.
def merged(*mappings, on_collision=lambda k, v1, v2: v2):
# implementation left as an exercise to the reader
Vorteile
- Die meisten der gleichen Vorteile wie bei den obigen Methoden.
- Erfordert keine Unterklasse zur Implementierung alternativer Verhaltensweisen bei Kollisionen, nur eine Funktion.
Nachteile
- Ist möglicherweise nicht wichtig genug, um in die integrierten Funktionen aufgenommen zu werden.
- Schwer, das Verhalten zu überschreiben, wenn man so etwas wie „zuerst gewinnt“ braucht, ohne die Möglichkeit zu verlieren, beliebige Schlüsselwortargumente zu verarbeiten.
Beispiele
Die Autoren dieser PEP haben eine Umfrage unter Drittanbieterbibliotheken für Dictionary-Merging durchgeführt, die Kandidaten für Dict-Union sein könnten.
Dies ist eine oberflächliche Liste, die auf einer Teilmenge der beliebigen Drittanbieterpakete basiert, die auf dem Computer eines der Autoren installiert waren, und spiegelt möglicherweise nicht den aktuellen Zustand eines Pakets wider. Beachten Sie auch, dass, während weitere (unabhängige) Refactorings möglich sind, die neu geschriebene Version nur die Verwendung der neuen Operatoren für einen direkten Vergleich hinzufügt. Sie reduziert auch das Ergebnis auf einen Ausdruck, wenn dies effizient möglich ist.
IPython/zmq/ipkernel.py
Vorher
aliases = dict(kernel_aliases)
aliases.update(shell_aliases)
Nachher
aliases = kernel_aliases | shell_aliases
IPython/zmq/kernelapp.py
Vorher
kernel_aliases = dict(base_aliases)
kernel_aliases.update({
'ip' : 'KernelApp.ip',
'hb' : 'KernelApp.hb_port',
'shell' : 'KernelApp.shell_port',
'iopub' : 'KernelApp.iopub_port',
'stdin' : 'KernelApp.stdin_port',
'parent': 'KernelApp.parent',
})
if sys.platform.startswith('win'):
kernel_aliases['interrupt'] = 'KernelApp.interrupt'
kernel_flags = dict(base_flags)
kernel_flags.update({
'no-stdout' : (
{'KernelApp' : {'no_stdout' : True}},
"redirect stdout to the null device"),
'no-stderr' : (
{'KernelApp' : {'no_stderr' : True}},
"redirect stderr to the null device"),
})
Nachher
kernel_aliases = base_aliases | {
'ip' : 'KernelApp.ip',
'hb' : 'KernelApp.hb_port',
'shell' : 'KernelApp.shell_port',
'iopub' : 'KernelApp.iopub_port',
'stdin' : 'KernelApp.stdin_port',
'parent': 'KernelApp.parent',
}
if sys.platform.startswith('win'):
kernel_aliases['interrupt'] = 'KernelApp.interrupt'
kernel_flags = base_flags | {
'no-stdout' : (
{'KernelApp' : {'no_stdout' : True}},
"redirect stdout to the null device"),
'no-stderr' : (
{'KernelApp' : {'no_stderr' : True}},
"redirect stderr to the null device"),
}
matplotlib/backends/backend_svg.py
Vorher
attrib = attrib.copy()
attrib.update(extra)
attrib = attrib.items()
Nachher
attrib = (attrib | extra).items()
matplotlib/delaunay/triangulate.py
Vorher
edges = {}
edges.update(dict(zip(self.triangle_nodes[border[:,0]][:,1],
self.triangle_nodes[border[:,0]][:,2])))
edges.update(dict(zip(self.triangle_nodes[border[:,1]][:,2],
self.triangle_nodes[border[:,1]][:,0])))
edges.update(dict(zip(self.triangle_nodes[border[:,2]][:,0],
self.triangle_nodes[border[:,2]][:,1])))
Umschreiben als
edges = {}
edges |= zip(self.triangle_nodes[border[:,0]][:,1],
self.triangle_nodes[border[:,0]][:,2])
edges |= zip(self.triangle_nodes[border[:,1]][:,2],
self.triangle_nodes[border[:,1]][:,0])
edges |= zip(self.triangle_nodes[border[:,2]][:,0],
self.triangle_nodes[border[:,2]][:,1])
matplotlib/legend.py
Vorher
hm = default_handler_map.copy()
hm.update(self._handler_map)
return hm
Nachher
return default_handler_map | self._handler_map
numpy/ma/core.py
Vorher
_optinfo = {}
_optinfo.update(getattr(obj, '_optinfo', {}))
_optinfo.update(getattr(obj, '_basedict', {}))
if not isinstance(obj, MaskedArray):
_optinfo.update(getattr(obj, '__dict__', {}))
Nachher
_optinfo = {}
_optinfo |= getattr(obj, '_optinfo', {})
_optinfo |= getattr(obj, '_basedict', {})
if not isinstance(obj, MaskedArray):
_optinfo |= getattr(obj, '__dict__', {})
praw/internal.py
Vorher
data = {'name': six.text_type(user), 'type': relationship}
data.update(kwargs)
Nachher
data = {'name': six.text_type(user), 'type': relationship} | kwargs
pygments/lexer.py
Vorher
kwargs.update(lexer.options)
lx = lexer.__class__(**kwargs)
Nachher
lx = lexer.__class__(**(kwargs | lexer.options))
requests/sessions.py
Vorher
merged_setting = dict_class(to_key_val_list(session_setting))
merged_setting.update(to_key_val_list(request_setting))
Nachher
merged_setting = dict_class(to_key_val_list(session_setting)) | to_key_val_list(request_setting)
sphinx/domains/__init__.py
Vorher
self.attrs = self.known_attrs.copy()
self.attrs.update(attrs)
Nachher
self.attrs = self.known_attrs | attrs
sphinx/ext/doctest.py
Vorher
new_opt = code[0].options.copy()
new_opt.update(example.options)
example.options = new_opt
Nachher
example.options = code[0].options | example.options
sphinx/ext/inheritance_diagram.py
Vorher
n_attrs = self.default_node_attrs.copy()
e_attrs = self.default_edge_attrs.copy()
g_attrs.update(graph_attrs)
n_attrs.update(node_attrs)
e_attrs.update(edge_attrs)
Nachher
g_attrs |= graph_attrs
n_attrs = self.default_node_attrs | node_attrs
e_attrs = self.default_edge_attrs | edge_attrs
sphinx/highlighting.py
Vorher
kwargs.update(self.formatter_args)
return self.formatter(**kwargs)
Nachher
return self.formatter(**(kwargs | self.formatter_args))
sphinx/quickstart.py
Vorher
d2 = DEFAULT_VALUE.copy()
d2.update(dict(("ext_"+ext, False) for ext in EXTENSIONS))
d2.update(d)
d = d2
Nachher
d = DEFAULT_VALUE | dict(("ext_"+ext, False) for ext in EXTENSIONS) | d
sympy/abc.py
Vorher
clash = {}
clash.update(clash1)
clash.update(clash2)
return clash1, clash2, clash
Nachher
return clash1, clash2, clash1 | clash2
sympy/parsing/maxima.py
Vorher
dct = MaximaHelpers.__dict__.copy()
dct.update(name_dict)
obj = sympify(str, locals=dct)
Nachher
obj = sympify(str, locals=MaximaHelpers.__dict__|name_dict)
sympy/printing/ccode.py und sympy/printing/fcode.py
Vorher
self.known_functions = dict(known_functions)
userfuncs = settings.get('user_functions', {})
self.known_functions.update(userfuncs)
Nachher
self.known_functions = known_functions | settings.get('user_functions', {})
sympy/utilities/runtests.py
Vorher
globs = globs.copy()
if extraglobs is not None:
globs.update(extraglobs)
Nachher
globs = globs | (extraglobs if extraglobs is not None else {})
Die obigen Beispiele zeigen, dass der Operator | manchmal zu einer deutlichen Steigerung der Lesbarkeit führt, die Anzahl der Codezeilen reduziert und die Klarheit verbessert. Andere Beispiele, die den Operator | verwenden, führen jedoch zu langen, komplexen einzelnen Ausdrücken, die möglicherweise weit über die maximale Zeilenlänge von 80 Zeichen von PEP 8 hinausgehen. Wie bei jedem anderen Sprachmerkmal sollte der Programmierer sein eigenes Urteilsvermögen walten lassen, ob | seinen Code verbessert.
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0584.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT