Czy ktoś może zmienić namedtuple lub zapewnić alternatywną klasę, tak aby działała dla obiektów zmiennych?
Przede wszystkim ze względu na czytelność chciałbym coś podobnego do namedtuple, które robi to:
from Camelot import namedgroup
Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
Point(x=100, y=0)
Powstały przedmiot musi być możliwy do wytrawienia. Zgodnie z charakterystyką nazwanej krotki kolejność danych wyjściowych, gdy są reprezentowane, musi odpowiadać kolejności na liście parametrów podczas konstruowania obiektu.
python
mutable
namedtuple
Alexander
źródło
źródło
namedtuple
s, wydaje się, że nie ma potrzeby odwoływania się do atrybutów za pomocą indeksu, tj. Takp[0]
ip[1]
czy byłyby alternatywne sposoby odwoływania sięx
iy
odpowiednio, prawda?Odpowiedzi:
Istnieje zmienna alternatywa dla
collections.namedtuple
- recordclass .Ma to samo API i ślad pamięci, co
namedtuple
i obsługuje przypisania (powinno być również szybsze). Na przykład:Dla Pythona 3.6 i nowszych
recordclass
(od 0.5) obsługują wskazówki typu:Jest bardziej kompletny przykład (zawiera również porównania wydajności).
Od wersji 0.9
recordclass
biblioteka udostępnia inny wariant -recordclass.structclass
funkcję fabryczną. Może tworzyć klasy, których instancje zajmują mniej pamięci niż__slots__
instancje oparte na. Może to być ważne w przypadku instancji z wartościami atrybutów, które nie miały mieć cykli odwołań. Może pomóc zmniejszyć zużycie pamięci, jeśli musisz utworzyć miliony instancji. Oto przykład ilustrujący .źródło
recordclass
jest wolniejszy, zajmuje więcej pamięci i wymaga rozszerzeń C w porównaniu z recepturą Antti Haapala inamedlist
.recordclass
jest zmienną wersją tego,collection.namedtuple
która dziedziczy swój interfejs API, ślad pamięci, ale obsługuje przypisania.namedlist
jest w rzeczywistości instancją klasy Pythona z gniazdami. Jest to bardziej przydatne, jeśli nie potrzebujesz szybkiego dostępu do jego pól według indeksu.recordclass
przykład dostęp do atrybutów (python 3.5.2) jest około 2-3% wolniejszy niż w przypadkunamedlist
namedtuple
prostego tworzenia klasPoint = namedtuple('Point', 'x y')
Jedi może automatycznie uzupełniać atrybuty, podczas gdy nie jest to prawdąrecordclass
. Jeśli użyję dłuższego kodu tworzenia (na podstawieRecordClass
), Jedi rozumiePoint
klasę, ale nie rozumie jej konstruktora lub atrybuty ... Czy jest sposób, abyrecordclass
ładnie pracować z Jedi?types.SimpleNamespace został wprowadzony w Pythonie 3.3 i obsługuje żądane wymagania.
źródło
SimpleNamespace
nie zdaje testów 6-10 (dostęp przez indeks, iteracyjne rozpakowywanie, iteracja, uporządkowany dykt, zamiana na miejscu) i 12, 13 (pola, sloty). Zwróć uwagę, że dokumentacja (do której odsyłasz w odpowiedzi) wyraźnie mówi „SimpleNamespace
może być przydatna jako zamiennikclass NS: pass
. Jednak w przypadku rekordu strukturalnego użyjnamedtuple()
zamiast tego”.SimpleNamespace
tworzy obiekt, a nie konstruktor klasy, i nie może zastąpić namedtuple. Porównanie typów nie zadziała, a zużycie pamięci będzie znacznie większe.Jako bardzo Pythonowa alternatywa dla tego zadania, od czasu Python-3.7, możesz użyć
dataclasses
modułu, który nie tylko zachowuje się jak zmienny,NamedTuple
ponieważ używa normalnych definicji klas, ale obsługuje również inne funkcje klas.Od PEP-0557:
Ta funkcja została wprowadzona w PEP-0557 , o której można przeczytać bardziej szczegółowo, korzystając z łącza do dokumentacji.
Przykład:
Próbny:
źródło
dataclass
nie przechodzi testów 6-10 (dostęp przez indeks, iteracyjne rozpakowywanie, iteracja, uporządkowany dykt, zamiana na miejscu) oraz 12, 13 (pola, gniazda) w Pythonie 3.7 .1.Najnowsza lista nazwana 1.7 przechodzi wszystkie testy zarówno z Pythonem 2.7, jak i Pythonem 3.5 na dzień 11 stycznia 2016 r. Jest to implementacja w czystym Pythonie, natomiast rozszerzenie
recordclass
jest w C. Oczywiście to zależy od Twoich wymagań, czy preferowane jest rozszerzenie C.Twoje testy (ale zobacz też uwagę poniżej):
Dane wyjściowe w Pythonie 2.7
Jedyną różnicą w stosunku do Pythona 3.5 jest to, że
namedlist
stał się mniejszy, rozmiar wynosi 56 (Python 2.7 raportuje 64).Zwróć uwagę, że zmieniłem Twój test 10 na wymianę na miejscu.
namedlist
Ma_replace()
metody, która robi kopię płytkie i że ma sens dla mnie, ponieważnamedtuple
w bibliotece standardowej zachowuje się w ten sam sposób. Zmiana semantyki_replace()
metody byłaby myląca. Moim zdaniem ta_update()
metoda powinna być używana do aktualizacji w miejscu. A może nie zrozumiałem celu twojego testu 10?źródło
namedlist
przechowywania w instancji listy. Chodzi o to, żecpython
jestlist
to właściwie tablica dynamiczna. Z założenia przydziela więcej pamięci niż to konieczne, aby uczynić mutację listy tańszą.list
po__slots__
optymalizacji i domyślnie korzysta z niej . Kiedyrecordclass
recorclass
zużywa więcej pamięci, ponieważ jesttuple
podobnym obiektem o zmiennej wielkości pamięci.types.SimpleNamespace
. Niestety, pylintowi się to nie podoba :-(Wygląda na to, że odpowiedź na to pytanie brzmi: nie.
Poniżej jest dość blisko, ale technicznie nie można go zmienić. To jest tworzenie nowej
namedtuple()
instancji ze zaktualizowaną wartością x:Z drugiej strony możesz utworzyć prostą klasę,
__slots__
która powinna dobrze działać w przypadku częstego aktualizowania atrybutów instancji klas:Aby dodać do tej odpowiedzi, myślę, że
__slots__
jest to dobre użycie, ponieważ zapewnia wydajną pamięć podczas tworzenia wielu instancji klas. Jedynym minusem jest to, że nie możesz tworzyć nowych atrybutów klas.Oto jeden istotny wątek, który ilustruje wydajność pamięci - słownik kontra obiekt - który jest bardziej wydajny i dlaczego?
Cytowana treść odpowiedzi w tym wątku jest bardzo zwięzłym wyjaśnieniem, dlaczego
__slots__
jest bardziej wydajna pamięć - sloty Pythonaźródło
Oto dobre rozwiązanie dla Pythona 3: Minimalna klasa używająca
__slots__
iSequence
abstrakcyjna klasa bazowa; nie ma ochoty na wykrywanie błędów lub coś podobnego, ale działa i zachowuje się głównie jak zmienna krotka (z wyjątkiem sprawdzania typu).Przykład:
Jeśli chcesz, możesz mieć również metodę tworzenia klasy (chociaż użycie jawnej klasy jest bardziej przejrzyste):
Przykład:
W Pythonie 2 musisz nieco to zmienić - jeśli będziesz dziedziczyć z
Sequence
, klasa będzie miała a,__dict__
a__slots__
przestanie działać.Rozwiązaniem w Pythonie 2 nie jest dziedziczenie z
Sequence
, aleobject
. Jeśliisinstance(Point, Sequence) == True
chcesz, musisz zarejestrowaćNamedMutableSequence
jako klasę bazową, abySequence
:źródło
Zaimplementujmy to z dynamicznym tworzeniem typów:
Sprawdza atrybuty, aby sprawdzić, czy są prawidłowe, zanim pozwolisz na kontynuację operacji.
Więc czy to jest marynowane? Tak, jeśli (i tylko wtedy) wykonasz następujące czynności:
Definicja musi znajdować się w Twojej przestrzeni nazw i musi istnieć wystarczająco długo, aby pikle ją znalazło. Więc jeśli zdefiniujesz to jako w swoim pakiecie, powinno działać.
Pickle nie powiedzie się, jeśli wykonasz następujące czynności lub sprawisz, że definicja będzie tymczasowa (na przykład wyjdzie poza zakres po zakończeniu funkcji):
I tak, zachowuje kolejność pól wymienionych przy tworzeniu typu.
źródło
__iter__
metodę zfor k in self._attrs_: yield getattr(self, k)
, będzie ona obsługiwać rozpakowywanie jak krotka.__len__
,__getitem__
oraz__setiem__
metod wspierania dostaniem valus przez indeks, jakp[0]
. Przy tych ostatnich fragmentach wydaje się to najbardziej kompletną i poprawną odpowiedzią (w każdym razie dla mnie).__len__
i__iter__
są dobre.__getitem__
i__setitem__
naprawdę może być odwzorowany naself.__dict__.__setitem__
iself.__dict__.__getitem__
Krotki są z definicji niezmienne.
Możesz jednak utworzyć podklasę słownika, w której będziesz mieć dostęp do atrybutów za pomocą notacji kropkowej;
źródło
Jeśli chcesz zachować podobne zachowanie jak namedtuples, ale zmienne, spróbuj namedlist
Zauważ, że aby być mutowalnym, nie może to być krotka.
źródło
Pod warunkiem, że wydajność nie ma większego znaczenia, można użyć głupiego hacka, takiego jak:
źródło
z
, musiszmutable_z.z.pop(0)
wtedy zadzwonićmutable_z.z.append(new_value)
. Jeśli zrobisz to źle, otrzymasz więcej niż 1 element, a Twój program będzie zachowywał się nieoczekiwanie.mutable_z.z[0] = newValue
. Jak stwierdzono, rzeczywiście jest to hack.