Questo è il primo post di una serie sul libro IronPython In Action (
qui il preambolo).
Cercherò di essere organico anche se probabilmente finirà con l’essere un braindump di considerazioni mie e informazioni tratte dal libro.
Introduzione a IronPython
IronPython è un cittadino a tutti gli effetti del magico mondo di .NET. Questo per sfatare ogni mito su toy language o che.
IronPython (d’ora in poi IP) è supportato da VisualStudio, ASP.NET, Silverlight, XNA, Microsoft Robotics Kit, Volta e quant’altro.
Python è un linguaggio multipurpose e supporta vari paradigmi di programmazione: procedurale, funzionale, object-oriented, meta programmazione, ecc. ecc. Riporto alcune parti dello zen di python per farne capire la sua natura:
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Readability counts.
practicality beats purity.
Errors should never pass silently.
There should be one-- and preferably only one --obvious way to do it.
If the implementation is hard to explain, it's a bad idea.
Si capisce quanto il concetto di semplicità (anche nelle applicazioni complesse) sia tenuto in assoluto riguardo.
IP è un progetto open source portato avanti da Microsoft con licenza approvata dall’OSI. Questo significa che è possibile accedere a tutto il codice e persino venderne lavori derivati. È scritto in C# e Python e usa un engine per compilare i sorgenti Python in assembly .NET. Ne esistono due versioni ora come ora: la 1 e la 2.0. La 2.0 ha come target Python 2.5 (rilasciato il 19 Settembre 2006), la 1 ha come target Python 2.5 (rilasciato il 30 Novembre 2004). Attualmente l’implementazione ufficiale di Python è distribuita in varie versioni, le cui più recenti sono: la 2.6 (1 Ottobre 2008) considerata stabile e la nuova 3.0 (3 Dicembre 2008) non considerata production ready.
Notoriamente Python ha sempre cercato di essere il più possibile compatibile, la 3.0 invece da un taglio (non esagerato) con il passato.
IP è solo una dele tante implementazioni di Python, altre sono: CPython (quella ufficiale), Jython (sulla JVM), PyPy, Stackless Python, tinypy, ecc. ecc.
IP è integrato in VisualStudio 2005, e integrabile nella 2008 attraverso IronPython Studio. IP 1 gira anche su Mono, la 2.0 non sono riuscito a farla funzionare. Il libro comunque si basa principalmente sui tool Microsoft, quindi no problem.
IP è il parto del geniale Jim Hugunin (creatore anche di Jython) che qualche tempo fa decise di sfidare il mondo tentando di dimostrare che la CLR non fosse ostile ai linguaggi dinamici. Microsoft lo invitò per presentare le sue scoperte e finì con l’assumerlo per permettergli di lavorare su IronPython a tempo pieno, mantenendolo open source.
L’intera comunità Python fu coinvolta all’inizio del progetto sia per assicurare la non divergenza dalla implementazione ufficiale che per fixare bug. Nel settembre 2006 la versione 1.0 fu rilasciata, seguita dalla 2.0 alpha nel 2007.
Le particolarità della presentazione della 2.0 sono: il DLR, un runtime separato specifico per i linguaggi dinamici, nato in seno al lavoro del team di IronPython e il fatto che è possibile usarlo all’interno dell’ambiente in sandbox di Silverlight.
IP è sicuramente rilevante per due tipi di sviluppatori: coloro che sviluppano abitualmente su piattaforme Python e coloro che invece usano .NET giorno per giorno.
IronPython per sviluppatori Python
La domanda da un milione di dollari è: perché un programmatore Python dovrebbe essere interessato a valutare IronPython (oltre alla sana curiosità) ?
- Non è presente il GIL nelle applicazioni multithreaded
- IP è più facilmente estensibile tramite C# che Python tramite C (niente API C o reference counting)
- Gli AppDomain di .NET permettono di ridurre i privilegi delle applicazioni
- È possibile usare le Windows Forms e WPF per costruire GUI su Windows
- Si ha accesso all’enorme numero di librerie di .NET
Ovviamente ci sono degli svantaggi, in particolare non è possibile usare la moltitudine di estensioni C sviluppate per Python anche se in alcuni casi esistono wrapper alternativi in IronPython.
IronPython per sviluppatori .NET
Veniamo al target forse più importante tra i due:
- È molto più facile usare la programmazione funzionale (e tutti gli altri paradigmi) ed è molto più facile creare tipi e oggetti a runtime
- Si hanno a disposizione closure, duck typing (adapter pattern dice qualcosa?) e la metaprogrammazione gratis.
- Non c’è la fase di compilazione prima di poter provare ciò che si è appena scritto, quindi le fasi di sviluppo e testing sono più rapide
- Python è molto usato nell’industria e nel mondo accademico (esiste da 17 anni)
- È semanticamente molto ricco: generator expression, valori di ritorno multipli, tuple, decoratori, insiemi, metaclassi e altro permettono di esprimere molti concetti facilmente
- L’inteprete interattivo per provare pezzi di codice al volo è uno strumento insostituibile durante la fase di sviluppo. Permette di avere nella shell tutta la potenza del linguaggio, della sua interpretazione e la sua documentazione
- È possibile infilare IP come linguaggio embedded in applicazioni realizzate con linguaggi diversi
Tra i problemi c’è da segnalare che non è possibile usare attributi direttamente, ma si può ovviare con classi stub in C#.
Python sulla CLR
Come ben sapete il Common Language Runtime è il cuore pulsante di .NET, il Dynamic Language Runtime invece è il risultato del lavoro di Hugunin, del team di IronPython e di IronRuby.
Attualmente sulla DLR girano IronPython, IronRuby, VBx, Managed JScript for Silverlight, ToyScript and IronScheme.
Dinamico significa principalmente che l’implementazione risolve i tipi a runtime (late binding), che non vi è una dichiarazione esplicita di tipi, che è possibile cambiare il tipo di una variabile durante l’esecuzione del programma. Ciò permette anche di esaminare e modificare oggetti a runtime e rende molto più semplice l’introspezione e la reflection.
In Python sono gli oggetti (non le variabili, che qui formalmente sono etichette) ad avere un tipo. Python è fortemente tipizzato, ossia per farla breve non permette di sommare numeri e stringhe senza cast espliciti come è possibile fare in JavaScript e PHP. In Python le operazioni fattibili su un tipo sono solo quelle che il tipo permette di fare (cast compresi).
Come ho detto le variabili sono etichette di oggetti presenti in memoria quindi è possibile riassegnarle ad oggetti diversi con tipi diversi. Grazie alla dinamicità del linguaggio i container naturalmente possono contenere oggetti di tipi diversi senza ricorrere a trucchi, specializzazioni o generics. In molti casi il solo duck typing è sufficiente.
La natura dinamica del linguaggio rendere ancora più importanti i test perché gli errori di tipo sono visibili soltanto a runtime.
IronPython in Silverlight
Silverlight è un plugin cross platform e cross browser per animazioni, streaming video e RIA. All’interno della versione 2 gira un sottoinsieme della CLR chiamato Core CLR per eseguire applicazioni in ambiente sandbox all’interno del browser. Essendovi integrato il DLR questo permette anche a IronPython di poter girare client side sul browser dell’utente.
Python
Dopo la suddetta filippica cerchiamo di concentrarci su ciò che fondamentalmente è IronPython: Python.
Python gira su Windows, Linux, Solaris, Mac OSX, FreeBSD, AIX, Windows Mobile, Symbian S60, OSX per IPhone, Nintendo DS e sicuramente altre piattaforme che ora non mi vengono in mente…
È un linguaggio completamente opensource con una licenza BSD-like e la sua implementazione ufficiale è curata dalla Python Software Foundation. È stato creato nel 1990 da Guido Van Rossum (ora dipendente Google) e ovviamente il suo nome deriva dai Monty Python.
L’implementazione ufficiale è comunemente nota come CPython (scritta in C e Python) e contiene una enorme collezione di librerie e un runtime relativamente piccolo (3-5 megabyte).
Una parte del core team di Python ora lavora per Google che lo annovera come uno dei tre linguaggi più diffuso all’interno delle sue applicazioni private e pubbliche (insieme a C++ e Java). È molto ben recepito anche nell’ambiente accademico (principalmente matematico, bioinformatico e su lavori genomici).
Come ho già detto supporta vari paradigmi:
- procedurale: il codice non deve per forza essere incorporato in classi ed è molto ricco di tipi e strutture dati builtin
- funzionale: le funzioni sono oggetti (non esistono i delegate, tanto per capirci) e possono essere passate in giro o ritornate da altre funzioni in maniera normale permettendo la creazione di funzioni di ordine superiore. Incorpora anche vari costrutti derivati dal mondo funzionale: map, reduce, funzioni lambda, iteratori, currying
- metaprogrammazione: è possibile rendere custom in maniera molto facile l’accesso agli attributi e rendere custom il processo di creazione di una classe
- object-oriented: tutto è un oggetto in Python, tutto. E supporta l’ereditarietà multipla anche se è molto poco usata.
Torniamo a IronPython.
Interprete IronPython
Il tutorial di Python parte sempre dall’interprete interattivo, perché è il modo migliore per impratichirsi con il linguaggio, esplorarlo, leggere la documentazione dei metodi e appunto, imparare (tra l’altro è usabile anche come calcolatrice ;-). Con IronPython è lo stesso, basta installare IronPython 2.0) e tutte le sue dipendenze e poi avviare la console.
Da linea di comando è possibile specificare anche la completazione automatica tramite tasto tab e la colorazione della sintassi che sono molto, molto comode (usate ipy.exe -D -X:TabCompletion -X:ColorfulConsole).
Vediamo un esempio:
C:\Documents and Settings\Lawrence>ipy.exe -D -X:TabCompletion -X:ColorfulConsole
IronPython 2.0 (2.0.0.0) on .NET 2.0.50727.1433
Type "help", "copyright", "credits" or "license" for more information.
>>> 3 + 4
7
>>> print "Hello UGIdotNET"
Hello UGIdotNET
>>>
Gli assembly (a parte System e mscorlib) vanno referenziati esplicitamente:
>>> import clr
>>> clr.AddReference('System.Drawing')
>>> from System.Drawing import Point, Color
>>> Point(10, 30)
<System.Drawing.Point object at 0x000000000000002B [{X=10,Y=30}]>
>>> Color.Red
<System.Drawing.Color object at 0x000000000000002C [Color [Red]]>
>>> p = Point(10, 30)
>>> dir(p)
['Add', 'Ceiling', 'Empty', 'Equals', 'GetHashCode', 'GetType', 'IsEmpty', 'MemberwiseClone', 'Offset', 'ReferenceEquals', 'Round', 'Subtract', 'ToString', 'Truncate', 'X', 'Y', '__add__', '__class__', '__delattr__', '__doc__', '__eq__', '__getattribute__', '__hash__', '__init__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__sub__']
>>> str(p)
'{X=10,Y=30}'
>>> p.ToString()
'{X=10,Y=30}'
Un’altra delle caratteristiche di Python (e quindi IronPython) è la possibilità di manipolare oggetti vivi sempre tramite l’interprete:
>>> import clr
>>> clr.AddReference('System.Windows.Forms')
>>> clr.AddReference('System.Drawing')
>>> from System.Windows.Forms import Application, Button, Form
>>> from System.Drawing import Point
>>> x = 0
>>> y = 0
>>> form = Form()
>>> form.Text = "Hello World"
>>> button = Button(Text="Button Text")
>>> form.Controls.Add(button)
>>> def click(sender, event):
... global x, y
... button.Location = Point(x, y)
... x += 5
... y += 5
...
>>> button.Click += click
>>> Application.Run(form)
Questa sessione interattiva aprirà una finestra Windows Forms con un bottone che si sposta ad ogni click. Il controllo ritornerà all’inteprete una volta chiusa la finestra.
Riguardo all’introspezione degli oggetti:
>>> import System.Collections
>>> interfaces = [entry for entry in dir(System.Collections) if entry.startswith('I')]
>>> for interface in interfaces:
... print interface
...
ICollection
IComparer
IDictionary
IDictionaryEnumerator
IEnumerable
IEnumerator
IEqualityComparer
IHashCodeProvider
IList
dir() permette di estrarre la lista degli attributi e dei metodi di un oggetto. Un altra utilità è l’help in linea sia sulla parte .NET che sulla libreria standard di Python, tramite la funzione help():
>>> from System.Collections import Stack
>>> help(Stack.Push)
Help on method_descriptor:
Push(...)
Push(self, object obj)
Inserts an object at the top of the
System.Collections.Stack.
obj: The System.Object to push onto the
System.Collections.Stack. The value can be null.
>>> # help works on the standard library too, of course
>>> import os
>>> help(os.makedirs)
Help on function makedirs in module os:
makedirs(name, mode=511)
makedirs(path [, mode=0777])
Super-mkdir; create a leaf directory and all intermediate ones.
Works like mkdir, except that any intermediate path segment (not
just the rightmost) will be created if it does not exist. This is
recursive.
Direi che è tutto come introduzione dell’ambiente.