Python

Python, il cui nome deriva da un famoso programma della BBC "Monty Python's Flying Circus'', è la creazione di Guido van Rossum dello Stichting Mathematisch Centrum (Olanda), come evoluzione di un linguaggio chiamato ABC, agli inizi del 1990. Van Rossum ne è il principale autore, ma il progetto si avvale ora di numerosi contributori.

Python è un linguaggio di programmazione facile da imparare e, poiché è un linguaggio interpretato, è semplice e piacevole da utilizzare. E' dotato di un'efficiente struttura di dati di alto livello ed un efficace approccio alla programmazione object-oriented. È ideale come linguaggio di scripting o per lo sviluppo rapido di applicazioni. Python è disponibile in molte piattaforme.

L'interprete di Python è estendibile tramite funzioni o tipi di dato scritti in C o C++; si presta anche ad essere integrato in applicazioni per fornirle di programmabilità.

E' corredato di diverse librerie, fra cui librerie per le funzioni di I/O, per il WEB, per la crittografia, per l'elaborazione parallela e per interfacce grafiche. Di queste ve ne sono parecchie, quelle "più ufficiali" sono: Tkinter, un'interfaccia al toolkit Tk GUI, Tix un'estensione di Tk e turtle che implementa una tartaruga tipo LOGO, utilizzando Tk (Tk è stato scritto con il linguaggio TCL).

I programmi, grazie alla tipizzazione dinamica dei dati, all'utilizzo dell'indentazione per la costruzione dei blocchi di istruzioni e alla non necessità di dichiarare le variabili, sono compatti e leggibili.

L'interattività di Python ne fa una calcolatrice efficace (>>> è il prompt di Python):

>>> altezza = 10.5

>>> larghezza = 20

>>> peso = 20

>>> 'peso specifico', larghezza*altezza/peso

('peso specifico', 10.5)

Fra i tipi di dato si hanno:

§         Oggetti. Dopo averli dichiarati vanno istanziati. Nell'esempio che segue possiamo pensare l'oggetto Coord come un punto generico nel piano, mentre w e z sono due punti particolari. La funzione dist utilizza gli oggetti Coord per calcolare la distanza di due punti nel piano.

>>> class Coord:

     def __init__(self, ordinata, ascissa):

         self.x = ordinata

         self.y = ascissa

>>> w=Coord(0,0)

>>> z=Coord(3,4)

>>> def dist(p1,p2):

    return pow(pow((p1.x-p2.x),2)+pow((p1.y-p2.y),2),0.5)

>>> dist(w,z)

5.0

§         Numeri immaginari:

>>> imgr=(1.5 + 0.7J) + complex(2,3.3)

>>> imgr,imgr.real,imgr.imag

((3.5+4j), 3.5, 4.0)

§         Tuple: insiemi di dati separati da virgola, ed eventualmente racchiusi fra parentesi tonde, imgr, imgr.real, imgr.imag del punto precedente è una tupla. Le tuple, come del resto le stringhe di caratteri, non sono direttamente modificabili.

§         Liste o sequenze, con un'insieme di metodi e funzioni che le rendono maneggevoli ed atte a realizzare code o stack:

>>> coda = ["Carla", "Enrico", "Michele"]

>>> coda.append("Teresa")

>>> coda.pop(0)

'Carla'

>>> coda

['Enrico', 'Michele', 'Teresa']

La funzione range(from, to, step) genera una lista di numeri.

§         Matrici: insiemi omogenei di valori. Sono implementate come oggetti:

>>> import array      # libreria che implementa gli array

>>> mat = array.array('c','array di caratteri')

>>> mat

array('c', 'array di caratteri')

§         Dizionari: ogni elemento è individuato da una chiave, che è il mezzo per accedere al suo valore. Il valore può essere un qualsiasi dato o oggetto di Phyton:

>>> diz = {'imgr': imgr, 'coda': coda,'matrice': mat}

>>> diz['tupla']= 3.1415,'pigreco mezzi' # aggiungo una voce

>>> diz['matrice']

array('c', 'array di caratteri')

Esistono funzioni su funzioni: filter(), map(), e reduce(); sono molto efficaci quando utilizzate con liste: filter(funzione, sequenza) genera una sequenza con i soli elementi di sequenza per i quali la funzione (unaria) funzione(sequenza[indice]) è vera.

map(funzione, sequenza) genera una sequenza con i valori di funzione(sequenza[indice]). Ad esempio:

>>> def media(x,y): return(x+y)/2

>>> seq = range(8)

>>> seq

[0, 1, 2, 3, 4, 5, 6, 7]

>>> map(media,seq,range(8,24,2))

[4, 5, 7, 8, 10, 11, 13, 14]

reduce(funzione, sequenza) calcola la funzione (binaria) funzione, applicata ai primi due elementi della sequenza, successivamente applica funzione al risultato ed al terzo elemento, e così via fino ad esaurire la sequenza.

Nell’esempio che segue Python è utilizzato per simulare un servente di disco che cerca di minimizzare gli spostamenti della testina magnetica. Le richieste sono generate da task concorrenti. Una visualizzazione dei risultati è ottenuta con Liberty Basic (v. par. 2.7).

# Python 2.2b2 (#26, Nov 16 2001, 11:44:11) [MSC 32 bit (Intel)] on win32

#

# Simulazione di un servente di disco.

#  Le richieste sono accodate in una coda FIFO, tuttavia se nel tragitto dalla traccia

#   attuale a quella voluta, c'è una richiesta, questa è soddisfatta

# --- accesso alle funzioni utilizzate

from time import sleep, gmtime, strftime, clock

from threading import * # concorrenzialità

from random import random,choice

req = [] # coda delle richieste di servizio contiene un progressivo usato

         # come chiave del dizionario d_req i cui elementi sono una lista

         # con le caratteristiche del servizio richiesto

n_req = 0    # progressivo richieste

d_req ={}    # db delle richieste

n_task = 10  # numero task clienti

sema_req = BoundedSemaphore()  # semaforo per aggiornare l'array delle richieste

sema_log = BoundedSemaphore()  # semaforo per serializzare il log

StatoServente = Event()        # oggetto Evento per controllare il servente

# genero gli eventi associati ai task per segnalare la fine del servizio

for i in range(1,n_task+1):

  exec ('Cliente' + str(i) + ' = Event()')

fl = open('log.txt','w')

clock_in = clock()             # prelevo inizio clock

def logga(*args):

  sema_log.acquire()

  fl.write('%6.2f' % (clock() - clock_in))

  for x in args:

    fl.write(' ' + x)

    print ' ',x,

  fl.write('\n')

  print

  sema_log.release()

def simula():

  logga('----- Inizio simulazione -----')

  global n_req, req, d_req

  tServer = Thread(group=None,target=servente,name='Servente')

  tServer.start()

  tServer.run

  for i in range(1,n_task+1):

    nome_task = 't' + str(i)

    args = str(1.0*random()) + ',\'Cliente' + str(i) + '\',' + str(choice(range(5,15)))

    exec(nome_task + ' = Thread(target=task,args=(' + args + '))')

    exec(nome_task + '.start()')

    exec(nome_task + '.run')

  while activeCount() > 2: pass # attendo fine dei threads

  StatoServente.set()         # Servente inattivo

def task(tempo=1,cliente='anonimo',volte = 3):

# tempo:   intervallo medio di richiesta servizio

# cliente: nome del richiedente

# volte:   numero richieste

  logga('- Partenza di:', cliente,'-')

  logga('  intervallo medio richieste: %6.3f  Numero richieste: %3d' % (tempo,volte))

  for i in range(0,volte):

    trk_req = choice(range(1,1001))

    blk_req = choice(range(1,6))

    richiesta_servizio(cliente,trk_req,blk_req)

    init_time = clock()

    exec(cliente + '.clear()')

    exec(cliente + '.wait()')  

    logga('',cliente,' traccia:',str(trk_req),' t. risp.',('%5.3f' % (clock()-init_time)))

    sleep(2.0 * tempo * random())

  logga('Fine di:', cliente)

def richiesta_servizio(cliente,traccia,durata):

  global n_req, req, d_req

  # accodo la richiesta e creo voce dizionario

  sema_req.acquire()

  n_req = n_req + 1

  req.append(n_req)

  d_req[n_req] = [cliente,traccia, durata]

  sema_req.release()

def servente():

  global req, d_req

  logga('--- Partenza Servente ---')

  s_req = []           # lista serviti

  trk = 0              # 0 = posizione iniziale

  t_trk = 0.002        # tempo per spostarsi di 1 traccia

  t_blk = 0.0005       # tempo di lettura settore

  while not StatoServente.isSet():

    if len(req) > 0:

      sema_req.acquire() # blocco l'aggiornamento delle richieste

      logga('Coda in:',repr(req))

      trk_serv = d_req[req[0]][1]  # traccia richiesta

      indx = 0                     # indice della richiesta

      for i in range(1,len(req)):

        if trk_serv > trk:

          if d_req[req[i]][1] > trk and d_req[req[i]][1] < trk_serv:

            indx = i

            break                  # esce al primo trovato

        else:

          if d_req[req[i]][1] > trk_serv and d_req[req[i]][1] < trk:

            indx = i

            break                  # esce al primo trovato

      trk_serv = d_req[req[indx]][1]  # traccia richiesta

      cliente = d_req[req[indx]][0]   # cliente

      n_blk = d_req[req[indx]][2]     # blocchi da leggere

      t_serv = abs(trk-trk_serv)*t_trk + n_blk * t_blk

      s_req.append(trk_serv)

      del req[indx]                   # elimino richiesta servita

      logga('Coda out:',repr(req))

      sema_req.release() # rilascio aggiornamento richieste

      sleep(t_serv)      # simulo tempo di spostamento + lettura

      trk = trk_serv

      exec(cliente + '.set()') # fine servizio

  o_req = []           # richieste originarie

  for i in range(0,len(s_req)):

    o_req.append(d_req[i+1][1])

    logga('trk chiesta',str(o_req[i]))

    logga('trk servita',str(s_req[i]))

  t_teorico = t_reale = 0  # assegnazione multipla

  for i in range(1,len(o_req)):

    t_teorico = t_teorico + abs(o_req[i] - o_req[i-1]) * t_trk

    t_reale = t_reale + abs(s_req[i] - s_req[i-1]) * t_trk

  logga('Tempo teorico:', str(t_teorico))

  logga('Tempo reale:  ', str(t_reale))

  logga('--- Fine Servente ---')

  fl.close()

simula()