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