lundi 11 avril 2011

pylab: Graphiques sous python avec matplotlib

Description
Le module pylab est une implémentation donnant accès à toute la puissance de matplotlib.
matplotlib est une librairie extremement puissante permettant de générer et dessiner des graphiques (charting) en python.
Pour vous faire une idée des possibilités de matplotlib et pylab, je vous invite à visiter la galerie du site officiel.

Voici d'ailleurs quelques exemples issus directement de la galerie de matplotlib.

 


Installation
La compilation de matplotlib et son installation sous python à l'aide de pip ou easy_install n'est vraiment pas simple.
Il est préférable d'opter pour l'installation d'un package déjà prêt

sudo apt-get install python-matplotlib

à partir de là, le module pylab est accessible en python.

Lecture
Voir la description du livre "matplotlib for python developpers" de Sandro Tosi (aussi disponible sur Amazon).
Ce livre présente une introduction de matplotlib en partant des principes de base jusqu'au niveaux les plus avancés. Le but étant bien entendu de produire efficacement des graphiques 2D de hautes qualités. 
Le livre passe également en revue l'inclusion de graphiques matplotlib dans des environnements GTK+, Qt, wxWidgets ainsi que l'inclusion dans des sites web et son utilisation avec des frameworks populaires tels que de Pylons et Django.

Tutorial vidéo
Voici deux vidéos d'instructions enregistrée lors du Python Scientific Computing conférence.
La première est une introduction, la seconde un module avancé.





Par contre, le site officiel contient plusieurs références de tutoriels, de vidéos et code d'exemples... ainsi qu'une impressionnante galerie d'exemple (voir section ressources).

Quelques exemples en Python
Voici quelques exemples rudimentaire d'utilisation de matplotlib.
Pour chacun des exemples, le graphique sera sauvegardé dans un fichier (accessible dans le répertoire /tmp)

Je manque malheureusement de temps pour achever ce point et j'en suis le premier navré.
J'y penserai à l'occasion.

Ressources

vendredi 8 avril 2011

dotProject.net - un gestionnaire de projet libre - simple - efficace

Ce matin, en faisant un peu de lecture, j'ai eu l'occasion de prendre connaissance d'un projet libre nommé DotProject.net.

Introduction
dotProject.Net se veut être un gestionnaire de projet simple et efficace. Basé sur une interface WEB, dotProject cherche à regrouper tout ce qui est utile à la gestion de projet tout en évitant la surcharge avec de nombreux outils souvent fort peut utiles.

Depuis le début, dotProjet a pour but principal de remplir quelques exigences relativement simples:
  • Une interface utilisateur simple, épurée et consistante.
  • Intégrer des fonctionnalités de gestion de projet. - pas d'autre CMS, groupware ou tout autre choses. dotProject est juste un environnement de gestion de projet.
  • Open source, Libre d'acces (pas de royalties), libre d'usage.
Parmi les fonctionnalités, l'on retrouve:
  • Gestion des utilisateurs
  • Gestion helpdesk (troubles/bug) via e-mail basé sur un système de ticket (Integration de voxel.net's ticketsmith)
  • Gestion des clients et sociétés.
    Ce qui permet de gérer autre chose que des projets internes.
  • Listes de projets
  • Gestionnaire de tâche hiérarchique (une tâche dépendant d'une autre)
  • Dépôt de fichiers (très utile pour le stockage des documents de requirements) 
  • gestion des contacts
  • Calendrier (et donc gestion du temps, des heures prestées, des deadlines, etc). 
  • Forum de discussion
  • Ressource Based Permissions
Finalement, et contrairement à ce que laisserait penser le nom du projet, dotProjet.net n'est pas écrit en Asp.Net mais en PHP :-)
Il sera donc facile de l'installer sur un environnement Linux :-)

Vidéos
Voici quelques vidéos de présentations pour se faire une idée du soft.
La première est certes en néerlandais mais laisse néanmoins entrevoir le fonctionnement général du logiciel.


Au ce petit tutorial expliquant le fonctionnement de la gestion de projet




Lien utiles:

jeudi 7 avril 2011

La souris ne clique plus sur Ubuntu - FireFox

Introduction
ps: voir solution plus loin dans l'article.

Nous avons installé un PC (Packard Bell iMedia D3580BE) dans notre salon afin que les enfants puissent naviguer sur le net sans être livrés à eux-mêmes.
Pour éviter les problèmes de contamination par le net, j'ai remplacé Windows par Ubuntu 9.04 et chaque membre de la famille dispose de son propre login.
Pour rendre le tout plus réactif, nous n'éteignons jamais cette machine mais optons plutôt pour une "mise en veille".

Nous avons remarqué quelques problèmes avec le fonctionnement de la souris (sans marque, laser, port PS2) lorsque:
  • L'utilisateur était déconnecté par le système d'exploitation.
  • Lorsque l'ordinateur était mis en mode veille.
En effet, en sortant du mode veille pour connecter un autre utilisateur, nous avons souvent remarqué que la souris devenait inutilisable... elle bouge correctement mais ne clique plus.
En fait, c'est soit un mauvais bouton de la souris qui clique ou seulement certaines parties du bureau qui reste actifs.
Nous avons également remarqué que FireFox était presque toujours actif lorsque le problème apparaissait... et que cela ne faisait suite qu'a une phase de réveil de la machine ou à un changement d'utilisateur.

C'est assez agaçant parce que jusqu'à présent nous étions obligés de rebooter la machine pour retrouver un fonctionnement normal.

La solution
Il y a pourtant un truc simple pour remettre la souris en bonne état de marche.
Je positionne le curseur au dessus de la toolbar des raccourcis de Firefox (et oui, FireFox est souvent ouvert et en premier plan lorsque cela arrive).
Ensuite je fais ceci sans trop me presser:
  • Clique du bouton droit (menu contextuel) puis relâcher.
  • Clique du bouton central (roulette) puis relâcher.
  • Clique du bouton gauche (clique normal) puis relâcher.
Et par magie, tout rentre dans l'ordre.
Cela a toujours parfaitement fonctionné.

mercredi 6 avril 2011

Notation abrégée du IF-ELSE (IFF) en Python

Description
La plupart des langages ont une notation abrégée permettant de combiner une affectation et un test en une seule instruction.
Par exemple, en C#, il est possible d'écrire
// exemple en C#
X=testValue>5?1:2;
Ainsi, la valeur assignée à X dépendra de la valeur contenu dans testValue.
Si testValue est supérieur à 5, X contiendra la valeur 1 sinon X contiendra la valeur 2.

Notation équivalente en Python
# Exemple en Python 
X = testValue>5 and 1 or 2
Si testValue est supérieur à 5, X contiendra la valeur 1 sinon x contiendra la valeur 2.

Comment cela fonctionne t'il?
Quand Python rencontre une expression testValue>5 and 1 or 2, il essaye d'évaluer le premier argument (testValue>5) pour obtenir le résultat boolean.
Ensuite, l'interpréteur vérifie s'il se trouve dans une instruction "if".
Si c'est le cas, et en fonction du résultat de la première évaluation, Python passera à l'évaluation du and ou du or.
Si l'interpréteur est dans une opération d'assignation, Python extraira la valeur après le and si le test est positif ou bien la valeur après le or si le test est négatif. 

>>> #---- Exemple IF-ELSE condensé ------ >>> testValue = 1
>>> X = testValue > 5 and 1 or 2
>>> X
2
>>> testValue = 6
>>> X = testValue > 5 and 1 or 2
>>> X
1

>>> #---- Exemple TEST  ------ 
>>> testValue = 1
>>> if testValue > 5 and False or True:
...     print "OK"
... else:
...     print "!"
... 
OK
>>> if testValue > 5 and False or False:
...     print "OK"
... else:
...     print "!"
... 
!
>>> if testValue > 5 and True or False:
...     print "OK"
... else:
...     print "!"
... 
!
>>> testValue = 10
>>> if testValue > 5 and True or False:
...     print "OK"
... else:
...     print "!"
... 
OK

mardi 5 avril 2011

Les fonctions en Python

Introduction
Une fonction c'est une fonction et fonctionne selon les mêmes principes dans tous les langages de programmation.
Cependant, chaque langage (Delphi, C#, C++) apporte ses propres spécificités et Python apporte également les siennes mais toujours dans l'esprit de la simplicité.

Ainsi, les fonctions Python:
  • Acceptent des valeurs par défaut (comme beaucoup de langage).
  • Permet de retourner plusieurs valeurs!
  • Peut accepter un nombre indéterminé d'arguments (non nommés).
  • Permet de nommer les arguments assignés ou de fournir des arguments complémentaires nommés.
Description générale d'une fonction

def ma_fonction( argument, argument_optionel = -1, *args, **kwargs ):
  • argument - argument obligatoire, oublier sa valeur lors de l'appel de la fonction provoquera une erreur.
  • argument_optionel - argument avec une valeur par défaut. L'argument est optionnel, ainsi, si l'on ne précise pas sa valeur lors de l'appel, c'est la valeur par défaut qui sera utilisée.
  • *args - liste d'argument non nommés. Ils sont ajoutés en en fin d'appel de fonction... un exemple typique est la définition d'une fonction sum acceptant un nombre illimité d'arguments.
    Ex: sum( 1, 5, 7 ) + sum( 1, 3, 5, 48, 78, 47)
    La liste d'argument non nommé succède les arguments obligatoires et les arguments optionnels.
  • **kwargs - liste contenant les arguments nommés "keyword args". Il est ainsi possible d'ajouter à la volée des arguments identifiés par leur nom d'argument. J'évoquais plus haut le nom d'une colonne d'une feuille Excel.
Exemples pratiques
Utiliser la valeur par défaut
>>> def maFonction( a, b, c = -1 ):
...     print( "a=%i, b=%i, c=%i" % (a,b,c) )
... 
>>> maFonction(7,5)
a=7, b=5, c=-1
>>> maFonction(7,5,128)
a=7, b=5, c=128

Retourner plusieurs valeurs
Python est capable de retourner plusieurs valeurs en une seule ligne de commande.
Ces valeurs peuvent être récupérée dans différentes variables ou dans un tuple.
>>> def maFonction2( a, b, c):
...     return a+b, b+c, a-c
... 
>>> maFonction2( 10, 5, 3 )
(15, 8, 7)

>>> # récupération dans des variables 
>>> aa, bb, cc = maFonction2( 10, 5, 3 )
>>> print( "aa=%i, bb=%i, cc=%i" % (aa, bb, cc) )
aa=15, bb=8, cc=7

>>> # récupération via un Tuple 
>>> resultat = maFonction2( 10, 5, 3 )
>>> print( "aa=%i, bb=%i, cc=%i" % resultat )
aa=15, bb=8, cc=7
>>> print( "bb=%i" % ( resultat[1] ) )
bb=8 

*args - Accepter un nombre indéterminé d'arguments
Si cela peu paraître inutile, l'opportunité d'ajouter un nombre illimité de paramètre en fin de fonction peu s'avérer terriblement utile pour des fonctions de traitement.
Pour les puristes, il est certes possible de concevoir sa fonction pour imposer un argument de type liste... mais cela n'est pas des plus commodes à utiliser et alourdir inutilement l'utilisation de la syntaxe.

Voici l'exemple d'une fonction mySum capable de sommer un nombre illimité d'arguments.

>>> def mySum( *args ):
...     result = 0
...     for item in args:
...             result += item
...     return result
... 
>>> mySum( 1, 2, 3 )
6 

Plus d'information concernant *args et **kwargs sont disponible dans l'article "How to use *args and **kwargs in Python".

**kwargs - Utiliser les arguments nommés
Il est possible de fournir un ou plusieurs arguments (nom + valeur) à une fonction alors que ces derniers ne se trouvent pas dans la liste des paramètres.

Cela permet par exemple de mettre en place des recherche dynamique sur un contenu/données tout aussi dynamique... c'est à la dernière minute que l'on vérifiera si l'argument (identifier par son nom) existe bien dans les données à explorer (par exemple le nom d'une colonne d'une fiche Excel).

A noter que si l'on nomme les arguments obligatoire dans l'appel de fonction, il n'est alors plus possible d'utiliser les arguments complémentaires non nommées (*args)

Voici un exemple sommaire d'utilisation de **kwargs.
>>> def myKwTest( **kwargs ):
...     for key in kwargs:
...             print( "%s = %s" % (key, kwargs[key] ) )
... 
>>> myKwTest( a=12, violette="bonbon", homer="Simpson", age=38 )
a = 12
violette = bonbon
age = 38
homer = Simpson 

Par exemple, l'utilisation de **kwargs permettrait d'écrire une fonction retrouvant un noeud dans un document xml, noeud qui devrait avoir une valeur particulière pour un ou plusieurs attributs (à la fonction à adapter son fonctionnement)
Voici code rudimentaire mettant en oeuvre ce principe d'appel
>>> def findNode( tagName, **attrs_kwargs ):
...     pass
... 
>>> aNode = findNode( "invoice", customerId=125487, orderNr=128 )
>>> aNode2 = findNode( "invoice", invoiceNr="2011/04/03-12748" ) 

BeautifulSoup décrit dans l'article "BeautifulSoup - comment extraire ou manipuler une page html en Python" utilise d'ailleurs **kwargs pour faciliter l'écriture des recherche de noeud dans les documents.

Plus d'information concernant *args et **kwargs sont disponible dans l'article "How to use *args and **kwargs in Python".

Extra: Assignation d'argument via dictionnaire
Il est possible d'assigner les différents arguments d'une fonction en utilisant un dictionnaire. Ce dernier reprendra alors le nom de l'argument et sa valeur.
Notez la syntaxe du ** devant le dictionnaire lors de l'appel de la fonction.

>>> def myFunctionSimple( a, b, c=128 ):
...     print( "a=%i, b=%i, c=%i" % (a,b,c) )
... 
>>> myFunctionSimple( **{"b":007, "a":4587} )
a=4587, b=7, c=128
>>> myDico = { "a":12, "b":120, "c":1200 }
>>> myFunctionSimple( **myDico )
a=12, b=120, c=1200

Le dictionnaire peut bien entendu être utilisé pour fournir des arguments complémentaires qui seront soit utilisé par *args, soit par **kwargs.

>>> def myFunction3( a, b, c=1, **kwargs ):
...     print( "a=%i, b=%i, c=%i" % (a,b,c) )
...     for key in kwargs:
...             print( "kwargs... %s = %s" % (key, kwargs[key]) )
... 
>>> # Utilisation d'un dictionnaire pour assigner les valeurs
>>> #    des paramètres obligatoires et optionnels.
>>> myFunction3( **{"a":12, "zz":"moustique", "bzzz":"mouche", "b":-1} )
a=12, b=-1, c=1
kwargs... bzzz = mouche
kwargs... zz = moustique

Un exemple complet
Voici un exemple complet issus de Linux Magazine France Hors-série entièrement dédié à Python (no 53 parution mars/avril 2011)

Soit la fonction myFunc affichant le valeur de tous les paramètres.

def myFunc( datas, opt_datas=[], *args, **kwargs ):
    for d in datas:
        print( "datas: %s" % (d) )
    for d in opt_datas:
        print( "opt_datas: %s" % (d) )
    i = 0
    for a in args:
             i += 1
        print( "exploring args[%i]..." % (i) ) 
        for d in a:
            print( "args[%i] has data %s" % (i,d) )
    i = 0
    for key in kwargs:
        i += 1
        print( "exploring kwargs[\"%s\"]..." % (key) )
        values = kwargs[key]
        for d in values:
            print( "kwargs[\"%s\"] has data %s" % (key, d) ) 

Et différents appels de fonction dont on pourra inspecter les résultats
myFunc( [1,2] )
myFunc( [1,2], [3,4] )
myFunc( [1,2], [3,4], [4,6] )
myFunc( [1,2], [3,4], [4,6], [7,8] )
# ---------------------------------------------
myFunc( datas=[1,2] )
myFunc( datas=[1,2], opt_datas=[3,4] )
myFunc( datas=[1,2], opt_datas=[3,4], brol=[4,6] )
myFunc( datas=[1,2], opt_datas=[3,4], brol=[4,6], machin=[7,8] )
# ---------------------------------------------
myFunc( [1,2], opt_datas=[3,4] )
myFunc( [1,2], opt_datas=[3,4], brol=[4,6] )
myFunc( [1,2], [3,4], machin=[7,8] )
myFunc( [1,2], [3,4], [4,6], machin=[7,8] ) 

Le code source complet est disponible dans le fichier functionDemo.py (exécutable en ligne de commande).
Le résultat complet est disponible dans le fichier functionDemo.result

vendredi 1 avril 2011

Remonter un laptop serveur web sous Linux

Depuis que la maison est passée en recyclage intensif de nos déchets, je passe assez fréquemment au parc à container de Waterloo/Braine-L'Alleud.

Je suis assez effaré de voir la quantité industriel de déchet électroménager... dont un nombre certains d'ordinateurs plus ou moins dépassés.

Il y a une semaine, je me demandais s'il était possible de monter un petit Web serveur basé sur Linux en utilisant du matériel de recyclage.

J'ai donc récupéré une PC Portable Siemens Amilo visiblement délaissé sans alimentation.


Ma première étape passe par le démontage complet pour une mise sous tension.
1) Souder directement des cables d'alimentation sur la carte mère.
3) Utiliser un ancien transformateur Dell pour fournir les 19 volts nécessaire à la carte mère (cfr documentation trouvée sur internet).

2) Ici lors d'un second démontage, je vérifie le câblage de la dalle graphique car elle ne semble pas afficher d'image. Un examen aux reflets de la lumière indique qu'il y a une image mais que la dalle n'est pas rétro éclairée... en fin de compte, c'est le l'alimentation 19 volts qui était défectueuse. Juste besoin d'un petit coup de fer à souder.


1) détail de la soudure d'alimentation
2) Emplacement du disque dur est vide, il a été enlevé (voila quelqu'un d'intelligent!).
3) détail de l'alimentation de la dalle (19 Volts, 3.3 Volts et contrôle d'intensité).


Mise sous tension (de façon artisanal dans un premier temps).
Et contrôle de fonctionnement.


Hormis le manque de disque dur, cette machine boot sans problème un Live CD d'Ubuntu.
Voici les première information sur le système:
lecteur CD Rom en état de marche.
Connecteur USB sous le capot.
Ecran 1280 x 800 bonne qualité graphique.
micro, haut parleur interne.

Un petit passage par /proc/meminfo nous apprends que nous avons 500Mb de ram.
MemTotal:       482940 kB
MemFree:         10736 kB
Buffers:         50220 kB
Cached:         223368 kB
SwapCached:          0 kB

Et /proc/cpuinfo nous informe qu'il y a un Amd Sempron (mono core, 1 thread)  cadencé à 800 Mhz.
processor    : 0
vendor_id    : AuthenticAMD
cpu family   : 15
model        : 8
model name   : Mobile AMD Sempron(tm) Processor 2800+
stepping     : 2
cpu MHz      : 798.305
cache size   : 256 KB

Le matériel disponible est obtenu à l'aide de lspci
00:00.0 Host bridge: Silicon Integrated Systems [SiS] 760/M760 Host (rev 03)
00:01.0 PCI bridge: Silicon Integrated Systems [SiS] SG86C202
00:02.0 ISA bridge: Silicon Integrated Systems [SiS] SiS963 [MuTIOL Media IO] (rev 25)
00:02.1 SMBus: Silicon Integrated Systems [SiS] SiS961/2 SMBus Controller
00:02.5 IDE interface: Silicon Integrated Systems [SiS] 5513 [IDE]
00:02.6 Modem: Silicon Integrated Systems [SiS] AC'97 Modem Controller (rev a0)
00:02.7 Multimedia audio controller: Silicon Integrated Systems [SiS] AC'97 Sound Controller (rev a0)
00:03.0 USB Controller: Silicon Integrated Systems [SiS] USB 1.1 Controller (rev 0f)
00:03.1 USB Controller: Silicon Integrated Systems [SiS] USB 1.1 Controller (rev 0f)
00:03.3 USB Controller: Silicon Integrated Systems [SiS] USB 2.0 Controller
00:04.0 Ethernet controller: Silicon Integrated Systems [SiS] SiS900 PCI Fast Ethernet (rev 91)
00:06.0 FireWire (IEEE 1394): Texas Instruments TSB43AB22/A IEEE-1394a-2000 Controller (PHY/Link)
00:09.0 CardBus bridge: O2 Micro, Inc. OZ711M1/MC1 4-in-1 MemoryCardBus Controller (rev 20)
00:09.1 CardBus bridge: O2 Micro, Inc. OZ711M1/MC1 4-in-1 MemoryCardBus Controller (rev 20)
00:09.2 System peripheral: O2 Micro, Inc. OZ711Mx 4-in-1 MemoryCardBus Accelerator
00:18.0 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] HyperTransport Technology Configuration
00:18.1 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] Address Map
00:18.2 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] DRAM Controller
00:18.3 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] Miscellaneous Control
01:00.0 VGA compatible controller: Silicon Integrated Systems [SiS] 661/741/760 PCI/AGP or 662/761Gx PCIE VGA Display Adapter


Finalement, lsusb nous apprend qu'il n'y a pas de périphérique USB "surprise" directement monté sur la carte mère.

C'est déjà un bon début, ce n'est pas une bête de guerre mais c'est déjà bien suffisant pour ce que je veux en faire.
Iil ne manque plus qu'un disque pour faire un petit serveur :-)



4 Jours plus tard, je repasse au parc à container et cette fois, je mets la main sur un Compaq Pressario abandonné avec son alimentation.
Un examen rapide m'apprends que la machine est morte (ne démarre absolument plus, ne réagis pas à la mise sous tension).
Mais si la carte mère est morte, ce n'est pas forcement le cas des autres périphériques... allons donc voir si cette bête contient un disque dur?


En effet, il y a un disque dur.
Il est en parfait état de fonctionnement et fait 5Go :-)


Voici le tout remonté, avec disque dur mais sans dalle graphique (après tout, il est destiné à devenir un petit serveur).
Voici la machine (raccordée au moniteur de mon fils) en phase de chargement.
C'est fou ce que cela fait "Commodore 64 alike".

Pour l'instant, j'ai installé ArchLinux (sans interface graphique) et je vais prochainement m'intéresser à l'installation de:
  • OpenSSH pour prendre la commande à distance en mode terminal. 
  • L'installation d'un serveur web (orienté Python avec Zope) sous ArchLinux.
  • Considérer l'utilisation d'une distribution LAMP (XAMP étant plutôt réservé à Windows)
  • Et qui sait, peut-être qu'un jour je lui mettrai aussi des roulettes ;-)

Time, DateTime: Manipulation du temps en Python

Le module time
La gestion du temps est prit en charge par la librairie time.
Cette librairie représente le temps (date/heure) sous forme d'un tuple (struct_time).
Il est possible d'obtenir de l'aide facilement depuis l'interpréteur à l'aide de:

import time
help(time)

Quelques fonctions...
time()
Retourne un float qui représente le temps écoulé (en sec) depuis le 1ier Janvier 1970.
La valeur retournée représente le temps local (et non le temps UTC/GMT).
Cette fonction est fort utile (tout comme clock) pour évaluer des différences de temps.

clock()
Retourne le temps (en sec) écoulé depuis le démarrage du processus.

gmtime()
Retourne un tuple décrivant l'heure UTC (GMT) actuelle.
gmtime(x) permet également de transformer un nombre de secondes en temps UTC.
Notez le tm_wday qui est le jour de la semaine (commence à zéro pour lundi 0=lundi, 1=mardi, 2=mercredi, 3=jeudi, ... 6=dimanche)
mais aussi tm_yday qui le jour de l'année (Julian day entre 1 et 366) fort utile lorsque l'on utilise un logiciel comme JDEwards.
>>> ex: en Belgique, il est 10h24 le 10 mars 2011
>>> time.gmtime()
time.struct_time(tm_year=2011, tm_mon=3, tm_mday=10, tm_hour=9, tm_min=24, tm_sec=32, tm_wday=3, tm_yday=69, tm_isdst=0)

localtime()
Retourne un tuple avec le temps local (en fonction du timezone de la machine).
localtime(x) sert également a transformer un nombre de seconde en temps local.
>>> # Le 10 mars 2011 à 10h26, localtime affiche:
>>> time.localtime()
time.struct_time(tm_year=2011, tm_mon=3, tm_mday=10, tm_hour=10, tm_min=26, tm_sec=7, tm_wday=3, tm_yday=69, tm_isdst=0)

sleep(x)
Effectue une pause de x secondes.

asctime()
asctime "Ascii Time" converti le temps local en string.
asctime(x) converti un time tuple x en string.
Pour des fonctions plus avancées, voir strftime() et strptime().
>>> time.asctime( time.localtime() )
'Thu Mar 10 10:47:35 2011'
>>> time.asctime( time.gmtime() )
'Thu Mar 10 09:47:38 2011'
>>> time.asctime()
'Thu Mar 10 10:47:41 2011'

ctime()
ctime converti le temps local en string comme le fait asctime().
ctime(x) converti le temps x en second en string.
>>> time.ctime()
'Thu Mar 10 10:50:21 2011'
>>> time.ctime( time.time() )
'Thu Mar 10 10:50:25 2011'
>>> time.asctime( time.localtime() )
'Thu Mar 10 10:50:28 2011'

mktime
mktime (make time) convertit un time tuple contenant une date en temps local en seconds.

strftime() et strptime()
strftime (f pour format) permet de transformer un time tuple en date en suivant le format spécifique.
strptime (p pour parse) fait l'opération inverse.
>>> import time
>>> time.asctime( time.localtime() )
'Thu Mar 10 11:15:08 2011'
>>> time.strftime("%a, %d/%m/%Y %H:%M:%S +0000", time.localtime())
'Thu, 10/03/2011 11:15:11 +0000'
>>> time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())
'Thu, 10 Mar 2011 11:15:15 +0000'

Plus d'information sur la string de formatage dans l'article de docs.python.org.

Arithmétique des dates
Voici quelques exemples démontrant l'usage de l'arithmétique sur les dates.

Evaluer le temps écoulé
>>> atime=time.time()
>>> time.sleep(25.0)
>>> btime=time.time()
>>> difftime = btime - atime # display the difference in seconds
>>> time.ctime( difftime ) # display the difference in local time zone
'Thu Jan  1 01:00:52 1970'
>>> # ATTENTION: différence erronée de 1 heure 0 min 52 sec XXXXXXXX ROUGE
>>> # cette erreur est causée par la time zone dans lequel la date UTC 
>>  # est replacée. l'usage de localtime provoquerait le même problème!
>>> time.asctime( time.gmtime(difftime) ) # Difference in UTC time zone
'Thu Jan  1 00:00:52 1970'
>>> # Cette fois-ci la différence est correctement évalué. XXXXX VERT
>>> difftuple = time.gmtime(difftime)
>>> difftuple
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=52, tm_wday=3, tm_yday=1, tm_isdst=0)
>>> print( "la différence est de %i heures %i minutes %i secondes" % ( difftuple.tm_hour, difftuple.tm_min, difftuple.tm_sec) )
la différence est de 0 heures 0 minutes 52 secondes

Datetime module

Implémentation des types datetime, time, date et timedelta permettent l'implémentation et la manipulation rapide des types déjà mentionnés.
Ces types supportent également la manipulations arithmétiques.

>>> now = datetime.date.today()
>>> now.ctime()
'Thu Mar 10 00:00:00 2011'
>>> now2 = datetime.datetime.now()
>>> now2.ctime()
'Thu Mar 10 11:59:44 2011'
>>> # Conversion de la date en datetime
>>> now_datetime = datetime.datetime( now.year, now.month, now.day )
>>> now_datetime 
datetime.datetime(2011, 3, 10, 0, 0)
>>> # Evalutation de la difference
>>> diff = now2 - now_datetime
>>> diff
datetime.timedelta(0, 43184, 75719)
>>> print( "Différence en secondes = %i" % (diff.seconds) )
Différence en secondes = 43184
>>> print( "Différence en jours = %i" % (diff.days) )
Différence en jours = 0
>>> print( "Différence en heures = %i" % (diff.seconds / 3600) )
Différence en heures = 11

plus d'info sur documentation datetime sur docs.python.org

Autres modules
Concernant la manipulation du temps, il faut aussi parler des modules

calendar
Le module calandar contient des fonctions tels que calendar.isleapyear ou des fonctions utilitaires retournant des structures ou bien des formattages de calendrier (par mois).
voir www.tutorialspoint.com/python/python_date_time.htm

dateutil
Disponible par défaut, ce module offre quelques extensions concernant la manipulation des dates.
Il gère entre autre les relativedelta permettant de calculer une date relative, voir même de retrouver le dernier jeudi du mois (voir documentation).
L'on retrouve également rrule qui permet de définir des règles recurrences pour constituer des structures à base de date.
Un MUST pour tout ce qui touche la programmation d'agenda.

Exemple de relativedelta (issus du site http://niemeyer.net/python-dateutil):
>>> from datetime import *; from dateutil.relativedelta import *
>>> import calendar
>>> NOW = datetime.now() # Store some values.
>>> TODAY = date.today()
>>> NOW
datetime.datetime(2003, 9, 17, 20, 54, 47, 282310)
>>> TODAY
datetime.date(2003, 9, 17)
>>> NOW+relativedelta(months=+1) # Next month.
datetime.datetime(2003, 10, 17, 20, 54, 47, 282310)
>>> NOW+relativedelta(months=+1, weeks=+1) # Next month, plus one week.
datetime.datetime(2003, 10, 24, 20, 54, 47, 282310)
>>> TODAY+relativedelta(months=+1, weeks=+1, hour=10) # Next month, plus one week, at 10am.
datetime.datetime(2003, 10, 24, 10, 0)

Exemple de rrule (issus du site issus du site http://niemeyer.net/python-dateutil)
>>> list(rrule(DAILY,
               dtstart=parse("19970902T090000"),
               until=parse("19971224T000000")))
[datetime.datetime(1997, 9, 2, 9, 0),
 datetime.datetime(1997, 9, 3, 9, 0),
 datetime.datetime(1997, 9, 4, 9, 0),
 (...)
 datetime.datetime(1997, 12, 21, 9, 0),
 datetime.datetime(1997, 12, 22, 9, 0),
 datetime.datetime(1997, 12, 23, 9, 0)]

Plus de documentation sur niemeyer.net/python-dateutil

pytz
Module relatif à la gestion des timezones.
PS: Je n'ai pas encore été fureter dedans.