Chapter 2: Le langage Python

Le langage Python

Python

A propos de Python

Python est un langage de programmation haut niveau. Sa philosophie de design met en avant la productivité du programmeur et la lisibilité du code. Il possède une syntaxe minimaliste avec très peu de commandes basiques et une sémantique simple, mais il a également une librairies standard large et compréhensible, incluant une API (Application Programming Interface)

API
pour de nombreuses fonctions de base des systèmes d'exploitation. Le code Python, bien que minimaliste, pré-définit des objets tels que des listes (list), des tuples (tuple), des hashtables (dict), et arbitrairement des longs entiers (long).

Python supporte de multiples paradigmes de programmation, incluant la programmation orientée objet (class), impérative (def), et fonctionnelle (lambda).

Python a été lancé par uido van Rossum en 1991. Le langage a un modèle de développement ouvert basé sur la communauté et dirigée par la fondation bénévole Python Software Foundation. Il y a de nombreux interpréteurs et compilateurs qui implémentent le langage Python, dont un en Java (Jython), mais dans ce résumé, nous nous réfèrerons uniquemenent à l'implémentation C créée par Guido.

Vous pouvez trouver de nombreux tutoriels, la documentation officielle et les références de librairie du langage sur le site web officiel de Python. [python]

Pour de plus amples références Python, nous pouvons vous recommander les livres réf. [guido] et réf. [lutz]

Vous pouvez passer ce chapitre si vous êtes déjà familier avec le langage Python.

Démarrage

shell

Les distributions binaires de web2py pour Microsoft Windows ou Aple OS X sont fournies packagées avec un interpréteur Python compilé directement dans la distribution.

Vous pouvez le lancer sur Windows avec la ligne de commande suivante (à entrer sur une invite de commandes DOS):

web2py.exe -S welcome

Sur Apple OS X, entrez la ligne de commande suivante dans un Terminal (en supposant que vous êtes dans le même dossier que web2py.app):

./web2py.app/Contents/MacOS/web2py -S welcome

Sur un système Linux ou sur tout autre système Unix, il est fort possible que Python soit déjà installé. Si c'est le cas, entrez la commande suivante dans un terminal shell:

python web2py.py -S welcome

Si vous n'avez pas Python 2.5 (ou plus récent) déjà installé, vous devrez le télécharger et l'installer avant de lancer web2py.

L'option -S welcome sur la ligne de commande indique à web2py de démarrer sur un shell interactif comme si les commandes étaient exécutées dans un contrôleur pour l'application welcome, l'application par défaut de web2py. Elle présente presque toutes les classes web2py, les objets et fonctions. C'est la seule différence entre la ligne de commande interactive web2py et la ligne de commande basique de Python.

L'interface d'aministration fournit également un shell web pour chaque application. Vous pouvez tester le shell de l'application "welcome" à l'adresse

http://127.0.0.1:8000/admin/shell/index/welcome

Vous pouvez essayer tous les exemples de ce chapitre en utilisant le shell normal ou le shell web.

help, dir

help
dir

Le langage Python fournit deux commandes pour obtenir la documentation à propos d'objets définis, qu'ils soient intégrés ou définis par l'utilisateur.

Il est possible de demander help à propos d'un objet, par example "1":

>>> help(1)
Help on int object:

class int(object)
 |  int(x[, base]) -> integer
 |
 |  Convert a string or number to an integer, if possible.  A floating point
 |  argument will be truncated towards zero (this does not include a string
 |  representation of a floating point number!)  When converting a string, use
 |  the optional base.  It is an error to supply a base when converting a
 |  non-string. If the argument is outside the integer range a long object
 |  will be returned instead.
 |
 |  Methods defined here:
 |
 |  __abs__(...)
 |      x.__abs__() <==> abs(x)
...

et, puisque "1" est un entier, on obtient une desctiption à propos de la classe int et de toutes ses méthodes. Ici, l'affichage a été tronqué car il est normalement très long et détaillé.

De la même manière, il est possible d'obtenir une liste de méthodes de l'objet "1" avec la commande dir:

>>> dir(1)
['__abs__', ..., '__xor__']

Types

type

Python est un langage typé dynamiquement, cela signifie que les variables n'ont pas de type et n'ont même pas à être déclarées. Les valeurs, par contre, ont un type. Vous pouvez connaître le type de valeur contenue dans une variable:

>>> a = 3
>>> print type(a)
<type 'int'>
>>> a = 3.14
>>> print type(a)
<type 'float'>
>>> a = 'hello python'
>>> print type(a)
<type 'str'>

Python inclut également, nativement, les structures de données telles que les listes et les dictionnaires.

str

str
ASCII
UTF8
Unicode
encode

Python supporte l'utilisation de deux différents types de chaines de caractères: les chaines ASCII et Unicode. Les chaînes ASCII sont délimitées par '...', "..." ou par '..' ou """...""". Les trois quotes délimitent les chaînes multilignes. Les chaînes Unicode démarrent avec un u suivi par la chaîne contenant les caractères Unicode. Une chaîne Unicode peut être convertie en une chaîne ASCII en choisissant un encodage, par exemple:

>>> a = 'this is an ASCII string'
>>> b = u'This is a Unicode string'
>>> a = b.encode('utf8')

Après avoir exécuté ces trois commandes, le résultat a est une chaîne ASCII stockant des caractères UTF8. Par défaut, web2py utilise l'encondade UTF8 littéral.

Il est également possible d'écrire des variables dans des chaînes de différentes façons:

>>> print 'number is ' + str(3)
number is 3
>>> print 'number is %s' % (3)
number is 3
>>> print 'number is %(number)s' % dict(number=3)
number is 3

La dernière notation est plus explicite et moins d'erreur sont possibles, et doit être priviliégiée.

Beaucoup d'objets Python, par exemple les nombres, peuvent être sérialisés dans des chaînes en utilisant str ou repr. Ces deux commandes sont très similaires mais produisent un résultat quelque peu différent. Par exemple:

>>> for i in [3, 'hello']:
        print str(i), repr(i)
3 3
hello 'hello'

Pour les classes définies par l'utilisateur, str et repr peuvent être définies/redéfinies en utilisant les opérateurs spéciaux __str__ et __repr__. Ces opérateurs sont décrits brièvement plus tard; pour plus d'infos, référez-vous à la documentation officielle Python[pydocs] . repr a toujours une valeur par défaut.

Une autre caractéristique importante d'une chaîne de caractère Python est que, comme une liste, c'est un objet itératif.

>>> for i in 'hello':
        print i
h
e
l
l
o

list

list

Les principales méthodes d'une liste Python sont "append", "insert" et "delete":

>>> a = [1, 2, 3]
>>> print type(a)
<type 'list'>
>>> a.append(8)
>>> a.insert(2, 7)
>>> del a[0]
>>> print a
[2, 7, 3, 8]
>>> print len(a)
4

Les listes peuvent être séparées:

>>> print a[:3]
[2, 7, 3]
>>> print a[1:]
[7, 3, 8]
>>> print a[-2:]
[3, 8]

et concaténées:

>>> a = [2, 3]
>>> b = [5, 6]
>>> print a + b
[2, 3, 5, 6]

Une liste est itérative; vous pouvez effectuer une boucle dessus:

>>> a = [1, 2, 3]
>>> for i in a:
        print i
1
2
3

Les éléments d'une liste ne sont pas obligées d'être du même type; ils peuvent être de différents types d'objects Python.

Il y a des situations très communes pour lesquelles une list comprehension peut être utilisée. Considérons le code suivant:

>>> a = [1,2,3,4,5]
>>> b = []
>>> for x in a:
        if x % 2 == 0:
            b.append(x * 3)
            
>>> b
[6, 12]

Ce code définit clairement une liste d'objets, sélectionne et modifie une partie d'entre eux dans la liste de départ, et créé une nouvelle liste résultat. Ce code peut être entièrement remplacé par la list comprehension suivante:

>>> a = [1,2,3,4,5]
>>> b = [x * 3 for x in a if x % 2 == 0]
>>> b
[6, 12]

tuple

tuple

Un tuple est comme une liste, mais sa taille et ses éléments ne sont pas mutables, alors qu'ils le sont dans une liste. Si l'élément d'un tuple est un objet, les attributs de l'objet sont mutables. Un tupe est délimité par des parenthèses.

>>> a = (1, 2, 3)

Donc bien que ceci fonctionne pour une liste:

>>> a = [1, 2, 3]
>>> a[1] = 5
>>> print a
[1, 5, 3]

L'affectation d'un élément ne fonctionne pas pour un tuple:

>>> a = (1, 2, 3)
>>> print a[1]
2
>>> a[1] = 5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Un tuple, comme une liste, est un objet itératif. Notez qu'un tuple n'ayant qu'un seul élément doit inclure une virgule derrière, comme montré ci-dessous:

>>> a = (1)
>>> print type(a)
<type 'int'>
>>> a = (1,)
>>> print type(a)
<type 'tuple'>

Les tuples sont très utiles pour rassembler suffisamment d'objets à cause de leur immutabilité, et les parenthèses sont souvent optionnelles:

>>> a = 2, 3, 'hello'
>>> x, y, z = a
>>> print x
2
>>> print z
hello

dict

dict

Un dict-ionnaire Python est une table de hash qui fait correspondre une clé à la valeur d'un objet. Par exemple:

>>> a = {'k':'v', 'k2':3}
>>> a['k']
v
>>> a['k2']
3
>>> a.has_key('k')
True
>>> a.has_key('v')
False

Les clés peuvent être de n'importe quel type d'une table de hash (int, string, ou n'importe quel objet implémentant la méthode __hash__ dans la classe). Les valeurs peuvent être de n'importe quel type. L'ensemble des clés et valeurs dans un même dictionnaire n'ont pas à être forcément du même type. Si les clés sont des caractères alphanumériques, un dictionnaire peut aussi être déclaré avec une syntaxe alternative:

>>> a = dict(k='v', h2=3)
>>> a['k']
v
>>> print a
{'k':'v', 'h2':3}

Les méthodes utiles sont has_key, keys, values et items;

>>> a = dict(k='v', k2=3)
>>> print a.keys()
['k', 'k2']
>>> print a.values()
['v', 3]
>>> print a.items()
[('k', 'v'), ('k2', 3)]

The items method produces a list of tuples, each containing a key and its associated value. La méthode items fournit une liste des tupes, contenant chacun une clé et sa valeur associée.

Les éléments d'un ductionaire et les éléments d'une liste peuvent être supprimés avec la commande del;

>>> a = [1, 2, 3]
>>> del a[1]
>>> print a
[1, 3]
>>> a = dict(k='v', h2=3)
>>> del a['h2']
>>> print a
{'k':'v'}

De manière interne, Python utilise l'opérateur hash pour convertir les objets en entiers, et utilise l'entier pour déterminer où est stockée la valeur.

>>> hash("hello world")
-1500746465

A propos de l'indentation

Python utilise l'indentation pour délimiter les blocs de code. Un bloc commence avec une fin de ligne en colonne, et continue pour toutes les lignes qui ont la même indentation ou une indentation plus importante que la ligne suivante. Par exemple:

>>> i = 0
>>> while i < 3:
>>>    print i
>>>    i = i + 1
>>>
0
1
2

Il est habituel d'utiliser 4 espaces pour chaque niveau d'indentation. C'est une bonne règle pour ne pas confondre les tabulations avec les espaces, qui peuvent résulter en (d'invisibles) confusions.

for...in

for

En Python, vous pouvez effectuer des boucles avec des objets itératifs:

>>> a = [0, 1, 'hello', 'python']
>>> for i in a:
        print i
0
1
hello
python

Un raccourci commun est xrange, qui génère un rang itératif sans stocker entièrement la liste des éléments.

>>> for i in xrange(0, 4):
        print i
0
1
2
3

C'est équivalent à la syntaxe C/C++/C#/Java:

for(int i=0; i<4; i=i+1) { print(i); }

Une autre commande utile est enumerate, qui compte en bouclant:

>>> a = [0, 1, 'hello', 'python']
>>> for i, j in enumerate(a):
        print i, j
0 0
1 1
2 hello
3 python

Il y a également un mot-clé range(a, b, c) qui retourne une liste d'entiers en commençant par la valeur a, s'incrémentant de c, et finissant avec la dernière value plus petite que b, la valeur par défaut de æ est 0 et la valeur par défaut de c est 1. xrange est similaire ne génère par contre pas de liste, seulement un itérateur tout au long de la liste; c'est donc mieux pour des boucles.

Vous pouvez sortir d'une boucle en utilisant break

>>> for i in [1, 2, 3]:
         print i
         break
1

Vous pouvez aller à l'itération suivante sans exécuter le code entier du bloc en utilisant continue

>>> for i in [1, 2, 3]:
         print i
         continue
         print 'test'
1
2
3

while

while

La boucle while en Python fonctionne exactement comme dans la plupart des autres langages, en effectuant une boucle sur un nombre indéfini de fois et en testant une condition avant chaque itération. Si la condition est False, la boucle se termine.

>>> i = 0
>>> while i < 10:
        i = i + 1
>>> print i
10

Il n'y a pas de loop...until en Python.

if...elif...else

if
elif
else
L'utilisation de conditionnel en Python est intuitive.

>>> for i in range(3):
>>>     if i == 0:
>>>         print 'zero'
>>>     elif i == 1:
>>>         print 'one'
>>>     else:
>>>         print 'other'
zero
one
other

"elif" signifie "else if". Les clauses elif et else sont optionnelles. Il peut y avoir plusieurs elif mais seulement une déclaration else. Les conditions complexes peuvent être créées en utilisant les opérateurs notet or.

>>> for i in range(3):
>>>     if i == 0 or (i == 1 and i + 1 == 2):
>>>         print '0 or 1'

try...except...else...finally

try
except
finally
Exception
Python peut jeter - pardon, lever - des exceptions:

>>> try:
>>>     a = 1 / 0
>>> except Exception, e:
>>>     print 'oops: %s' % e
>>> else:
>>>     print 'no problem here'
>>> finally:
>>>     print 'done'
oops: integer division or modulo by zero
done

Si l'exception est levée, elle est capturée par la clause except, qui est exécutée, alors que la clause else ne l'est pas. Si aucune exception n'est renvoyée, la clause except n'est pas exécutée, mais la clause else l'est. La clause finally est toujours exécutée.

Il peut y avoir plusieurs clauses except pour différentes exceptions possibles:

>>> try:
>>>     raise SyntaxError
>>> except ValueError:
>>>     print 'value error'
>>> except SyntaxError:
>>>     print 'syntax error'
syntax error

Les clauses else et finally sont optionnelles.

Ci-dessous, la liste des exceptions incluses dans Python + HTTP (définies par web2py)

BaseException
 +-- HTTP (defined by web2py)
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- Exception
      +-- GeneratorExit
      +-- StopIteration
      +-- StandardError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
      |    +-- ImportError
      |    +-- LookupError
      |    |    +-- IndexError
      |    |    +-- KeyError
      |    +-- MemoryError
      |    +-- NameError
      |    |    +-- UnboundLocalError
      |    +-- ReferenceError
      |    +-- RuntimeError
      |    |    +-- NotImplementedError
      |    +-- SyntaxError
      |    |    +-- IndentationError
      |    |         +-- TabError
      |    +-- SystemError
      |    +-- TypeError
      |    +-- ValueError
      |    |    +-- UnicodeError
      |    |         +-- UnicodeDecodeError
      |    |         +-- UnicodeEncodeError
      |    |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning

Pour une description détaillée de chacune d'elle, vous pouvez vous référer à la documentation officielle Python.

web2py met en place seulement une nouvelle exception, appelée HTTP. Quand elle est levée, elle emmène le programme à retourner une page d'erreur HTTP (pour plus d'infos, se référer au chapitre 4).

N'importe quel objet peut être levé comme exception, mais il est de bonne pratique de ne lever que des objets qui étendent l'une des classes d'exceptions de Python.

def...return

def
return

Les fonctions sont déclarées en utilisant def. Ci-après, une déclaration typique de fonction Python:

>>> def f(a, b):
        return a + b
>>> print f(4, 2)
6

Il n'est pas nécessaire (voire inutile) de spécifier les types des arguments ou des valeurs de retour. Dans cet exemple, une fonction f est définie et accepte deux arguments.

Les fonctions sont la première syntaxe de code décrite dans ce chapitre pour présenter le concept de scope, ou namespance. Dans l'exemple ci-dessus, les identifiants a et b ne sont pas définis en dehors du périmètre de la fonction f:

>>> def f(a):
        return a + 1
>>> print f(1)
2
>>> print a
Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    print a
NameError: name 'a' is not defined

Les identifiants définis en dehors du périmètre de la fonction sont accessibles dans la fonction; observons comment l'identifiant a est pris en compte dans le code suivant:

>>> a = 1
>>> def f(b):
        return a + b
>>> print f(1)
2
>>> a = 2
>>> print f(1) # new value of a is used
3
>>> a = 1 # reset a
>>> def g(b):
        a = 2 # creates a new local a
        return a + b
>>> print g(2)
4
>>> print a # global a is unchanged
1

Si a est modifié, les appels de fonction suivants utiliseront la nouvelle valeur de la variable globale a car la définition de fonction enregistre la localisation de l'identifiant a, et non la valeur de a au moment de la déclaration de la fonction; cependant, si a est défini à l'intérieur de la fonction g, la variable globale a n'est pas affectée car la nouvelle valeur locale a cache la valeur globale. La référence externe peut être utilisée dans la création de closures:

>>> def f(x):
        def g(y):
            return x * y
        return g
>>> doubler = f(2) # doubler is a new function
>>> tripler = f(3) # tripler is a new function
>>> quadrupler = f(4) # quadrupler is a new function
>>> print doubler(5)
10
>>> print tripler(5)
15
>>> print quadrupler(5)
20

La fonction f créé de nouvelles fonctions; et noez que le périmètre du nom g est entièrement interne à f, les closures sont extrèmement puissantes.

Les arguments de fonction peuvent avoir des valeurs par défaut, et peuvent retourner plusieurs résultats:

>>> def f(a, b=2):
        return a + b, a - b
>>> x, y = f(5)
>>> print x
7
>>> print y
3

Les arguments de fonction peuvent être passés explicitement par nom, et cela signigie que l'ordre des arguments spécifié par l'appellant peut être différent de l'ordre défini dans la fonction:

>>> def f(a, b=2):
        return a + b, a - b
>>> x, y = f(b=5, a=2)
>>> print x
7
>>> print y
-3

Les fonctions peuvent également prendre un nombre d'arguments en variables d'exécution:

>>> def f(*a, **b):
        return a, b
>>> x, y = f(3, 'hello', c=4, test='world')
>>> print x
(3, 'hello')
>>> print y
{'c':4, 'test':'world'}

Ici, les arguments non passés par nom (3, 'hello') sont stockés dans le tupble a, et les arguments passés par nom (c et test) sont stockés dans le dictionnaire b.

Dans le cas opposé, une liste ou un tuple peuvent être passés à une fonction qui nécessite la position individuelle des arguments en les récupérant:

>>> def f(a, b):
        return a + b
>>> c = (1, 2)
>>> print f(*c)
3

et un dictionnaire peut être ouvert pour obtenir les arguments clés:

>>> def f(a, b):
        return a + b
>>> c = {'a':1, 'b':2}
>>> print f(**c)
3

lambda

lambda

lambda fournit un moyen de créer une fonction très courte non nommée très facilement:

>>> a = lambda b: b + 2
>>> print a(3)
5

L'expression "lambda [a]:[b]" est lue littéralement comme "une fonction avec les arguments [a] qui retourne [b]". L'expression lambda est elle-même non nommée, mais la fonction acquiert un nom en étant assignée à un identifiant a. Les règles de périmètre pour def s'appliquement à lambda également, et en fait, le code ci-dessous, avec le respect de a, est identique à la déclaration de la fonction en utilisant def:

>>> def a(b):
        return b + 2
>>> print a(3)
5

Le seule bénéfice de lambda est la brièveté; cependant, la brièveté peut être très contraignante dans certaines situations. Considérons une fonction appelée map qui exécute une fonction à tous les objets d'une liste, en créant une nouvelle liste:

>>> a = [1, 7, 2, 5, 4, 8]
>>> map(lambda x: x + 2, a)
[3, 9, 4, 7, 6, 10]

Ce code aurait doublé de taille si def avait été utilisé au lieu de lambda. Le principal aspect de lambda est que (dans l'implémentation Python) la syntaxe autorise seulement une seule expression; cependant, pour des fonctions plus longues, def peut être utilisé et le coût supplémentaire de fournir une fonction nommée diminue plus la longeur de la fonction augmente. Comme def, lambda peut être utilisée pour des fonctions curry: de nouvelles fonctions peuvent être créées en regroupant des fonctions existantes telles que la nouvelle fonction qui ramène un ensemble différent d'arguments:

>>> def f(a, b): return a + b
>>> g = lambda a: f(a, 3)
>>> g(2)
5

Il y a de nombreuses situations où cette fonctionnalité est utile, mais l'un d'eux est directement intégré dans web2py: le cache. Supposons que vous avez une fonction chère qui vérifie si l'argument qui lui est passé est premier:

def isprime(number):
    for p in range(2, number):
        if (number % p) == 0:
            return False
    return True

Cette fonction est malheureusement très coonsommatrice de temps.

Supposons que vous avez une fonction en cache cache.ram qui prend 3 arguments: une clé, une fonction et un nombre de secondes.

value = cache.ram('key', f, 60)

La première fois qu'elle est appelée, elle appelle la fonction f(), stocke la sortie dans un dictionnaire en mémoire (appelé "d"), et le retourne afin que la valeur soit:

value = d['key']=f()

La deuxième fois qu'elle est appelée, si la clé est dans le dictionnaire et n'est pas plus ancienne que le nombre de secondes spécifié (60), elle retourne la valeur correspondante sans effectuer l'appel à la fonction.

value = d['key']

Comment mettre en cache la sortie de la fonction isprime pour n'importe quelle entrée ? Voici comment procéder:

>>> number = 7
>>> seconds = 60
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True

La sortie est toujours la même, mais la première fois cache.ram est appelée, isprime est appelé; la deuxième fois ile ne le sont pas.

Les fonctions Python, créées que ce soit avec def ou lambda acceptent la re-définition de fonctions existantes utilisant des ensembles d'arguments différents. cache.ram et cache.disk sont des fonctions de cache web2py.

class

class

Car Python a une définition de types dynamique, les classes et objets Python peuvent sembler étranges. En fait, vous n'avez pas besoin de définir les variables membre (attributs) quand vous déclarez une classe, et différentes instances de la même classe peuvent avoir différents attributs. Les attributs sont généralement associés avec l'instance, et non avec la classe (excépté lorsqu'ils sont déclarés comme "attributs de classe", ce qui est la même chose que des "variables statiques" en C++/Java).

Voici un exemple:

>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.myvariable = 3
>>> print myinstance.myvariable
3

Notons que pass est une commande qui n'exécute rien. Dans ce cas, c'est utilisé pour définir une classe MyClass qui ne contient rien. MyClass() appelle le constructeur de la classe (dans ce cas, le constructeur par défaut) et retourne un objet, une instance de la classe. L (object) dans la définition de la classe indique que notre classe étend la classe de l' objet de base. Ce n'est pas requis, mais c'est une bonne pratique.

Voici une classe plus complexe:

>>> class MyClass(object):
>>>    z = 2
>>>    def __init__(self, a, b):
>>>        self.x = a
>>>        self.y = b
>>>    def add(self):
>>>        return self.x + self.y + self.z
>>> myinstance = MyClass(3, 4)
>>> print myinstance.add()
9

Les fonctions déclarées à l'intérieur de la classe sont des méthodes. Plusieurs méthodes ont des noms réservés. Par exemple, __init__ est le constructeur. Toutes les variables sont locales à la méthode exceptées les variables déclarées en dehors des méthodes. Par exemple, z est une variable de classe, équivalente à une variable statique en C++ qui conserve la même valeur pour toutes les instances de la classe.

Notons que __init__ prend trois arguments et add n'en prend qu'un, et nous les appelons maintenant avec 2 et 0 arguments respectivement. Le premier argument représente, par convention, le nom local utilisé à l'intérieur de la méthode pour se référer à l'objet courant. Nous utilisons ici self pour seréférer à l'objet courant, mais nous pourrions utiliser n'importe quel quel autre nom. self joue le même rôle que *this en C++ ou que this en Java, mais self n'est pas un mot-clé réservé.

Cette syntaxe est nécessaire pour éviter tout ambiguité en déclarant de nouvelles classes, telle qu'une classe qui est locale à une méthode à l'intérieur d'une autre classe.

Attributs spéciaux, méthodes et opérateurs

Les attributs de classe, les méthodes et les opérateurs commençant avec deux 'underscore' sont habituellement destinés à être privés (i.e. à être utilisés de manière interne, mais non exposés à l'extérieur de la classe). Même si c'est une convention, ce n'est pas forcé par l'interpréteur.

Certains d'entre eux sont des mots-clés réservés et ont une signification spéciale.

Par exemple, ci-dessous, trois d'entre eux:

  • __len__
  • __getitem__
  • __setitem__

Ils peuvent être utilisés, par exemple, pour créer un objet conteneur qui agit comme une liste:

>>> class MyList(object):
>>>     def __init__(self, *a): self.a = list(a)
>>>     def __len__(self): return len(self.a)
>>>     def __getitem__(self, i): return self.a[i]
>>>     def __setitem__(self, i, j): self.a[i] = j
>>> b = MyList(3, 4, 5)
>>> print b[1]
4
>>> b.a[1] = 7
>>> print b.a
[3, 7, 5]

D'autres opérateurs spéciaux incluent __getattr__ and __setattr__, qui définissent le get et le set des attributs pour la classe, et __sum__ et __sub__, qui surchargent des opérateurs arithmétiques. Pour l'utilisation de ces opérateurs, nous renvoyons le lecteur à des livres avancés sur le sujet. Nous avons déjà mentionné les opérateurs spéciaux __str__ et __repr__.

Entrée/Sortie de fichier

file.read
file.write

En Python vous pouvez ouvrir et écrire dans un fichier avec:

>>> file = open('myfile.txt', 'w')
>>> file.write('hello world')
>>> file.close()

De manière similaire, vous pouvez relire le fichier avec:

>>> file = open('myfile.txt', 'r')
>>> print file.read()
hello world

De manière alternative, vous pouvez lire en mode binaire avec "rb", écrire en mode binaire avec "wb", et ouvrir le fichier en mode d'ajout avec "a", en utilisant la notation standard C.

La commande read prend un argument optionnel, qui est le nombre d'octets. Vous pouvez également aller vers n'importe quel endroit du fichier en utilisant seek,

file.seek

Vous pouvez relire le fichier avec read

>>> print file.seek(6)
>>> print file.read()
world

et vous pouvez fermer le fichier avec:

>>> file.close()

Dans la distribution standard Python, qui est connue comme CPython, les variables sont référencées, incluant ces descripteurs de fichiers, de sorte à ce que CPython sache que lorsque le compteur de référence d'un descripteur de fichier ouvert diminue jusqu'à zéro, le fichier peut être fermé et les variables libérées. Cependant, dans d'autres implémentations de Python telles que PyPy, le garbage collection est utilisé à la place des compteurs de référencement, et ceci signifie qu'il est possible qu'il y ait une accumulation de trop de descripteurs de fichiers en même temps, résultant en une erreur avant que le gc ait une chance de fermer et de tous les libérer. De plus, il est préférable de fermer explicitement les descripteurs de fichier lorsqu'ils ne sont plus nécessaires. web2py fournit deux fonction d'aide : read_file() et write_file() à l'intérieur de l'espace de nom gluon.fileutils qui encapsule l'accès aux fichiers et s'assure que le descripteur de fichier utilisé est correctement fermé.

En utilisant web2py, vous ne savez pas où est le répertoire courant, car il dépend de la manière dont web2py est configuré. LA variable request.folder contient le chemin vers l'application courante. Les chemins peuvent être concaténés avec la commande os.path.join, expliquée ci-dessous.

exec, eval

exec
eval

Contrairement à Java, Python est un vrai langage interprété. Ceci signifie qu'il a la possibilité d'exécuter des traitements Python stockés dans des chaînes de caractères. Par exemple:

>>> a = "print 'hello world'"
>>> exec(a)
'hello world'

Que s'est-il passé ? La fonction exec demande à l'interpréteur de s'appeler lui-même et exécute le contenu de la chaîne passée en argument. Il est également possible d'exécuter le contenu de la chaîne dans un contexte défini par les symboles dans un dictionnaire:

>>> a = "print b"
>>> c = dict(b=3)
>>> exec(a, {}, c)
3

Ici, l'interpréteur, en exécutant la chaîne a, voit les symboles définis dans c (b dans l'exemple), mais ne voit pas c ou a eux-mêmes. C'est différent d'un environnement restreint, puisque exec ne limite pas ce que le code interne peut faire; il définit juste l'ensemble de vraiables visibles pour le code. Une fonction similaire est la fonction eval, qui fonctionne vraiment comme exec excepté le fait qu'il s'attende à ce que l'argument soit évalué comme une valeur, et retourne cette valeur.

>>> a = "3*4"
>>> b = eval(a)
>>> print b
12

import

import
random
La vraie puissance de Python est dans ses modules et librairies. Ils fournissent un ensemble large et complet d'API (Application Programming Interfaces) à beaucoup de librairies système (souvent indépendamment du système d'exploitation utilisé).

Par exemple, si vous avez besoin d'utiliser un générateur de nombre aléatoire, vous pouvez faire:

>>> import random
>>> print random.randint(0, 9)
5

Ceci imprime un entier aléatoire compris entre 0 et 9 (incluant 9), 5 dans l'exemple. La fonction randint est définie dans le module random. Il est également possible d'importer un objet depuis module dans l'espace de nom courant.

>>> from random import randint
>>> print randint(0, 9)

ou d'importer tous les objets depuis un module dans l'espace de nom courant:

>>> from random import *
>>> print randint(0, 9)

ou de tout importer dans un nouvel espace de nom à définir:

>>> import random as myrand
>>> print myrand.randint(0, 9)

Dans le reste de ce livre, nous utiliserons principalement des objets définis dans les modules os, sys, datetime, time et cPickle?

Tous les objets web2py sont accessibles via un module appelé gluon, et c'est le sujet des chapitres suivants. De manière interne, web2py utilise de nombreux modules Python (par exemple thread), mais vous aurez rarement besoin d'y accéder directement.

Dans les sous-sections suivantes nous considérons que ces modules comme les plus utiles.

os

os
os.path.join
os.unlink

Ce module fournit une interface à l'API du système d'exploitation. Par exemple:

>>> import os
>>> os.chdir('..')
>>> os.unlink('filename_to_be_deleted')
Certaines des fonctions os, telles que chdir, NE DOIVENT PAS être utilisées dans web2py car elles ne sont pas "thread-safe".

os.path.join est très utile; il permet la concaténation de chemins indépendamment du système d'exploitation utilisé:

>>> import os
>>> a = os.path.join('path', 'sub_path')
>>> print a
path/sub_path

Les variables d'environnement systèmes peuvent être accessibles via:

>>> print os.environ

qui est un dictionnaire en lecture seule.

sys

sys
sys.path

Le module sys contient de nombreuses variables et fonctions, mais celle qui est la plus utilisée est sys.path. Il contient une liste de chemins où Python cherche les modules. Lorsque nous essayons d'importer un module, Python cherche dans tous les dossiers listés dans sys.path. Si vous installez des modules additionels dans différents endroits et que vous souhaitez que Python puisse les trouver, vous devrez ajouter le nouveau chemin dans sys.path.

>>> import sys
>>> sys.path.append('path/to/my/modules')

Lorsque web2py fonctionne, Python reste en mémoire, et il n'y a qu'un seul sys.path, alors qu'il y a de nombreux threads pour les requêtes HTTP. Pour éviter une fuite de mémoire, il est mieux de vérifier si le chemin est déjà présent avant de l'ajouter:

>>> path = 'path/to/my/modules'
>>> if not path in sys.path:
        sys.path.append(path)

datetime

date
datetime
time

L'utilisation du module datetime est mieux illustrée avec quelques exemples:

>>> import datetime
>>> print datetime.datetime.today()
2008-07-04 14:03:90
>>> print datetime.date.today()
2008-07-04

Parfois vous pouvez avoir besoin de marquer des données en utilisant l'heure UTC plutôt que l'heure locale. Dans ce cas, vous pouvez utiliser la fonction suivante:

>>> import datetime
>>> print datetime.datetime.utcnow()
2008-07-04 14:03:90

Le module datetime contient des classes diverses: date, datetime, time et timedelta. La différence entre deux dates ou deux datetime ou deux objets time est un timedelta:

>>> a = datetime.datetime(2008, 1, 1, 20, 30)
>>> b = datetime.datetime(2008, 1, 2, 20, 30)
>>> c = b - a
>>> print c.days
1

Dans web2py, date et datetime sont utilisés pour stocker les entrées SQL correspondantes quand ils sont passés ou retournés par la base de données.

time

time

Le module time diffère de date et de datetime car il représente le temps en secondes depuis "l'epoch" (en 1970).

>>> import time
>>> t = time.time()
1215138737.571

Vous pouvez vous référer à la documentation Python pour les fonctions de conversion entre temps en secondes et temps en datetime.

cPickle

cPickle

C'est un module très puissant. Il fournit des fonctions qui peuvent sérialiser presque n'importe quel objet Python. Par exemple, construisons un objet étrange:

>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.x = 'something'
>>> a = [1 ,2, {'hello':'world'}, [3, 4, [myinstance]]]

et maintenant:

>>> import cPickle
>>> b = cPickle.dumps(a)
>>> c = cPickle.loads(b)

Dans cet exemple, b est une représentation par chaîne de a, et c est une copie de a générée en dé-sérialisant b.

cPickle peut également sérialiser vers et dé-sérialiser depuis un fichier:

>>> cPickle.dump(a, open('myfile.pickle', 'wb'))
>>> c = cPickle.load(open('myfile.pickle', 'rb'))
 top