Word Counter in Python
Transcript
Word Counter in Python
Report sul progetto realizzato Andrea Sotgiu, Samuele Deriu, Simone Setzu Programma WordCounter in Python Il progetto realizzato riguardava la realizzazione di un programma in Python. Che cos'è il Python? “Python è un linguaggio di programmazione orientato agli oggetti. Venne ideato da Guido Van Rossum intorno agli anni novanta ed oggi è nella Top 10 dei linguaggi di programmazione più usati.” Il Python è molto usato nell'ambito della ricerca: • Nel progetto di microscopia digitale, Python è una delle tecnologie alla base di Omero e viene utilizzato anche come linguaggio per l'elaborazione di immagini; • Nel progetto di telemedicina, Pyhton costituisce il 90% della tecnologia utilizzata in fase di sviluppo. A scuola non studiamo il linguaggio Python, per cui abbiamo potuto apprendere qualcosa di completamente nuovo. Come applicazione pratica a fini didattici abbiamo sviluppato un piccolo software che, dato un file come parametro simula il comando "word counter " di Linux: ○ restituisce il numero di linee del file di testo; ○ restituisce il numero di parole contenute nel testo (anche il numero di volte che si ripetono); ○ restituisce il numero dei caratteri presenti nel testo. Il codice è stato un personalizzato con l'aggiunta di logger, che memorizzano tutte le operazione del programma quando è in esecuzione. Con l’utilizzo del parser abbiamo creato una micro guida sulla sintassi da usare. Questa è la prima versione del Word Counter realizzato senza l’utilizzo della classe con metodi. import sys FILE_NAME = 'star_wars.txt' // file di testo non dinamico // tutti i nomi che iniziano con def indicano delle // funzioni o delle procedure def read_text(fname): f = open(fname) return f.readlines() 1 //conteggio linee def count_lines(text): return len(text) //conteggio caratteri def count_chars(text): if type(text) == list: ct = 0 for line in text: ct += len(line) return ct elif type(text) == str: return len(text) else: raise ValueError('Type %r not supported' % type(text)) // conteggio parole def count_words(text): if type(text) == str: words = get_words(text) #print words return len(words) else: raise ValueError('text is not a string') //controllo se la parola esiste già def word_count(text): words = get_words(text) count = {} for x in words: if count.has_key(x): count[x]+=1 else: count[x]=1 return count // concatena le stringhe def concat(text, separator=''): full_text = separator.join(text) return full_text.replace('\n', ' ').strip() // restituisce le parole separate da un dato carattere def get_words(text, separator=' '): 2 return text.split(separator) // programma principale def main(argv): print 'Starting program with file %s' % FILE_NAME text = read_text(FILE_NAME) line_counter = count_lines(text) print 'Number of lines: %d' % line_counter print 'Counting characters' chars_from_list = count_chars(text) text = concat(text) chars_from_string = count_chars(text) print 'Charachters are %d (from list) --- %d (from string)' % (chars_from_list,chars_from_string) words = count_words(text) print 'Words are %d' % words print '' count = word_count(text) for k,v in count.iteritems(): print'%s=>%d' % (k,v) if __name__ == '__main__': main(sys.argv[1:]) Il codice è commentato per rendere l’idea di cosa fa ogni porzione del codice. Nelle funzioni ci sono vari controlli che effettuano delle operazioni ben precise e restituiscono un valore. Non è per niente banale scrivere un codice di questo tipo, oltre la conoscenza del linguaggio che stiamo utilizzando, ci vogliono delle buone conoscenze di base di programmazione per implementare algoritmi efficienti che non presentino problemi nell’esecuzione del codice; ma grazie alle conoscenze assimilate con gli anni siamo riusciti a completare il codice con successo. Dopo la prima versione abbiamo implementato il codice con l’uso delle classi, utilizzando quindi la programmazione ad oggetti. Abbiamo quindi reso il programma dinamico facendogli accettare parametri dall'utente (modulo argparse) e facilmente debuggabile tramite l'introduzione di un log (modulo logger) Abbiamo infine racchiuso il nostro modulo word_counter all'interno di un package. Il programma finale è costituito da tre file. Il file principale è quello che esegue il programma vero e proprio (inizializza i logger ed esegue il parsing della riga di comando), ed è stato chiamato "run_word_counter.py". Il contenuto del file è il seguente: import sys, logging from argparse import ArgumentParser from summer_school.word_counter import WordCounter def get_parser(): 3 parser = ArgumentParser(description = 'Simulate the wc Linux command') parser.add_argument('-F', '--file', type = str, help = 'Input file for the WordCounter object', required = True) parser.add_argument('-L', '--logfile', type=str, help = 'Logging file', default='word_counter.log') return parser def main(argv): LOG_FORMAT = '%(asctime)s|%(levelname)-8s|%(message)s' LOG_DATEFMT = '%Y-%m-%d %H:%M:%S' parser = get_parser() args = parser.parse_args(argv) logging.basicConfig(format = LOG_FORMAT, datefmt = LOG_DATEFMT, level = logging.INFO, filename = args.logfile) logger = logging.getLogger('word_counter_main') in_file = args.file logger.info('Starting program with file %s' % in_file) try: counter = WordCounter(in_file, logger) except IOError: logger.critical('File %s does not exist' % in_file) sys.exit('File %s does not exist' % in_file) logger.info('##### Counting lines #####') line_counter = counter.count_lines() logger.info('Number of lines: %d' % line_counter) logger.info('##### Counting characters #####') chars_from_list = counter.count_chars() logger.info('Charachters are %d' % chars_from_list) logger.info('##### Counting words #####') words = counter.count_words() logger.info('Words are %d' % words) logger.info('###### Counting single words #####') words = counter.word_count() for k,v in words.iteritems(): logger.info('%s ---> %d' % (k,v)) if __name__ == '__main__': main(sys.argv[1:]) 4 Gli altri due file fanno parte del package realizzato. Per identificare un package, questo viene racchiuso in una directory, che abbiamo chiamato "summer_school". All'interno di questa directory viene inserito il file "__init__.py", che può essere lasciato vuoto: è proprio la presenza di questo file che indica al Python che il contenuto della directory corrisponde ad un package. L'altro file, chiamato "word_counter.py" è la versione ad oggetti del word counter già scritto in precedenza. Il contenuto di questo file è il seguente: from collections import Counter class WordCounter(object): def __init__(self, text_file_name, logger = None): self.logger = logger self.text_file_name = text_file_name self.text = self.__read_text(self.text_file_name) if len(self.text) == 0: raise ValueError('Text length is zero, cannot work') def __read_text(self, fname): # with chiama implicitamente f.close() al termine del ciclo with open(fname) as f: lines = f.readlines() return lines def count_lines(self): return len(self.text) def count_chars(self): if type(self.text) == list: ct = 0 for line in self.text: ct += len(line) return ct else: logger.critical('Type %r not supported' % type(self.text)) raise ValueError('Type %r not supported' % type(self.text)) def count_words(self): words = self.__get_words() return len(words) def word_count(self): words = self.__get_words() counter = Counter() for w in words: counter[w] += 1 return counter 5 def __concat(self, separator=''): full_text = separator.join(self.text) return full_text.replace('\n', ' ').strip() def __get_words(self, separator=' '): text = self.__concat() return text.split(separator) Si noti il modo di importare una classe di un modulo usato dal Python: nel file "run_word_counter.py" è presente la riga: from summer_school.word_counter import WordCounter Questa istruzione indica al Python di caricare il modulo "word_counter", presente nel package "summer_school", e che di questo modulo si vuole importare la classe WordCounter. Per eseguire il programma è sufficiente mandare in esecuzione il file "run_word_counter.py" seguito dai parametri indicati nella breve guida mostrata a video dal Parser, come è visibile nelle righe seguenti (sempre appartenenti al file "run_word_counter.py"): parser = ArgumentParser(description = 'Simulate the wc Linux command') parser.add_argument('-F', '--file', type = str, help = 'Input file for the WordCounter object', required = True) parser.add_argument('-L', '--logfile', type=str, help = 'Logging file', default='word_counter.log') 6