Zadanie 1.3 omówienie rozwiązania - WykopWyzwanie/wykop_wyzwaniepython GitHub Wiki
##Zadanie 1.3
Link do rozwiązania
Do rozwiązania potrzebne będą 4 biblioteki - os
, shutil
, datetime
oraz glob
.
import os
import shutil
from datetime import datetime
from glob import glob
from os.path import abspath, getctime, getmtime, getsize, isdir, isfile
-- Następnie tworzona jest klasa CmdHandlers, ale nie należy dać się zmylić - ten program nie ma nic wspólnego z paradygmatem obiektowym. Klasa jest tu używana jako namespace - w praktyce lepiej gdyby jej zawartość była po prostu umieszczona w osobnym module.
class CmdHandlers():
-- Przyjętą konwencją jest nazywanie metody obsługującej daną komendę nazwą obsługiwanej komendy. Metoda obsługująca komendę przyjmuje takie same argumenty jak obsługiwana komenda według specyfikacji. Jak widać 6 z 8 handlerów to funkcje jednolinijkowe używające odpowiednich elementów bibliotek.
class CmdHandlers():
def pwd():
return os.getcwd()
def cd(path):
os.chdir(path)
def mv(src, dst):
shutil.move(src, dst)
def ls():
return '\n'.join(os.listdir(os.getcwd()))
def touch(path):
with open(path, 'w+'):
pass
def cp(src, dst):
shutil.copy2(src, dst) if isfile(src) else shutil.copytree(src, dst)
def rm(path):
os.remove(path) if isfile(path) else shutil.rmtree(path)
--
Trochę ciekawsza jest tylko obsługa komendy info
, ponieważ informacje wypisywane dla folderów różnią się od informacji wypisywanych dla plików.
Wartości odpowiednich pól (a także informacja czy to pole ma być wyświetlane) są generowane dynamicznie.
def info(path):
filesizes = CmdHandlers._lst_file_sizes(path) if isdir(path) else [getsize(path)]
return '\n'.join('{}: {}'.format(name, value) for (name, value) in [
('typ', 'plik' if isfile(path) else 'katalog' if isdir(path) else 'inny'),
('sciezka', abspath(path)),
('rozmiar', '{}B'.format(sum(filesizes))),
('liczba_plikow', sum(isfile(f) for f in os.listdir()) if isdir(path) else None),
('ctime', datetime.fromtimestamp(getctime(path)).date()),
('mtime', datetime.fromtimestamp(getmtime(path)).date()),
] if value is not None)
def _lst_file_sizes(path):
return list(getsize(f) for f in glob("{}/**".format(path), recursive=True) if isfile(f))
--
Funkcja parse(cmd, *args, **kwargs)
działa w bardzo prosty sposób.
Próbuje ona wyciągnąć funkcję obsługującą daną komendę z namespace CmdHandlers, a jeśli się to nie uda to funkcją obsługującą jest pusta lambda, czyli w praktyce komenda zostaje pominięta.
Po wyciągnięciu odpowiedniej funkcji obsługującej jest ona wywoływana z argumentami przekazanymi jako *args
i **kwargs
.
def parse(cmd, *args, **kwargs):
return CmdHandlers.__dict__.get(cmd, lambda *a, **kw: None)(*args, **kwargs)
--
Funkcja main
odpowiada za interaktywną obsługę wpisywanych komend. To co wpisze użytkownik jest dzielone na komendę i jej argumenty, a następnie używając tych wartości wywoływana jest funkcja parse
. Jeżeli parse
zwróci jakiś napis jest on wypisywany na konsolę, w przeciwnym wypadku wypisywana jest pusta linia oznaczająca zakończenie wykonywania polecenia. Aby opuścić tryb interaktywny należy wpisać komendę exit
.
def main():
command, args = '', []
while command != 'exit':
command, *args = input().split()
print(parse(command, *args) or '')
if __name__ == '__main__':
main()