Ich versuche, eine längliche hohle "Daten" -Klasse in ein benanntes Tupel umzuwandeln. Meine Klasse sieht derzeit so aus:
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
Nach der Konvertierung namedtuple
sieht es so aus:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Aber hier gibt es ein Problem. In meiner ursprünglichen Klasse konnte ich nur einen Wert übergeben und mich um den Standard kümmern, indem ich Standardwerte für die Argumente named / keyword verwendete. Etwas wie:
class BinaryTree(object):
def __init__(self, val):
self.root = Node(val)
Dies funktioniert jedoch nicht bei meinem überarbeiteten benannten Tupel, da erwartet wird, dass ich alle Felder übergebe. Ich kann natürlich die Vorkommen von Node(val)
to ersetzen , Node(val, None, None)
aber es gefällt mir nicht.
Gibt es also einen guten Trick, der mein Umschreiben erfolgreich macht, ohne viel Codekomplexität hinzuzufügen (Metaprogrammierung), oder sollte ich einfach die Pille schlucken und mit dem "Suchen und Ersetzen" fortfahren? :) :)
Node
Klasse so wie sie ist. Warum in benanntes Tupel konvertieren?Node
und andere Klassen einfache Datenhalter-Wertobjekte mit einer Reihe verschiedener Felder sind (Node
ist nur eines davon). Diese Klassendeklarationen sind meiner Meinung nach nicht viel mehr als Leitungsrauschen und wollten sie daher reduzieren. Warum etwas pflegen, was nicht benötigt wird? :).debug_print()
Methode, die über den Baum läuft und ihn druckt?BinaryTree
Klasse.Node
und andere Dateninhaber benötigen keine solchen speziellen Methoden, insbesondere da benannte Tupel eine anständige__str__
und__repr__
repräsentative Form haben . :)Antworten:
Python 3.7
Verwenden Sie den Standardparameter .
Oder noch besser, verwenden Sie die neue Datenklassenbibliothek , die viel besser ist als namedtuple.
Vor Python 3.7
Auf
Node.__new__.__defaults__
die Standardwerte setzen.Vor Python 2.6
Auf
Node.__new__.func_defaults
die Standardwerte setzen.Auftrag
Wenn Sie in allen Python-Versionen weniger Standardwerte als im Namedtuple festlegen, werden die Standardeinstellungen auf die Parameter ganz rechts angewendet. Auf diese Weise können Sie einige Argumente als erforderliche Argumente beibehalten.
Wrapper für Python 2.6 bis 3.6
Hier ist ein Wrapper für Sie, mit dem Sie sogar (optional) die Standardwerte auf etwas anderes als festlegen können
None
. Dies unterstützt keine erforderlichen Argumente.Beispiel:
quelle
isinstance
... alle Vor- und Nachteile ... schade, dass Sie etwas spät dran waren die Party. Dies ist die beste Antwort.(None)
es kein Tupel ist, es istNone
. Wenn Sie(None,)
stattdessen verwenden, sollte es gut funktionieren.Node.__new__.__defaults__= (None,) * len(Node._fields)
Ich habe namedtuple in eine Unterklasse unterteilt und die
__new__
Methode überschrieben :Dadurch bleibt eine intuitive Typhierarchie erhalten, die bei der Erstellung einer als Klasse getarnten Factory-Funktion nicht möglich ist.
quelle
__new__
wird nicht aufgerufen, wenn_replace
verwendet wird.Wickeln Sie es in eine Funktion.
quelle
isinstance(Node('val'), Node)
: Es wird jetzt eine Ausnahme ausgelöst, anstatt True zurückzugeben. Die Antwort von @ justinfay (unten) ist zwar etwas ausführlicher, behält jedoch die Informationen zur Typhierarchie ordnungsgemäß bei. Dies ist wahrscheinlich ein besserer Ansatz, wenn andere mit Node-Instanzen interagieren.def make_node(...):
anstatt vorzutäuschen, dass es sich um eine Klassendefinition handelt. Auf diese Weise sind Benutzer nicht versucht, die Funktion auf Typpolymorphismus zu überprüfen, sondern verwenden die Tupeldefinition selbst.isinstance
falsch verwendet werden.Mit
typing.NamedTuple
in Python 3.6.1+ können Sie einem NamedTuple-Feld sowohl einen Standardwert als auch eine Typanmerkung bereitstellen. Verwendentyping.Any
Sie, wenn Sie nur die erstere benötigen:Verwendungszweck:
Falls Sie sowohl Standardwerte als auch optionale Veränderbarkeit benötigen, wird Python 3.7 Datenklassen (PEP 557) haben , die in einigen (vielen?) Fällen Namedtuples ersetzen können.
Nebenbemerkung: Eine Besonderheit der aktuellen Spezifikation von Annotationen (Ausdrücke nach
:
für Parameter und Variablen und nach->
für Funktionen) in Python ist, dass sie zum Definitionszeitpunkt * ausgewertet werden . Da also "Klassennamen definiert werden, sobald der gesamte Hauptteil der Klasse ausgeführt wurde", müssen die Anmerkungen'Node'
in den obigen Klassenfeldern Zeichenfolgen sein, um NameError zu vermeiden.Diese Art von Typhinweisen wird als "Vorwärtsreferenz" ( [1] , [2] ) bezeichnet, und mit PEP 563 wird Python 3.7+ einen
__future__
Import haben (der in 4.0 standardmäßig aktiviert ist), der die Verwendung von Vorwärtsreferenzen ermöglicht ohne Anführungszeichen, Verschiebung ihrer Bewertung.* AFAICT Nur lokale Variablenanmerkungen werden zur Laufzeit nicht ausgewertet. (Quelle: PEP 526 )
quelle
left
undright
(dhNode
) vom selben Typ wie die zu definierende Klasse ist und daher als Zeichenfolgen geschrieben werden muss.my_list: List[T] = None
self.my_list = my_list if my_list is not None else []
? Können wir solche Standardparameter nicht verwenden?typing.NamedTuple
. Bei Datenklassen können Sie jedochField
Objekte mit einemdefault_factory
attr verwenden. Ersetzen Sie dazu Ihre Redewendung durchmy_list: List[T] = field(default_factory=list)
.Dies ist ein Beispiel direkt aus den Dokumenten :
Das Beispiel des OP wäre also:
Ich mag jedoch einige der anderen hier gegebenen Antworten besser. Ich wollte dies nur der Vollständigkeit halber hinzufügen.
quelle
_
Methode entschieden haben (was im Grunde genommen eine private bedeutet), für etwas,replace
das ziemlich nützlich erscheint.*args
. Es kann sein, dass es der Sprache hinzugefügt wurde, bevor viele dieser Dinge standardisiert wurden._
Präfix soll verhindern, dass Kollisionen mit den Namen der benutzerdefinierten Tupelfelder auftreten (relevantes Dokumentzitat: "Für einen Feldnamen kann jeder gültige Python-Bezeichner verwendet werden, außer für Namen, die mit einem Unterstrich beginnen."). Was die durch Leerzeichen getrennte Zeichenfolge betrifft, denke ich, dass dies nur dazu dient, ein paar Tastenanschläge zu speichern (und Sie können eine Folge von Zeichenfolgen übergeben, wenn Sie dies bevorzugen)._
macht das dann sehr viel Sinn.Ich bin mir nicht sicher, ob es nur mit dem eingebauten Namedupuple einen einfachen Weg gibt. Es gibt ein nettes Modul namens recordtype , das diese Funktionalität hat:
quelle
recordtype
für zukünftige Arbeiten sicherlich interessant aussieht. +1recordtype
veränderlich ist, während diesnamedtuple
nicht der Fall ist. Dies kann von Bedeutung sein, wenn Sie möchten, dass das Objekt hashbar ist (was Sie wahrscheinlich nicht tun, da es als Klasse begann).Hier ist eine kompaktere Version, die von der Antwort von justinfay inspiriert wurde:
quelle
Node(1, 2)
Beachten Sie, dass dies mit diesem Rezept nicht funktioniert, aber in der Antwort von @ justinfay funktioniert. Ansonsten ist es ziemlich geschickt (+1).In Python3.7 + gibt es ein brandneues Argument für Standardwerte = Schlüsselwörter.
Anwendungsbeispiel:
quelle
Kurz, einfach und führt nicht dazu, dass Menschen
isinstance
unsachgemäß verwenden:quelle
Ein leicht erweitertes Beispiel zum Initialisieren aller fehlenden Argumente mit
None
:quelle
Python 3.7: Einführung von
defaults
param in die Namedtuple-Definition.Beispiel wie in der Dokumentation gezeigt:
Lesen Sie hier mehr .
quelle
Sie können auch Folgendes verwenden:
Dies gibt Ihnen grundsätzlich die Möglichkeit, ein beliebiges benanntes Tupel mit einem Standardwert zu erstellen und nur die Parameter zu überschreiben, die Sie benötigen, zum Beispiel:
quelle
Kombinieren von Ansätzen von @Denis und @Mark:
Dies sollte die Erstellung des Tupels mit Positionsargumenten und auch mit gemischten Fällen unterstützen. Testfälle:
unterstützen aber auch TypeError:
quelle
Ich finde diese Version leichter zu lesen:
Dies ist nicht so effizient, da das Objekt zweimal erstellt werden muss. Sie können dies jedoch ändern, indem Sie das Standard-Duple im Modul definieren und nur die Funktion die Ersetzungszeile ausführen lassen.
quelle
Da Sie
namedtuple
als Datenklasse verwenden, sollten Sie sich darüber im Klaren sein, dass Python 3.7 genau@dataclass
zu diesem Zweck einen Dekorator einführt - und natürlich Standardwerte hat.Ein Beispiel aus den Dokumenten :
Viel sauberer, lesbarer und benutzerfreundlicher als Hacken
namedtuple
. Es ist nicht schwer vorherzusagen, dass die Verwendung vonnamedtuple
s mit der Einführung von 3.7 sinken wird.quelle
Inspiriert von dieser Antwort auf eine andere Frage, ist hier meine vorgeschlagene Lösung, die auf einer Metaklasse basiert und verwendet
super
(um zukünftige Subkalibrierungen korrekt zu handhaben). Es ist der Antwort von justinfay ziemlich ähnlich .Dann:
quelle
Die Antwort von jterrace auf die Verwendung von recordtype ist großartig, aber der Autor der Bibliothek empfiehlt, sein Namedlist- Projekt zu verwenden, das sowohl veränderbare (
namedlist
) als auch unveränderliche (namedtuple
) Implementierungen bietet .quelle
Hier ist eine kurze, einfache generische Antwort mit einer schönen Syntax für ein benanntes Tupel mit Standardargumenten:
Verwendungszweck:
Minimiert:
quelle
Mit der
NamedTuple
Klasse aus meinerAdvanced Enum (aenum)
Bibliothek und derclass
Syntax ist dies ganz einfach:Der einzige mögliche Nachteil ist die Anforderung einer
__doc__
Zeichenfolge für jedes Attribut mit einem Standardwert (optional für einfache Attribute). Im Gebrauch sieht es so aus:Die Vorteile gegenüber
justinfay's answer
:ist Einfachheit, sowie
metaclass
basiert stattexec
basiert.quelle
Eine andere Lösung:
Verwendungszweck:
quelle
Hier ist eine weniger flexible, aber präzisere Version von Mark Lodatos Wrapper: Er verwendet die Felder und Standardeinstellungen als Wörterbuch.
Beispiel:
quelle
dict
hat keine Garantie für die Bestellung.