Zanurkuj w Pythonie/Wyjątki i operacje na plikach - wszystko razem
Wszystko razem
[edytuj]Jeszcze raz ułożymy wszystkie puzzle domina w jednym miejscu. Już poznaliśmy, w jaki sposób działa każda linia kodu. Powrócimy do tego jeszcze raz i zobaczymy, jak to wszystko jest ze sobą dopasowane.
listDirectory
def listDirectory(directory, fileExtList): #(1)
u"zwraca listę obiektów zawierających metadane dla plików o podanych rozszerzeniach"
fileList = [os.path.normcase(f) for f in os.listdir(directory)]
fileList = [os.path.join(directory, f) for f in fileList
if os.path.splitext(f)[1] in fileExtList] #(2)
def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]): #(3)
u"zwraca klasę metadanych pliku na podstawie podanego rozszerzenia"
subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:] #(4)
return hasattr(module, subclass) and getattr(module, subclass) or FileInfo #(5)
return [getFileInfoClass(f)(f) for f in fileList] #(6)
listDirectory
jest główną atrakcją tego modułu. Przyjmuje ona na wejściu katalog (np. c:\music\_singles\) i listę interesujących nas rozszerzeń plików (jak np.['.mp3']
), a następnie zwraca listę instancji klas, które są podklasami słownika, a przechowują metadane na temat każdego interesującego nas pliku w tym katalogu. I to wszystko jest wykonywane za pomocą kilku prostych linii kodu.- Jak dowiedzieliśmy się w poprzednim podrozdziale, ta linia kodu zwraca listę pełnych ścieżek wszystkich plików z danego katalogu, które mają interesujące nas rozszerzenie (podane w argumencie
fileExtList
). - Starzy programiści Pascala znają zagnieżdżone funkcje (funkcje wewnątrz funkcji), ale większość ludzi jest zdziwionych, gdy mówi się im, że Python je wspiera. Zagnieżdżona funkcja
getFileInfoClass
może być wywołana tylko z funkcji, w której jest zadeklarowana, czyli zlistDirectory
. Jak w przypadku każdej innej funkcji, nie musimy przejmować się deklaracją interfejsu, ani niczym innym. Po prostu definiujemy funkcję i implementujamy ją. - Teraz, gdy już znamy moduł
os
, ta linia powinna nabrać sensu. Pobiera ona rozszerzenie pliku (os.path.splitext(filename)[1]
), przekształca je do dużych liter (.upper()
), odcina kropkę ([1:]
) i tworzy nazwę klasy używając łańcucha formatującego. c:\music\ap\mahadeva.mp3 zostaje przekształcone na .mp3, potem na .MP3, a następnie na MP3 i na końcu otrzymujemy MP3FileInfo. - Mając nazwę klasy obsługującej ten plik, sprawdzamy czy tak klasa istnieje w tym module. Jeśli tak, zwracamy tę klasę, jeśli nie -- klasę bazową
FileInfo
. To bardzo ważne: zwracamy klasę. Nie zwracamy obiektu, ale klasę samą w sobie. - Dla każdego pliku z listy
fileList
wywołujemygetFileInfoClass
z nazwą pliku(f)
. WywołaniegetFileInfoClass(f)
zwraca klasę. Dokładnie nie wiadomo jaką, ale to nam nie przeszkadza. Potem tworzymy obiekt tej klasy (jaka by ona nie była) i przekazujemy nazwę pliku (znówf
) do jej metody__init__
. Jak pamiętamy z wcześniejszych rozdziałów, metoda__init__
klasyFileInfo
ustawia wartośćself["name"]
, co powoduje wywołanie__setitem__
klasy pochodnej, czyliMP3FileInfo
, żeby odpowiednio przetworzyć plik i wyciągnąć jego metadane. Robimy to wszystko dla każdego interesującego pliku i zwracamy listę obiektów wynikowych.
Zauważmy, że metoda listDirectory jest bardzo ogólna. Nie wie w żaden sposób, z jakimi typami plików będzie pracować, ani jakie klasy są zdefiniowane do obsługi tych plików. Zaczyna pracę od przejrzenia katalogu, w poszukiwaniu plików do przetwarzania, a potem analizuje swój moduł, żeby sprawdzić, jakie klasy obsługi (np. MP3FileInfo
) są zdefiniowane. Możemy rozszerzyć ten program, żeby obsługiwać inne typy plików definiując klasy o odpowiednich nazwach: HTMLFileInfo
dla plików HTML, DOCFileInfo
dla plików Worda itp. listDirectory
, bez potrzeby modyfikacji kodu tej funkcji, obsłuży je wszystkie, zrzucając całe przetwarzanie na odpowiednie klasy i zbierając otrzymane wyniki.