Zadanie
Napisz funkcję tail pobierającą N parametrów z końca listy. Lista jest podana do funkcji jako parametr. Nagłówek naszej funkcji będzie wyglądał następująco.
def tail(n, lista): pass
Jak to ma działać
Funkcja tail na pewno jest znana użytkownikom Linuxa. Służy ona do wyświetlania N ostatnich linii z podanego jako parametr pliku. Nasza funkcja ma robić to samo dla listy.
Doprecyzujmy parę szczegółów funkcji tail:
- Przyjmuje parametr mówiący o tym ile ostatnich linii ma wyświetlić
- Jeśli plik ma tylko 2 linie, a chcemy wyświetlić np. 10, to tylko te 2 linie zostaną wyświetlone
- Dla pustego pliku nic nie zostanie wyświetlone
No to bierzmy się do roboty
Wyobraźmy sobie, że bierzemy właśnie udział w rekrutacji. Stres i pośpiech nie jest naszym sprzymierzeńczym. Napiszmy to w najłatwiejszy możliwy sposób, nie do końca zgodny ze stylem Pythona.
#TO JEST ZLE TAK NIE RÓBCIE def tail(n, lista): nowa_lista = [] # do tej listy przypiszemy pobrane wartości for x in range(len(lista)-n,len(lista)): # iterujemy się po odpowiedniej części listy nowa_lista.append(lista[x]) # dodajemy pobrane wartości do nowej listy return nowa_lista # zwracamy listę z pobranymi elementami LISTA = ['p','y','t','h','o','n'] print(tail(2, LISTA)) # wywołanie funkcji by pobrać dwa ostatnie elementy i wypisanie ich
Output:
['o', 'n']
No i super mamy działającą funkcję tail. Ale czy na pewno? Zanim przejdziemy do szczegółów wytłumaczmy jak to działa. Użyliśmy tutaj funkcji wbudowanej range. Generuje ona nam liczby z podanego przez parametry zakresu.
range(1,9) wygeneruje liczby od 1 do 8
Wszystko wydaje się ok. Zastanówmy się jednak co się stanie jak spróbujemy wywołać naszą funkcję dla np: 20 elementów.
#TO JEST ZLE TAK NIE RÓBCIE def tail(n, lista): nowa_lista = [] # do tej listy przypiszemy pobrane wartości for x in range(len(lista)-n,len(lista)): # iterujemy się po odpowiedniej części listy nowa_lista.append(lista[x]) # dodajemy pobrane wartości do nowej listy return nowa_lista # zwracamy listę z pobranymi elementami LISTA = ['p','y','t','h','o','n'] print(tail(20, LISTA)) # wywołanie funkcji by pobrać dwa ostatnie elementy i wypisanie ich
Zauważ, że nasza lista o błyskotliwej nazwie
LISTA = ['p','y','t','h','o','n']
ma tylko 6 elementów. Nasza funkcja tail powinna zatem wyświetlić całą zawartość listy.
Tymczasem zgłoszony zostanie wyjątek.
Exception has occurred: IndexError list index out of range
Dlaczego to nie działa
Funkcja range zwraca nam zakres, który potem używamy do iterowania się po liście. Problem w tym, że nasza lista jest krótsza niż obszar po, którym staramy się iterować. To skutkuje wyjątkiem mówiącym o przekroczeniu zakresu. Oczywiście można to naprawić dodając do naszego kodu całą masę sprawdzeń itp. Jednak spowodowało by to jeszcze większe skomplikowanie już mało przejrzystego kodu. Jak się pewnie spodziewacie da się to zrobić w ładniejszy sposób – zgodnie z duchem pythonic way.
Jak to powinno być zrobione
By pokazać naszym wyimaginowanym rekruterom, że znamy się na rzeczy zalecał bym skorzystanie z możliwości jakie daje nam Python, a mianowicie dzielnie list za pomocą zakresu. Oto parę przykładów:
>>> LISTA = ['p','y','t','h','o','n'] >>> LISTA[1:3] ['y', 't'] >>> LISTA[:3] ['p', 'y', 't'] >>> LISTA[3:] ['h', 'o', 'n'] >>> LISTA[:] ['p', 'y', 't', 'h', 'o', 'n'] >>> LISTA[-2:] ['o', 'n']
W ten z sposób z jednej listy tworzymy nową listę zawierającą interesujący nas wycinek. Składnia wygląda następująco
LISTA[indeks początkowy:indeks końcowy]
Posiadając tą wiedze napiszmy nasza funkcję tail jeszcze raz.
Funkcja tail z podziałem zakresu
LISTA = ['p','y','t','h','o','n'] def tail(n,lista): return lista[-n:] print(tail(2,LISTA))
Output:
['o', 'n']
Teraz nasz funkcja jest dużo ładniejsza i jest o wiele czytelniejsza. Jednak dalej mamy pewien problem, a nawet dwa.
Testowanie
Zastanówmy się dla jakich danych należało by przetestować naszą funkcję.
- Lista ma więcej elementów niż chcemy wyświetlić
- Lista ma mniej elementów niż chcemy wyświetlić
- Lista jest pusta
- Próba wyświetlania ujemnej ilości
- Próba wyświetlenia zerowej ilości elementów listy
Kompletne rozwiązanie
LISTA = ['p','y','t','h','o','n'] def tail(n,lista): return lista[-n:] if n > 0 else [] print(tail(2,LISTA))
Teraz już wszytko powinno działać. By spełnić wymagania należało dodać sprawdzanie czy ilość elementów jakie chcemy wyświetlić jest większa od zera. Dodatkowo zobaczcie jak ładne i przejrzyste jest nasze rozwiązanie. Dużo lepsze od naszego pierwszego podejścia. Pamiętaj, że wbrew pozorom kod częściej się czyta niż pisze i to czytelność kodu powinna być dla nas najważniejsza. Dla ciekawych zachęcam sprawdzić co zwróci LISTA[-0:]
.
A dlaczego w rozwiązaniu jest duże N ?
def tail(n,lista):
return lista[-N:]
Czy przypadkiem nie powinno być małe n ?
Tak znalazłaś bladą. Dzięki poprawię. Generalnie zasada jest taka, że zmienne piszemy z małej czyli zostawię wersję z małym n. Fajnie że mamy takich uważnych czytelników