Próbuję przekonwertować podłużną pustą klasę „data” na krotkę nazwaną. Moja klasa wygląda obecnie tak:
class Node(object):
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
Po konwersji namedtuple
wygląda następująco:
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
Ale tutaj jest problem. Moja oryginalna klasa pozwoliła mi przekazać tylko wartość i zająłem się wartością domyślną, używając domyślnych wartości dla argumentów nazwanych / słów kluczowych. Coś jak:
class BinaryTree(object):
def __init__(self, val):
self.root = Node(val)
Ale to nie działa w przypadku mojego refaktoryzowanego krotki o nazwie krotka, ponieważ oczekuje, że przejdę wszystkie pola. Mogę oczywiście zastąpić wystąpienia Node(val)
do Node(val, None, None)
, ale nie jest mi do gustu.
Czy istnieje więc dobra sztuczka, która może sprawić, że moje ponowne pisanie zakończy się powodzeniem bez dodawania dużej złożoności kodu (metaprogramowanie), czy też powinienem po prostu przełknąć pigułkę i przejść do „wyszukiwania i zamiany”? :)
Node
klasa taka, jaka jest. Po co konwertować na nazwaną krotkę?Node
i inne klasy są prostymi obiektami posiadającymi dane z wieloma różnymi polami (Node
jest tylko jednym z nich). Te deklaracje klas są niczym więcej niż szumem linii, dlatego IMHO chciał je wyciąć. Po co utrzymywać coś, co nie jest wymagane? :).debug_print()
metody, która kroczy po drzewie i drukuje je?BinaryTree
klasy.Node
i inni posiadacze danych nie wymagają specjalnych metod esp takie dane, które nazwane krotki mieć przyzwoity__str__
i__repr__
reprezentacji. :)Odpowiedzi:
Python 3.7
Użyj parametru defaults .
Lub jeszcze lepiej, skorzystaj z nowej biblioteki klas danych , która jest o wiele ładniejsza niż namedtuple.
Przed Pythonem 3.7
Ustaw
Node.__new__.__defaults__
wartości domyślne.Przed Pythonem 2.6
Ustaw
Node.__new__.func_defaults
wartości domyślne.Zamówienie
We wszystkich wersjach Pythona, jeśli ustawisz mniej wartości domyślnych niż istnieje w namedtuple, wartości domyślne zostaną zastosowane do skrajnie prawych parametrów. Pozwala to zachować niektóre argumenty jako wymagane argumenty.
Opakowanie dla Pythona od 2.6 do 3.6
Oto opakowanie dla Ciebie, które pozwala (opcjonalnie) ustawić wartości domyślne na coś innego niż
None
. To nie obsługuje wymaganych argumentów.Przykład:
źródło
isinstance
... wszystkie zalety, bez wad ... szkoda, że spóźniłeś się trochę impreza. To najlepsza odpowiedź.(None)
nie jest to krotkaNone
. Jeśli użyjesz(None,)
zamiast tego, powinno działać dobrze.Node.__new__.__defaults__= (None,) * len(Node._fields)
Podklasowałem nazwanepleple i zastąpiłem
__new__
metodę:Zachowuje to intuicyjną hierarchię typów, czego nie robi tworzenie funkcji fabryki ukrytej pod klasą.
źródło
__new__
nie jest wywoływany, gdy_replace
jest używany.Zawiń to w funkcję.
źródło
isinstance(Node('val'), Node)
: spowoduje teraz wyjątek, zamiast zwracać wartość True. Chociaż nieco bardziej szczegółowe, odpowiedź @ justinfay (poniżej) prawidłowo zachowuje informacje o hierarchii typów, więc prawdopodobnie jest to lepsze podejście, jeśli inni będą wchodzić w interakcje z instancjami Węzła.def make_node(...):
a nie udając, że jest to definicja klasy. W ten sposób użytkownicy nie mają ochoty sprawdzać polimorfizmu typu w funkcji, ale używają samej definicji krotki.isinstance
niewłaściwego używania .W
typing.NamedTuple
Pythonie 3.6.1+ możesz podać zarówno wartość domyślną, jak i adnotację typu w polu NamedTuple. Użyj,typing.Any
jeśli potrzebujesz tylko tego pierwszego:Stosowanie:
Ponadto, jeśli potrzebujesz zarówno wartości domyślnych, jak i opcjonalnej zmienności, Python 3.7 będzie miał klasy danych (PEP 557), które mogą w niektórych (wielu?) Przypadkach zastąpić nazwane pary.
Sidenote: jednym dziwactwem obecnej specyfikacji adnotacji (wyrażeń po
:
parametrach i zmiennych oraz po->
funkcjach) w Pythonie jest to, że są one oceniane w czasie definicji * . Tak więc, ponieważ „nazwy klas są definiowane po wykonaniu całego ciała klasy”, adnotacje'Node'
w polach klasy powyżej muszą być ciągami znaków, aby uniknąć błędu nazwy.Tego rodzaju wskazówki typu nazywa się „referencjami do przodu” ( [1] , [2] ), a w PEP 563 Python 3.7+ będzie miał
__future__
import (domyślnie włączony w 4.0), który pozwoli na korzystanie z referencji do przodu bez cytatów, odroczenie ich oceny.* Adnotacje tylko lokalnych zmiennych AFAICT nie są analizowane w czasie wykonywania. (źródło: PEP 526 )
źródło
left
iright
(tj.Node
) Jest tego samego typu co definiowana klasa, a zatem musi być napisany jako ciąg.my_list: List[T] = None
self.my_list = my_list if my_list is not None else []
? Czy nie możemy użyć takich domyślnych parametrów?typing.NamedTuple
. Ale dzięki klasom danych możesz używaćField
obiektów odefault_factory
attr. w tym celu zastępując swój idiommy_list: List[T] = field(default_factory=list)
.Oto przykład prosto z dokumentacji :
Tak więc przykładem PO byłoby:
Jednak bardziej podoba mi się niektóre inne odpowiedzi podane tutaj. Chciałem tylko dodać to dla kompletności.
źródło
_
metodę (która w zasadzie oznacza metodę prywatną) dla czegoś,replace
co wydaje się dość przydatne ..*args
. Być może zostało to dodane do języka, zanim wiele z tych rzeczy zostało znormalizowanych._
Prefiks jest, aby uniknąć kolizji z nazwami pól krotka zdefiniowane przez użytkownika (dotyczy doc cytat: „Każda ważna Python identyfikator może być używany do nazwy pola z wyjątkiem nazw zaczynających się od znaku podkreślenia.”). Jeśli chodzi o ciąg oddzielony spacjami, myślę, że to po prostu zaoszczędzić kilka naciśnięć klawiszy (i możesz przekazać sekwencję ciągów, jeśli wolisz)._
ma to sens.Nie jestem pewien, czy istnieje prosty sposób dzięki wbudowanej nazwie o nazwie. Jest ładny moduł o nazwie recordtype, który ma tę funkcjonalność:
źródło
recordtype
pewnością wygląda interesująco dla przyszłych prac. +1recordtype
można go modyfikować, podczas gdynamedtuple
nie. Może to mieć znaczenie, jeśli chcesz, aby obiekt był haszowalny (co, jak sądzę, nie masz, ponieważ zaczął jako klasa).Oto bardziej kompaktowa wersja inspirowana odpowiedzią justinfay:
źródło
Node(1, 2)
który nie działa z tym przepisem, ale działa w odpowiedzi @ justinfay. W przeciwnym razie jest całkiem fajny (+1).W python3.7 + jest nowy argument defaults = słowo kluczowe.
Przykładowe użycie:
źródło
Krótko, prosto i nie prowadzi do
isinstance
niewłaściwego użycia :źródło
Nieco rozszerzony przykład inicjalizacji wszystkich brakujących argumentów za pomocą
None
:źródło
Python 3.7: wprowadzenie
defaults
param w definicji namedtuple.Przykład pokazany w dokumentacji:
Przeczytaj więcej tutaj .
źródło
Możesz także użyć tego:
Zasadniczo daje to możliwość skonstruowania dowolnej nazwanej krotki z wartością domyślną i zastąpienia tylko potrzebnych parametrów, na przykład:
źródło
Łącząc podejścia @Denis i @Mark:
To powinno wspierać tworzenie krotki z argumentami pozycyjnymi, a także z mieszanymi przypadkami. Przypadki testowe:
ale także obsługuje TypeError:
źródło
Ta wersja jest dla mnie łatwiejsza do odczytania:
Nie jest to tak wydajne, jak wymaga dwukrotnego utworzenia obiektu, ale można to zmienić, definiując domyślny dupleks w module i po prostu uruchamiając funkcję zastępowania wiersza.
źródło
Ponieważ używasz
namedtuple
jako klasy danych, powinieneś być świadom, że Python 3.7 wprowadzi@dataclass
dekorator do tego właśnie celu - i oczywiście ma wartości domyślne.Przykład z dokumentów :
Znacznie czystszy, czytelny i użyteczny niż hakowanie
namedtuple
. Nie jest trudno przewidzieć, że użycienamedtuple
s spadnie wraz z przyjęciem 3,7.źródło
Zainspirowany tą odpowiedzią na inne pytanie, oto moje proponowane rozwiązanie oparte na metaklasie i użyciu
super
(do prawidłowej obsługi przyszłych subkalowań). Jest dość podobny do odpowiedzi justinfay .Następnie:
źródło
Odpowiedź jterrace'a na użycie typu record jest świetna, ale autor biblioteki zaleca skorzystanie ze swojego projektu nazwanego , który zapewnia implementacje zarówno mutable (
namedlist
), jak i immutable (namedtuple
).źródło
Oto krótka, prosta ogólna odpowiedź z ładną składnią dla nazwanej krotki z domyślnymi argumentami:
Stosowanie:
Zminimalizowane:
źródło
Używając
NamedTuple
klasy z mojejAdvanced Enum (aenum)
biblioteki i używającclass
składni, jest to dość proste:Jedyną potencjalną wadą jest wymóg dla
__doc__
dowolnego atrybutu o wartości domyślnej (jest opcjonalny dla prostych atrybutów). W użyciu wygląda to tak:Zalety ma to w porównaniu z
justinfay's answer
:to prostota, a także bycie
metaclass
opartym zamiastexec
opartym.źródło
Inne rozwiązanie:
Stosowanie:
źródło
Oto mniej elastyczna, ale bardziej zwięzła wersja opakowania Marka Lodato: Pobiera pola i domyślnie jako słownik.
Przykład:
źródło
dict
nie ma gwarancji zamówienia.