Pamiętaj, że w Pythonie chcemy używać „pisania kaczego”. Tak więc wszystko, co działa jak lista, może być traktowane jako lista. Nie sprawdzaj więc typu listy, po prostu sprawdź, czy działa ona jak lista.
Ale łańcuchy znaków również działają jak lista i często nie tego chcemy. Są chwile, kiedy jest to nawet problem! Sprawdź więc wyraźnie, czy nie ma łańcucha, ale następnie użyj wpisywania kaczki.
Oto funkcja, którą napisałem dla zabawy. Jest to specjalna wersja, repr()
która drukuje dowolną sekwencję w nawiasach kątowych („<”, „>”).
def srepr(arg):
if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
return repr(arg)
try:
return '<' + ", ".join(srepr(x) for x in arg) + '>'
except TypeError: # catch when for loop fails
return repr(arg) # not a sequence so just return repr
Ogólnie rzecz biorąc, jest to czyste i eleganckie. Ale co isinstance()
tam robi ta kontrola? To rodzaj włamania. Ale to jest niezbędne.
Ta funkcja wywołuje się rekurencyjnie do wszystkiego, co działa jak lista. Gdybyśmy nie traktowali łańcucha specjalnie, wówczas byłby traktowany jak lista i dzieliłby po jednym znaku na raz. Ale wtedy wywołanie rekurencyjne spróbuje traktować każdą postać jak listę - i zadziała! Nawet ciąg jednoznakowy działa jako lista! Funkcja będzie się ciągle wywoływać rekurencyjnie, aż do przepełnienia stosu.
Funkcje takie jak ta, które zależą od każdego wywołania rekurencyjnego rozkładającego pracę do wykonania, wymagają ciągów znaków specjalnych - ponieważ nie można rozbić ciągu poniżej poziomu ciągu jednego znaku, a nawet jednego -znak znaków działa jak lista.
Uwaga: try
/ except
jest najczystszym sposobem wyrażenia naszych intencji. Ale jeśli ten kod byłby w jakiś sposób krytyczny czasowo, moglibyśmy chcieć zastąpić go jakimś testem, aby sprawdzić, czy arg
jest to sekwencja. Zamiast testować typ, powinniśmy prawdopodobnie przetestować zachowania. Jeśli ma .strip()
metodę, jest to ciąg znaków, więc nie traktuj jej jako sekwencji; w przeciwnym razie, jeśli jest indeksowalny lub iterowalny, jest to sekwencja:
def is_sequence(arg):
return (not hasattr(arg, "strip") and
hasattr(arg, "__getitem__") or
hasattr(arg, "__iter__"))
def srepr(arg):
if is_sequence(arg):
return '<' + ", ".join(srepr(x) for x in arg) + '>'
return repr(arg)
EDYCJA: Pierwotnie napisałem powyższe z zaznaczeniem, __getslice__()
ale zauważyłem, że w collections
dokumentacji modułu ciekawą metodą jest __getitem__()
; ma to sens, tak indeksujesz obiekt. Wydaje się to bardziej fundamentalne, __getslice__()
więc zmieniłem powyższe.