Jakie są nazwane krotki?
Krotka o nazwie to krotka.
Robi wszystko, co może krotka.
Ale to coś więcej niż krotka.
Jest to specyficzna podklasa krotki, która jest programowo tworzona zgodnie ze specyfikacją, z nazwanymi polami i stałą długością.
To, na przykład, tworzy podklasę krotki, a poza tym, że ma stałą długość (w tym przypadku trzy), można jej używać wszędzie tam, gdzie krotka jest używana bez zerwania. Jest to znane jako substytucyjność Liskowa.
Nowość w Pythonie 3.6 , możemy użyć definicji klasytyping.NamedTuple
do utworzenia parametru o nazwie:
from typing import NamedTuple
class ANamedTuple(NamedTuple):
"""a docstring"""
foo: int
bar: str
baz: list
Powyższe jest takie samo, jak poniżej, z tym że powyższe dodatkowo zawiera adnotacje typu i dokumentację. Poniższe jest dostępne w Python 2+:
>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)
To tworzy instancję:
>>> ant = ANamedTuple(1, 'bar', [])
Możemy to sprawdzić i wykorzystać jego atrybuty:
>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']
Głębsze wyjaśnienie
Aby zrozumieć nazwane krotki, najpierw musisz wiedzieć, czym jest krotka. Krotka jest zasadniczo niezmienną (nie można jej zmienić w pamięci) listą.
Oto jak możesz użyć zwykłej krotki:
>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'
Możesz rozwinąć krotkę za pomocą iterowalnego rozpakowywania:
>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
Krotki nazwane to krotki, które umożliwiają dostęp do ich elementów według nazwy zamiast po prostu indeksu!
Tworzysz nazwę o nazwie:
>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])
Możesz także użyć pojedynczego ciągu z nazwami oddzielonymi spacjami, nieco bardziej czytelne użycie interfejsu API:
>>> Student = namedtuple('Student', 'first last grade')
Jak z nich korzystać?
Możesz zrobić wszystko, co mogą zrobić krotki (patrz wyżej), a także wykonać następujące czynności:
>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')
Komentator zapytał:
Gdzie w dużym skrypcie lub programie definiuje się nazwaną krotkę?
Typy, które tworzysz, namedtuple
są w zasadzie klasami, które możesz tworzyć za pomocą prostego skrótu. Traktuj je jak zajęcia. Zdefiniuj je na poziomie modułu, aby marynarka i inni użytkownicy mogli je znaleźć.
Przykład roboczy na poziomie globalnego modułu:
>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')
A to pokazuje brak wyszukiwania definicji:
>>> def foo():
... LocalNT = namedtuple('LocalNT', 'foo bar')
... return LocalNT('foo', 'bar')
...
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed
Dlaczego / kiedy powinienem używać krotek nazwanych zamiast krotek normalnych?
Używaj ich, gdy poprawia kod, aby semantyka elementów krotkowych była wyrażona w kodzie.
Możesz użyć ich zamiast obiektu, jeśli w innym przypadku używałbyś obiektu z niezmiennymi atrybutami danych i bez funkcji.
Możesz także podzielić je na klasy, aby dodać funkcjonalność, na przykład :
class Point(namedtuple('Point', 'x y')):
"""adding functionality to a named tuple"""
__slots__ = ()
@property
def hypot(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
Dlaczego / kiedy powinienem używać normalnych krotek zamiast krotek nazwanych?
Prawdopodobnie regresją byłoby przejście od używania krotek nazwanych do krotek. Początkowa decyzja projektowa skupia się na tym, czy koszt dodatkowego kodu jest wart poprawionej czytelności, gdy używana jest krotka.
Nie ma dodatkowej pamięci używanej przez krotki nazwane w porównaniu krotek.
Czy istnieje jakaś „lista nazwanych” (zmienna wersja nazwanej krotki)?
Szukasz obiektu z wycięciem, który implementuje wszystkie funkcje listy o statycznym rozmiarze lub listy o podklasach, która działa jak nazwana krotka (i która w jakiś sposób blokuje zmianę wielkości listy).
Rozbudowany, a może nawet zastępujący Liskov, przykład pierwszego:
from collections import Sequence
class MutableTuple(Sequence):
"""Abstract Base Class for objects that work like mutable
namedtuples. Subclass and define your named fields with
__slots__ and away you go.
"""
__slots__ = ()
def __init__(self, *args):
for slot, arg in zip(self.__slots__, args):
setattr(self, slot, arg)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
# more direct __iter__ than Sequence's
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
# Sequence requires __getitem__ & __len__:
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
Aby użyć, wystarczy podklasę i zdefiniować __slots__
:
class Student(MutableTuple):
__slots__ = 'first', 'last', 'grade' # customize
>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A