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.

Przykład. 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)
  1. 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.
  2. 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).
  3. 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 z listDirectory. Jak w przypadku każdej innej funkcji, nie musimy przejmować się deklaracją interfejsu, ani niczym innym. Po prostu definiujemy funkcję i implementujamy ją.
  4. 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.
  5. 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.
  6. Dla każdego pliku z listy fileList wywołujemy getFileInfoClass z nazwą pliku (f). Wywołanie getFileInfoClass(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ów f) do jej metody __init__. Jak pamiętamy z wcześniejszych rozdziałów, metoda __init__ klasy FileInfo ustawia wartość self["name"], co powoduje wywołanie __setitem__ klasy pochodnej, czyli MP3FileInfo, ż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.