Archives de catégorie : DebuterEnPython

OutillageTests

[wiki: Tests]

PageOutline

#!comment
A REGARDER DE PRES :
http://pytest.org/latest/
http://sametmax.com/un-gros-guide-bien-gras-sur-les-tests-unitaires-en-python-partie-1/
http://sametmax.com/un-gros-guide-bien-gras-sur-les-tests-unitaires-en-python-partie-2/
http://sametmax.com/un-gros-guide-bien-gras-sur-les-tests-unitaires-en-python-partie-3/

Tester un module

Une pratique courante consiste à placer en fin de module des instructions de test, à la suite d’une instruction if name == "main": . En effet, si le module est importé, name contient le nom du module et les instructions ne sont pas exécutées. Par contre, si le module est exécuté en tant que programme principal, name est bien égal à main, et les tests sont exécutés.

Exemple :

#!python


def minmax(test,*args):
res = args[0]
for arg in args[1:]
if test(arg, res):
res = arg
return res

def lessthan(x,y): return x < y
def grtrthan(x,y): return x > y

if name == "main":
print minmax(lessthan,4,2,1,5,6,3)
print minmax(grtrthan,4,2,1,5,6,3)


doctest

Le module doctest recherche des morceaux de texte ressembant à des sessions interactives, et les exécute pour s’assurer que le résultat affiché est conforme aux attentes.

Exemple :

#!python


def minmax(test,*args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res

def lessthan(x,y):
"""
>>> minmax(lessthan,4,2,1,5,6,3)
1
"""
return x < y

def grtrthan(x,y):
"""
>>> minmax(grtrthan,4,2,1,5,6,3)
6
"""
return x > y


Pour exécuter les tests :

python -m doctest -v <monfichier>

!PyUnit

Etapes standards :
1. Définir une classe héritant de unittest.TestCase.
2. Y définir des méthodes dont le nom commence par test_.
3. Placer un appel à unittest.main() dans le fichier à tester, généralement à la fin.

Exemple :

#!python
import unittest


def minmax(test,*args):
res = args[0]
for arg in args[1:]
if test(arg, res):
res = arg
return res

def lessthan(x,y): return x < y
def grtrthan(x,y): return x > y

class TestMinMax(unittest.TestCase):

def setUp(self):
pass

def test_lessthan(self):
self.assertEqual( minmax(lessthan,4,2,1,5,6,3), 1)

def test_grtrthan(self):
self.assertEqual( minmax(grtrthan,4,2,1,5,6,3), 6)

if name == 'main':
unittest.main()


Nose

#!comment


A mature, programmable text editor that you can learn once for all your
text editing needs, and that already has excellent Python support, like
GNU Emacs or Vim.

A refactoring tool that works with your editor, like ‘bicyclerepair’
<URL:http://bicyclerepair.sourceforge.net/> or ‘rope’
<URL:http://rope.sourceforge.net/>.

A good unit test framework (sorry, ‘unittest’, you're a good start but
not very Pythonic) like ‘nose’ (which builds incrementally on
‘unittest’) <URL:http://somethingaboutorange.com/mrl/projects/nose/>.

A test double library (sometimes called a “mock object” library), my
favourite being MiniMock <URL:http://pypi.python.org/pypi/MiniMock>.

Sphinx <URL:http://sphinx.pocoo.org/> for extracting reStructuredText
documentation from your code and rendering it to various formats.

A development-environment isolation tool, like ‘virtualenv’
<URL:http://pypi.python.org/pypi/virtualenv>.

A project build scripting tool which integrates with many of the above
tools, like Paver <URL:http://www.blueskyonmars.com/projects/paver/>.


Références

LangageObjetsComplements

[wiki: Orientation objets : compléments] [#license ©]

PageOutline

#!comment
Les types prédéfinis sont des classes ?
Les littéraux des objets ?


QUID DES METACLASSES ?

QUID DES CLASSES ABSTRAITES ? (http://legacy.python.org/dev/peps/pep-3119/)
from collections import Iterable
isinstance('ab', Iterable)
isinstance([1, 2], Iterable)
# comme on l'a vu un objet qui a une methode iter()
# et une next() est considere comme un iterable
class Foo():
def iter(self):
return self
def next(self):
# ceci naturellement est bidon
return

foo = Foo()
isinstance(foo, Iterable)

MRO : il y a des cas insolubles, rejetés par l'nterpréteur :
# en version code
class X(object): pass
class Y(object): pass
class XY(X, Y): pass
class YX(Y, X): pass

# on essaie de créer une sous-classe de XY et YX
try:
class Class(XY,YX): pass
# mais ce n'est pas possible
except Exception as e:
print "On ne peut pas créer Class"
print e


Attributs cachés

Dans un objet, les attributs ordinaires sont stockés dans un attribut spécial nommé dict. Chaque instance possède également un attribut nommé class qui pointe vers sa classe.

Dans une classe, on trouve également des attributs spéciaux :

  • doc : documentation de la classe,
  • dict : attributs de la classe,
  • name : nom de la classe,
  • bases : tuple contenant les classes de bases de la classe courante.
  • mro : ordre de parcours des ancêtres de la classe.
  • module : nom du module où la classe est définie (main en mode interactif).
#!div style="width:600px"


{{{
#!python
class A1 : pass
class A2 : pass

class B(A1,A2):
def init(self,value): self.data = value
def display(self) : print("data:",self.data)

b = B("abc")

print(b.dict)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
{'data': 'abc'}
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print(b.dict['data'])


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
abc
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print((b.class, ))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(<class main.B at 0x0000000004206588>,)
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print(b.class.name)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print(b.class.bases)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(<class main.A1 at 0x0000000004180EE8>, <class main.A2 at 0x00000000042065E8>)
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print(b.class.dict)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
{'module': 'main', 'display': <function display at 0x000000000422A588>, 'init': <function init at 0x000000000422A5F8>, 'doc': None}
</pre>

}}}

Attributs pseudo-privés

Au sein d’une instruction composée class, tous les noms qui sont préfixés par un double souligné __ (et non à la fin), sont « magiquement » manipulés par l’interpréteur Python qui insère devant le nom un simple souligné _ et le nom de la classe courante. Ainsi, les attributs recoivent un nom qui devient spécifique à la classe, et qui ne risque plus d’être redéfini par erreur dans les classes dérivées. On parle d’attributs « pseudo-privés », non pas parce que l’accès à ces attributs est véritablement interdit, mais parce que l’altération automatique de leurs noms complique leur accès de l’extérieur.

#!div style="width:600px"


{{{
#!python
class Demo:
public_data = "public data"
__private_data = "private data"
def public_method(self): print("public method()")
def __private_method(self): print("private method()")

dir(Demo)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['_Demo__private_data',
 '_Demo__private_method',
 'doc',
 'module',
 'public_data',
 'public_method']
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print(Demo.public_data)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
public data
</pre>

}}}

#!div style="width:600px"


{{{
#!python
print(Demo.__private_data)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-9-5a651a2f66b8> in <module>()
----> 1(print(Demo.__private_data)


AttributeError: class Demo has no attribute '__private_data'
</pre>


}}}

#!div style="width:600px"


{{{
#!python
print(Demo._Demo__private_data)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
private data
</pre>

}}}

#!div style="width:600px"


{{{
#!python
d = Demo()
d.public_method()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
public method()
</pre>

}}}

#!div style="width:600px"


{{{
#!python
d.__private_method()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-24-f98a2e3c7104> in <module>()
----> 1 d.__private_method()


AttributeError: Demo instance has no attribute '__private_method'
</pre>


}}}

#!div style="width:600px"


{{{
#!python
d._Demo__private_method()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
private method()
</pre>

}}}

A la différence d’autres langages,toutes les données d’un objet réside dans l’objet lui-même, quelle que soit la classe et la méthode à l’origine de ces données. Cela peut générer des interactions involontaires, en particulier dans le cas d’un héritage multiple venant de classes issues de développeurs indépendants. Les attributs pseudo-privés évitent ces interactions non voulues.)

#!div style="width:600px"


{{{
#!python
class C1:
def set1(self): self.__X = 1
def get1(self): return self.__X

class C2:
def set2(self): self.__X = 2
def get2(self): return self.__X

class C3(C1,C2): pass

obj = C3()
obj.set1()
obj.set2()
print(obj.get1(),obj.get2())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
1 2
</pre>

}}}

Propriétés

On peut définir dans une classe des attributs particuliers de type « propriété » (property). Ces propriétés ressemblent à des variables membres, et s’utilisent comme des variables membres, mais en réalité le fait de lire une propriété déclenche l’utilisation du « getter » qui lui est attaché, et le fait d’affecter une nouvelle valeur à cette propriété déclenche l’utilisation du « setter » attaché (si et seulement si il est défini). Typiquement, « getter » et « setter » manipulent une variable interne pseudo-privée.

Ci-dessous, nous définissons pour la classe Vector des propriétés x et y, dont la lecture déclenche un appel à getx() ou gety(), qui renvoient les valeurs des variables pseudo-privées x ou y des instances de Vector. Seule la propriété x a été associée à un « setter ». y, en l’absence de « setter », est en lecture seule : on ne peut pas lui affecter de valeur (par contre on peut le faire sur __y via init()).

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def getx(self):
return self.__x
def setx(self,u):
self.__x = u
x = property(getx,setx)
def gety(self):
return self.__y
y = property(gety)

v = Vecteur()
v.init(3, -4)
print(v.x,v.y)
v.x = 2
print(v.x)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3 -4
2
</pre>

}}}

#!div style="width:600px"


{{{
#!python
v.y = 5


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-29-916c9ff1d97a> in <module>()
----> 1 v.y = 5


AttributeError: can't set attribute
</pre>


}}}

Ce qui est remarquable ici, c’est que Python ne force pas une encapsulation précoce et inutile. On peut très bien développer une première version de classe avec des attributs publics, avoir des clients, puis décider ultérieurement d’en faire des propriétés, si le besoin s’en fait sentir.

On a vu aussi, dans notre exemple, qu’il est facile de faire des propriétés « read-only » en ne déclarant que les « getters ». Il est cependant plus classique de fournir à la fois la méthode de lecture et d’écriture, notamment pour controler qu’on n’enregistre que des valeurs valides dans une propriété. Par exemple, si nos vecteurs ne peuvent contenir que des valeurs entre -1 et 1 :

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.x = u
self.y = v
def getx(self):
return self.__x
def setx(self,x):
if (x<-1): self.__x = -1
elif (x>1): self.__x = 1
else: self.__x = x
x = property(getx,setx)
def gety(self):
return self.__y
def sety(self,y):
if (y<-1): self.__y = -1
elif (y>1): self.__y = 1
else: self.__y = y
y = property(gety,sety)

v = Vecteur()
v.init(3, -4)
print(v.x,v.y)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
1 -1
</pre>

}}}

Variables de classe

On peut ajouter des données à une classe (et pas seulement des méthodes), auquel cas elles sont en quelque sorte partagées et visibles pour toutes les instances de la classe.

En effet, quand vous demandez à lire la donnée nommée x de l’instance obj, ce qui se note obj.x, l’interpréteur Python cherche d’abord le nom dans l’instance elle-même, puis à défaut dans la classe, puis dans ses ancêtres, comme pour n’importe quel attribut.

Dans l’exemple ci-dessous, on dote la classe Vecteur de variables x et y qui contiennent des valeurs par défaut pour les instances. On peut créer ces variables directement lors de la création de classe, comme nous le faisons pour x, ou l’ajouter ultérieurement, comme nous le faisons pour y.

#!div style="width:600px"


{{{
#!python
class Vecteur:
x = 0

Vecteur.y = 0

v = Vecteur()

print(Vecteur.x,Vecteur.y)
print(v.x,v.y)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
0 0
0 0
</pre>

}}}

Par contre, si vous affectez une nouvelle valeur à obj.x, et que ce nom n’existe pas encore dans l’instance, un nouvel attribut est créé à cette occasion, dans l’instance. Formulons le à nouveau : en cas de lecture, l’interpréteur cherche l’attribut dans l’instance concernée, puis dans sa classe et ses ancêtres, mais en cas d’affectation d’une nouvelle valeur, l’attribut est ajouté à l’instance si il n’existe pas déjà.

#!div style="width:600px"


{{{
#!python
v.x, v.y = 10, 20

print(Vecteur.x,Vecteur.y)
print(v.x,v.y)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
0 0
10 20
</pre>

}}}

Un langage très dynamique

Les instructions class et les instructions def, sont traitées à l’exécution comme n’importe quelles instructions. Elles retournent des objets, certes un peu spéciaux, et leur assigne un nom dans l’espace de nom courant. Avec ces objets, on peut réaliser des manipulations impossibles dans d’autres langages, comme par exemple créer une méthode d’abord en tant que fonction, à l’extérieur de la classe, puis la rattacher à posteriori.

#!div style="width:600px"


{{{
#!python
class A:
def init(self,value): self.data = value
def display(self) : print("data:",self.data)

a = A('bonjour')

def my_display_upper(self):
print("data:",self.data.upper())

A.display_upper = my_display_upper

a.display_upper()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
data: BONJOUR
</pre>

}}}

Une méthode peut s’invoquer à travers le nom de classe, mais dans ce cas il ne faut pas oublier de redonner le nom de l’objet à traiter comme premier argument (self) :

#!div style="width:600px"


{{{
#!python
A.display_upper(a)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
data: BONJOUR
</pre>

}}}

Je peux toujours aussi appeler directement la fonction à travers son nom original (my_display_upper) :

#!div style="width:600px"


{{{
#!python
my_display_upper(a)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
data: BONJOUR
</pre>

}}}

On peut même s’amuser, comme ci-dessous, à décrocher la fonction de la classe (en effacant le nom), et à l’accrocher directement à l’instance. Mais dans ce cas, la définition automatique de self n’est pas réalisée (ce mécanisme n’est actif que pour les fonctions attachées dans des classes). On doit alors redonner a comme argument à l’appel de fonction.

#!div style="width:600px"


{{{
#!python
del A.display_upper

a.display_upper = my_display_upper

a.display_upper(a)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
data: BONJOUR
</pre>

}}}

Les encoches

La définition dans une classe d’un attribut spécial nommé slots permet de limiter la liste des attributs autorisés (sans pour autant les créer). Cela permet d’éviter des erreurs de frappe involontaire, de faire certaines optimisations, mais en contre-partie cela peut affecter l’existence de l’attribut spécial dict, et perturber le fonctionnement d’outils génériques qui s’appuient sur ce dictionnaire interne.

#!div style="width:600px"


{{{
#!python
class DemoSlots:
slots = ['att1','att2']

ds = DemoSlots()
print(ds.att1)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-26-c8617deaf333> in <module>()
      3 
      4 ds = DemoSlots()
----> 5 print(ds.att1)


AttributeError: att1
</pre>


}}}

#!div style="width:600px"


{{{
#!python
ds.att1 = "this is att1"
print(ds.att1)
this is att1
In [40]:

ds.att3 = "this is att3"


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-40-3f4ad9ce7b2c> in <module>()
----> 1 ds.att3 = "this is att3"


AttributeError: 'DemoSlots' object has no attribute 'att3'
</pre>


}}}

#!div style="width:600px"


{{{
#!python
class DerivedSlots(DemoSlots): pass
ds = DerivedSlots()
ds.att3 = "this is att3"


}}}

Méthodes statiques et méthodes de classe

#!comment
QUE SE PASSE T IL EN CAS D HERITAGE ET DE REDEFINITION D UNE METHODE ?

Imaginons que l’on veuille compter le nombre d’instances créées par une classe. Pour récupérer le nombre courant d’instances,on peut écrire une méthode qui ne se sert que de l’attribut de classe, et cette méthode devrait pouvoir être appelée via le nom de classe, sans passer par une instance, mais cela ne fonctionne pas à l’aide d’une implémentation « naive » telle que celle-ci 🙂

#!div style="width:600px"


{{{
#!python
class DemoComptage:
__nb_objets = 0
def init(self):
DemoComptage.nb_objets = DemoComptage.nb_objets+1
def nb_objets():
return __nb_objets

a = DemoComptage()
b = DemoComptage()
c = DemoComptage()

print(DemoComptage.nb_objets())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-225b71655857> in <module>()
     10 c = DemoComptage()
     11 
---> 12 print(DemoComptage.nb_objets())


TypeError: unbound method nb_objets() must be called with DemoComptage instance as first argument (got nothing instead)
</pre>


}}}

En effet, même si self n’est pas utilisé dans le corps d’une méthode, l’interpréteur Python exige qu’une méthode soit invoquée à travers une instance. L’attribut nb_objets de la classe est évidemment accessible à tous, donc il peut être lu directement par les clients, mais si on tient à préserver l’encapsulation, une simple fonction extérieure peut faire l’affaire 🙂

#!div style="width:600px"


{{{
#!python
class DemoComptage:
nb_objets = 0
def init(self):
DemoComptage.nb_objets = DemoComptage.nb_objets+1

def nb_objets():
return DemoComptage.nb_objets

a = DemoComptage()
b = DemoComptage()
c = DemoComptage()

print(nb_objets()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3
</pre>

}}}

Cependant,pour satisfaire les programmeurs qui tiennent à localiser la fonction au sein de la classe, depuis Python 2.2, on peut définir des méthodes dites « statiques », qui peuvent s’invoquer sans passer par une instance 🙂

#!div style="width:600px"


{{{
#!python
class DemoComptage:
_nb_objets = 0
def init(self):
DemoComptage._nb_objets = DemoComptage._nb_objets+1
def nb_objets():
return DemoComptage._nb_objets
nb_objets = staticmethod(nb_objets)

a = DemoComptage()
b = DemoComptage()
c = DemoComptage()

print(DemoComptage.nb_objets()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3
</pre>

}}}

Il existe également des méthodes dites « de classe »,qui recoivent en premier argument non pas l’instance courante, mais la classe courante.)

#!div style="width:600px"


{{{
#!python
class DemoClassMethod:
def m(cls,data):
print(cls,data)
m = classmethod(m)

DemoClassMethod.m("bonjour")


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<class 'main.DemoClassMethod'> bonjour
</pre>

}}}

Décorateurs de fonctions

Les méthodes statiques et les méthodes de classes sont des cas particuliers de « décorateurs » de fonction.

Un décorateur est une fonction qui manipule une fonction. Il peut effectuer une action une fois pour toute, et renvoyer la fonction originale, ou bien renvoyer une nouvelle fonction qui effectuera des manipulations à chaque appel, avant de le répercuter à la fonction originale qui aura été mémorisée en interne.

Par exemple, lors de l’instruction nb_objets = staticmethod(nb_objets), on appelle le décorateur staticmethod, qui substitue à la fonction nb_objets() originale une autre fonction, dont le rôle sera, à chaque appel, de mettre de côté le premier argument (self) et de transmettre les autres à la fonction nb_objets() originale.

On peut maintenant définir plus facilement une décoration, à l’aide du caractère @ :

#!div style="width:600px"


{{{
#!python
class DemoDeco:
_nb_objets = 0
def init(self):
DemoDeco._nb_objets += 1
@staticmethod
def nb_objets():
return DemoDeco._nb_objets
@classmethod
def m(cls,data):
print(cls,data)

a = DemoDeco()
b = DemoDeco()
c = DemoDeco()

print(DemoDeco.nb_objets()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3
</pre>

}}}

#!div style="width:600px"


{{{
#!python
DemoDeco.m("bonjour")


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<class 'main.DemoDeco'> bonjour
</pre>

}}}

On peut empiler autant de décorateurs que souhaités. Le code ci-dessous :

#!div style="width:600px"


{{{
#!python
@A @B @C
def f():
...


}}}

est l’équivalent de :

#!div style="width:600px"


{{{
#!python
def f():
...
f = A(B(C(f)))


}}}

On peut bien sur écrire ses propres décorateurs. On les implémente en général à l’aide d’une classe qui stocke la fonction décorée,et définit l’opérateur d’appel call. Par exemple, ci-dessous, une classe qui compte les appels à la fonction décorée 🙂

#!div style="width:600px"


{{{
#!python
class compteur:
def init(self,func):
self.func = func
self.count = 0
def call(self,*args):
self.count += 1
print('call {} to {}'.format(self.count,self.func.name))
self.func(*args)

@compteur
def bidon(texte):
print(texte)

bidon("bonjour")


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
call 1 to bidon
bonjour
</pre>

}}}

#!div style="width:600px"


{{{
#!python
bidon("bonsoir")


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
call 2 to bidon
bonsoir
</pre>

}}}

Enfin, notons qu’il existe des décorateurs permettant de simplifier la définition des propriétés d’une classe (attributs dont l’accès en lecture et/ou en écriture est confié à des méthodes). L’exemple de vecteur précédemment vu peut-être réécrit ainsi :

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.x = u
self.y = v
@property
def x(self):
return self.__x
@x.setter
def x(self,u):
if (u<-1): self.__x = -1
elif (u>1): self.__x = 1
else: self.__x = u
@property
def y(self):
return self.__y
@y.setter
def y(self,v):
if (v<-1): self.__y = -1
elif (v>1): self.__y = 1
else: self.__y = v

v = Vecteur()
v.init(3, -4)
print(v.x,v.y)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
1 -1
</pre>

}}}

Hériter d’un type prédéfini

On peut hériter des types prédéfinis, comme de n’importe quelle autre classe. ATTENTION, dans l’exemple ci-dessous, comme on hérite d’une classe « non-modifiable » (« immutable »), on ne peut pas redéfinir init, qui intervient après a création de l’instance ; on est obligé de passer par une redéfinition de new.

#!div style="width:600px"


{{{
#!python
class FloatWithUnit(float):
def new(cls,value,unit):
instance = float.new(cls,value)
instance.unit = unit
return instance
def str(self):
return float.str(self)+" "+self.unit
def mul(self,other):
return FloatWithUnit(float.mul(self,other),self.unit+"*"+other.unit)

largeur = FloatWithUnit(2,"cm")
longueur = FloatWithUnit(5,"cm")
print(largeur*longueur)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
10.0 cm*cm
</pre>

}}}

Les classes de style ancien

Toutes les explications de cette formation concerne les classes de nouveau style, apparue avec 2.2, et qu’il faut utiliser à chaque fois que c’est possible. Comment savoir si une classe est d’ancien ou nouveau style ?

  • avant 2.2 : toutes les classes sont de stype ancien
  • de 2.2 à 2.x : les classes qui ont la classe object parmi leurs ancêtres sont de nouveau style.
  • à partir de 3 : toutes les classes sont de nouveau style

Un grand nombre de fonctionnalités avancées ne sont disponibles que pour les classes de nouveau style : super(), propriétés, encoches, méthodes statiques, décorateurs, héritage d’un type prédéfini…

Par ailleurs, en cas d’héritage en losange, les comportements son subtilement différents. Pour les classes de nouveau style, la recherche d’attribut se fait en profondeur d’abord, puis de gauche à droite, à une exception près : les classes dérivées sont toujours explorées avant leurs classes de base. Cette exception n’avait pas cours pour les classes de style ancien :

#!div style="width:600px"


{{{
#!python
class A: x = "A"
class B1(A): pass
class B2(A): x = "B2"
class C(B1,B2): pass

print(C.x)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
A
</pre>

}}}

En cas de nouveau style :

#!div style="width:600px"


{{{
#!python
class A: x = "A"
class B1(A): pass
class B2(A): x = "B2"
class C(B1,B2): pass

print(C.x)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B2
</pre>

}}}

Pour éviter toute ambiguité, quel que soit le style de classe, on peut dire explicitement quelle est la méthode à utiliser :

#!div style="width:600px"


{{{
#!python
class A: x = "A"
class B1(A): pass
class B2(A): x = "B2"
class C(B1,B2): x = B2.x

print(C.x)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B2
</pre>

}}}

Itérateurs

Aujourd’hui, avant de rechercher une méthode getitem, l’interpréteur Python cherche d’abord une méthode iter, qui peut implémenter des schémas d’itération plus complexes. La méthode iter est supposée retourné un objet « itérateur », sur lequel on va ensuite appeler la méthode next, qui renvoit un nouvel élément à chaque appel, et lève une exception StopIteration lorsqu’il n’y a plus d’éléments.

#!div style="width:600px"


{{{
#!python
class ReverseIterator:
def init(self, seq):
self.seq = seq
self.index = len(seq.data)
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.seq.data[self.index]

class Reverse:
def init(self, data):
self.data = data
def iter(self):
return ReverseIterator(self)

for char in Reverse('123'):
print(char)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3
2
1
</pre>

}}}

Notre classe peut servir elle aussi dans tous les contextes d’itération.

#!div style="width:600px"


{{{
#!python
inverse = Reverse('123')
[c for c in inverse]


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['3', '2', '1']
</pre>

}}}

#!div style="width:600px"


{{{
#!python
[c for c in inverse]


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['3', '2', '1']
</pre>

}}}

#!div style="width:600px"


{{{
#!python
[c+d for c in inverse for d in inverse]


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['33', '32', '31', '23', '22', '21', '13', '12', '11']
</pre>

}}}

Pour éviter d’avoir à écrire deux classes, on peut être tenté de dire qu’objet est son propre itérateur, et de placer la méthode next() dans la classe d’origine (exemple ci-dessous). Dans la pratique, c’est peu utile, car l’objet ne peut être itéré qu’une seule fois. On peut alors être tenté d’ajouter une méthode raz() qui remettrait l’index à 0, mais avec encore une limite : impossible de lancer deux itérations simultanées. Ne rusez pas : faites deux classes.

#!div style="width:600px"


{{{
#!python
class Reverse:
def init(self, data):
self.__data = data
self.__index = len(data)
def iter(self):
return self
def next(self):
if self.__index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]

inverse = Reverse('123')
print([c for c in inverse])
print([c for c in inverse])


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['3', '2', '1']
[]
</pre>

}}}

#!div style="width:600px"


{{{
#!python
class Reverse:
def init(self, data):
self.__data = data
self.__index = len(data)
def iter(self):
return self
def next(self):
if self.__index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
def raz(self):
self.index = len(self.data)

inverse = Reverse('123')
print([c for c in inverse])
inverse.raz()
print([c for c in inverse])
inverse.raz()
print([c+d for c in inverse for d in inverse])


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['3', '2', '1']
['3', '2', '1']
['32', '31']
</pre>

}}}

#!comment


Exercice

Écrire une classe qui permettra de gérer un fichier (dont le nom sera passé au constructeur). Elle devra comporter plusieurs méthodes qui réaliseront des opérations sur le fichier :

  • count : renvoie le nombre de lignes, de mots et de lettres.
  • head(n) : renvoie les n premières lignes.
  • tail(n) : renvoie les n dernières lignes.
  • find(str) : afficher toutes les lignes contenant la chaine str.
  • replace(str1, str2) : afficher à l’écran toutes les lignes du fichier, en remplaçant chaque occurrence de str1 par str2.
  • tout ce qui pourra vous passer par la tête (mais qui ne modifie pas le fichier lui-même).

Il faudra veiller à ne pas charger en mémoire tout le contenu du fichier, ni à le conserver ouvert trop longtemps.

Travaux Pratiques

1. Dotez la classe Expression d’une variable PI. Modifiez Expression.init() afin qu’elle reconnaisse la valeur PI, et essayez d’évaluer l’expression 2*PI avec votre calculatrice.
1. Définir Expression.slots et controllez son bon fonctionnement.
1. Protéger la valeur PI, supposée constante, en la rendant pseudo-privée et en fournissant une méthode statique de récupération de la valeur. Utilisez la notation « décorateur » pour cette méthode.
1. A la place de la méthode Calculatrice.getres(), ajouter une propriété res, accessible en lecture seule.

#!comment
1. Observez ce qui se passe si la classe Calculatrice n'hérite plus de la classe object.

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments

LangageMethodesSpeciales

[wiki: Méthodes spéciales] [#license ©]

PageOutline

#!comment


A traiter :
nonzero


Constructeur et destructeur

Le constructeur est une méthode spéciale, appelée init(), qui est appelée lors de la création d’une instance de classe.

Le destructeur est une méthode spéciale, appelée del(), qui est appelée lors de la destruction d’une instance. ATTENTION : on ne contrôle pas vraiment le moment ou le destructeur est appelé. De ce fait, il est rarement utilisé.

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def getx(self):
return self.__x
def gety(self):
return self.__y
def norm(self):
x2 = self.__x**2
y2 = self.__y**2
return (x2+ y2)**(1/2)

v1 = Vecteur(3, 4)
print(v1.getx(),v1.gety())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3 4
</pre>
#!python
v2 = Vecteur(-2, 7)
print(v2.getx(),v2.gety())
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
-2 7
</pre>

}}}

Affichage

Par défaut, l’affichage d’une instance renvoie son nom de classe et son adresse en mémoire :

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v

v = Vecteur(3,4)

print(v)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<main.Vecteur object at ...>
</pre>

}}}

On peut personnaliser cet affichage en s’appuyant sur les méthodes spéciales str() et repr() :

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def str(self):
return "({:.0f},{:.0f})".format(self.x,self.y)
def repr(self):
return "Vecteur({:.0f},{:.0f})".format(self.x,self.y)

v = Vecteur(3,4)

print(v)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(3,4)
</pre>
#!python
v
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
Vecteur(3,4)
</pre>

}}}

En cas d’affichage destiné à l’utilisateur, tel qu’un appel à print(), c’est la méthode str() qui est recherchée en priorité, et seulement en dernier recours la méthode repr(). En principe, cette dernière est supposée retourner un texte qui, si il était exécuté, recréerait l’objet original.

Objets-fonctions

En dotant une classe de la méthode call, les instances de cette classe peuvent être utilisées comme des fonctions. On parle d’objets-fonctions. Cela peut-être utile lorsqu’on veut utiliser un programme générique qui attend une fonction en paramètre, et que l’on veut lui donner à la place des objets qui savent se comporter en fonction, tout en ayant des paramètres internes.

#!div style="width:600px"


{{{
#!python
class FonctionLineaire:
def init(self,constant):
self.constant = constant
def call(self,other):
return self.constant*other

fois2 = FonctionLineaire(2)
print(fois2(6))
print(map(fois2,[1,2,3,4,5]))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
12
[2, 4, 6, 8, 10]</pre>

}}}

Ce mécanisme est souvent le plus sain pour associer des données persistantes à une fonction, plutôt que l’utilisation de variables globales, de fonctions imbriquées, ou de valeurs par défaut modifiables.

Opérateurs mathématiques

Il est possible de permettre l’utilisation des opérateurs habituels avec les nouvelles classes, en redéfinissant des méthodes spéciales au nom prédéfini :

  • + : add(self,other)
  • - : sub(self,other)
  • * : mul(self,other)
  • / : truediv(self,other)
  • // : floordiv(self,other)
  • % : mod(self,other)
  • ** : pow(self,other)
#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def add(self, other):
if isinstance(other, Vecteur):
newx = self.x + other.x
newy = self.y + other.y
return Vecteur(newx, newy)
def str(self):
return "({:.0f},{:.0f}".format(self.x,self.y)

v1 = Vecteur(2,3)
v2 = Vecteur(-4,7)
v3 = v1 + v2

print(v3)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(-2,10)
</pre>

}}}

Index de séquence

En dotant une classe de la méthode getitem(), on peut utiliser ses instances avec toutes les formes de [] :

#!div style="width:600px"


{{{
#!python
class MonTexte:
def init(self,value):
self.data = value
def getitem(self,index):
return self.data[index]

txt = MonTexte("Bonjour")
print(txt[2])


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
n
</pre>
#!python
print(txt[-1::-1])
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
ruojnoB
</pre>

}}}

En bonus, la présence de getitem() permet d’utiliser un objet au sein d’une boucle for. L’interpréteur va invoquer cette méthode avec des valeurs entières croissantes, en commencant par 0, et jusqu’à l’émission d’une exception IndexError.

#!div style="width:600px"


{{{
#!python
for c in txt:
print(c)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B
o
n
j
o
u
r
</pre>

}}}

En réalité, la présence de getitem() permet d’utiliser un objet dans de multiples autres « contextes d’itération », tels que qu’un test d’appartenance in, une « compréhension de liste », la fonction prédéfinie map, l’assignation de séquences, …

#!div style="width:600px"


{{{
#!python
'i' in txt


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False
</pre>
#!python
[c for c in txt]
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['B', 'o', 'n', 'j', 'o', 'u', 'r']
</pre>
#!python
map(ord,txt)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
[66, 111, 110, 106, 111, 117, 114]
</pre>
#!python
(c1, c2, c3, c4, c5, c6, c7) = txt
c3, c2, c1
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
('n', 'o', 'B')
</pre>
#!python
list(txt), tuple(txt), '-'.join(txt)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(['B', 'o', 'n', 'j', 'o', 'u', 'r'],
 ('B', 'o', 'n', 'j', 'o', 'u', 'r'),
 'B-o-n-j-o-u-r')
</pre>

}}}

#!comment


Itérateurs

  • Un objet peut être utilisé comme itérateur si sa classe définit :
  • iter(self)
  • next(self) # En python 3 : next

{{{
#!div style="width:600px"

{{{
#!python
>>> class Reverse:
... def init(self, data):
... self.data = data
... self.index = len(data)
... def iter(self):
... return self
... def next(self):
... if self.index == 0:
... raise StopIteration
... self.index = self.index - 1
... return self.data[self.index]

>>> for char in Reverse('Jour'):
... print(char)
r
u
o
J


}}}

}}}

Travaux Pratiques

1. Dotez la classe Expression d’un constructeur à partir d’une chaîne de caractère, (et y fondre la fonction analyse()).
1. Transformez la méthode display() en méthode str().
1. Dotez la classe Expression d’un comportement de séquence (Transformez paires() en getitem()).
1. Dotez la classe Calculatrice d’un constructeur, puis transformez là en objet-fonction.

le fichier « calculatrice.py » contient à présent :

#!python
    .....
    expr = Expression(chaine)
    print(expr)
    for op, num in expr:
	if op=='=': res = num
	else:
            fct = operateurs.operations[op]
            res = fct(res,num)
    .....

et le fichier « main.py » :

#!python
calc = Calculatrice()


print()
ligne=input(">>> ")
while ligne!='OFF':
calc(ligne)
ligne=input(">>> ")


© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments

LangageHeritage

[wiki: Héritage] [#license ©]

PageOutline

Généralités

Python permet d’exprimer une relation « est une sorte de » entre deux classes. Si B est une sorte de A, on s’attend à ce que les instances de B possède les attributs (données et méthodes) de cette classe, mais aussi tous ceux de A, puisque ces objets sont aussi une sorte de A. On dit que B hérite de A, ou dérive de A. On peut aussi qualifier A de « classe mère », de « classe de base », de « super-classe », et B de « classe dérivée », de « classe fille », de « sous-classe » de A.

Pour hériter d’une classe mère, il suffit d’indiquer son nom entre parenthèses après le nom de la classe fille. La classe fille a accès à tous les attributs de la classe mère. En d’autres termes, quand on écrit obj.x, l’interpréteur cherche x dans l’instance, puis dans sa classe, puis dans la classe mère de cette classe, etc.

#!div style="width:600px"


{{{
#!python
class A:
def x(self): return "A"
def y(self): return "A"

class B(A):
def z(self): return "B"

b = B()

print(b.x(),b.y(),b.z())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
A A B
</pre>

}}}

Surcharge

Une classe dérivée peut ajouter de nouveaux attributs, qu’il s’agisse de données ou de méthodes. Elle peut aussi redéfinir un des attributs de la classe de base, auquel cas, pour les instances de la classe dérivée, c’est la nouvelle définition qui sera trouvée la première, et utilisée. On parle de « surcharge ».

#!div style="width:600px"


{{{
#!python
class A:
def x(self): return "A"
def y(self): return "A"
def z(self): return "A"

class B(A):
def x(self): return "B"
def t(self): return "B"

b = B()

print(b.x(),b.y(),b.z(),b.t())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B A A B
</pre>

}}}

Héritage multiple

Une classe peut hériter de plusieurs autres en même temps (on donne alors une liste de noms séparés par des virgules). L’ordre de gauche à droite des classes parentes est respecté lorsque l’interpréteur Python recherche un nom (donnée ou méthode).

#!div style="width:600px"


{{{
#!python
class A1:
def x(self): return "A1"

class A2:
def x(self): return "A2"

class B(A1,A2):
pass

b = B()
print(b.x())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
A1
</pre>

}}}

Arborescence d’héritage

En plus de l’héritage multiple, chaque classe de base peut à son tour hériter d’une autre classe, etc. L’ensemble des classes dont hérite une instance forme ainsi une arborescence parfois complexe. Dans cette arborescence, la recherche d’un attribut se fait de bas en haut et de gauche à droite, en profondeur d’abord, mais en faisant en sorte qu’une classe de base ne soit jamais explorée avant que toutes ses dérivées le soient (on parle de « linéarisation C3 »). Ainsi, dans l’exemple ci-dessous, A est prioritaire sur C, mais pas sur B2 :

#!div style="width:600px"


{{{
#!python
class A:
def x(self): return "A"
def y(self): return "A"
def z(self): return "A"

class B1(A):
pass

class B2(A):
def x(self): return "B2"

class C:
def y(self): return "C"
def z(self): return "C"

class D(B1,B2,C):
z = C.z

d = D()
print(d.x(),d.y(),d.z())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B2 A C
</pre>

}}}

L’ordre de parcours des classes est appelé le MRO (Method Resolution Order).

On voit aussi, dans l’exemple ci-dessus, que l’on peut explicitement copier la méthode z de C dans D. Ainsi, tout appel à la méthode z pour une instance de D se trouve redirigé vers la méthode de C (au lieu de A).

Chaque recherche d’attribut est indépendante et repart de self

Lorsque j’appelle une méthode m1 de l’objet obj, cette méthode est recherchée dans l’arborescence de classes de obj. Si, à son tour, m1 appelle une autre méthode m2, la recherche de m2 repart de obj (et pas de la classe de m1). En C++, on dirait que toutes les méthodes sont virtuelles et toutes les variables polymorphiques.

#!div style="width:600px"


{{{
#!python
class A:
def x(self): return "A"
def y(self): return "A"
def affichex(self): print(self.x())

class B1(A):
pass

class B2(A):
def x(self): return "B2"

class C:
def y(self): return "C"
def affichey(self): print(self.y())

class D(B1,B2,C):
pass

d = D()
d.affichex()
d.affichey()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B2
A
</pre>

}}}

Si on transforme les méthodes x() et y() en attributs pseudo-privés, ces attributs ne sont visibles qu’aux méthodes de leurs classes respectives, et ces méthodes utiliseront exclusivement ces attributs.

#!div style="width:600px"


{{{
#!python
class A:
def __x(self): return "A"
def __y(self): return "A"
def affichex(self): print(self.__x())

class B1(A):
pass

class B2(A):
def __x(self): return "B2"

class C:
def __y(self): return "C"
def affichey(self): print(self.__y())

class D(B1,B2,C):
pass

d = D()
d.affichex()
d.affichey()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
A
C
</pre>

}}}

Reutiliser explicitement une méthode de base

Lorsqu’une méthode est redéfinie dans une classe dérivée, on dit qu’elle est surchargée. Plutôt que de réécrire toutes les instructions, il peut être utile de commencer par exécuter le code de la méthode de la classe de base. C’est particulièrement vrai pour les méthodes spéciales, telles que les constructeurs. Il est assez facile d’appeler explicitement une méthode de base en passant par le nom de la classe :

#!div style="width:600px"


{{{
#!python
class Fruit:
def init(self, couleur, variete=''):
self.__couleur= couleur
self.__variete= variete
def str(self):
if (self.__variete!=''):
return "{:s} {:s}".format(self.variete,self.couleur)
else:
return "fruit {:s}".format(self.__couleur)

class Pomme(Fruit):
def init(self, couleur, variete='golden'):
Fruit.init(self,couleur,variete)
def str(self):
return ("pomme " + Fruit.str(self))

p = Pomme('rouge')

print(p)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
pomme golden rouge
</pre>

}}}

On peut remplacer l’appel explicite à la classe de base par un appel à la fonction prédéfinie super(). Cela permet de modifier ultérieurement une classe de base sans avoir à corriger tous les appels directs aux méthodes de cette classe mère. Dans cette variante, on ne redonne pas self en premier argument de l’appel de fonction.

#!div style="width:600px"


{{{
#!python
class Pomme(Fruit):
def init(self, couleur, variete='golden'):
super().init(couleur,variete)
def str(self):
return ("pomme " + super().str())

p = Pomme('rouge')

print(p)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
pomme golden rouge
</pre>

}}}

En cas d’héritage multiple, super() retourne le parent le plus proche en suivant le chemin habituellement parcouru pour rechercher un attribut (MRO). Ainsi, si les méthodes de même nom s’appellent en chaîne à travers super(), on parcourt l’ensemble de l’arborescence.

#!div style="width:600px"


{{{
#!python
class A:
def x(self):
print("<A>")
print("</A>")

class B1(A):
def x(self):
print("<B1>")
super().x()
print("</B1>")

class B2(A):
def x(self):
print("<B2>")
super().x()
print("</B2>")

class C(B1,B2):
def x(self):
print("<C>")
super().x()
print("</C>")

c = C()
c.x()


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<C>
<B1>
<B2>
<A>
</A>
</B2>
</B1>
</C>
</pre>

}}}

Travaux Pratiques

1. Ajoutez dans calculatrice.py une classe dérivée CalculatriceEtendue, qui ajoute la possibilité de demander l’inverse du dernier résultat obtenu, en tapant inv (et rien que ça), ou bien sa racine carrée en tapant sqrt. Traiter ces cas particuliers dans la méthode call de la nouvelle classe CalculatriceEtendue (pas dans la classe Expression).
1. Dans main.py, utilisez et testez la nouvelle calculatrice.

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments

LangageComposition

[wiki: Les objets composés] [#license ©]

PageOutline

Généralités

Un objet peut contenir des objets, qui peuvent contenir des objets, qui… On peut descendre dans les niveaux successifs en enchainant les . .

#!div style="width:600px"


{{{
#!python
class Vecteur: pass
class Impulsion : pass

imp = Impulsion()
imp.vitesse = Vecteur()
imp.vitesse.x = 1.
imp.vitesse.y = 0.
imp.vitesse.z = 0.
imp.masse = 10.

print(imp.vitesse.x,imp.vitesse.y,imp.vitesse.z,imp.masse)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
1.0 0.0 0.0 10.0
</pre>

}}}

(Non)Chainage des constructeurs

Dans un constructeur, il n’y a pas d’appel automatique aux constructeurs des sous-parties de l’objet courant, comme cela existe dans d’autres langages. Un objet n’a pas d’obligation de contenir des données, et donc tout ce qui est ajouté doit l’être explicitement, y compris dans les constructeurs.

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self):
self.x = 0
self.y = 0
self.z = 0
def str(self):
return "{:d}|{:d}|{:d}".format( self.x, self.y, self.z )

class Impulsion:
def init(self):
self.vitesse = Vecteur()
self.masse = 0
def str(self):
return "{:s}|{:d}".format( self.vitesse.str(), self.masse )

i1 = Impulsion()
print(i1)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
0|0|0|0
</pre>

}}}

Du point de vue de l’affectation, de la copie, de la comparaison, et de beaucoup d’autres opérations, un objet se comporte essentiellement comme un dictionnaire contenant des attributs.

Affectation et copie

Lorsqu’on affecte un objet à une variable, il n’y a pas de copie. Comme d’habitude, cela génère une nouvelle variable qui référence le même objet original. Dans l’exemple ci-dessous, on voit que si je modifie un attribut de i2, i1 est également modifié.

#!div style="width:600px"


{{{
#!python
i2 = i1
i2.masse = 1
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|0|1), (0|0|0|1)
</pre>

}}}

Pour dupliquer un objet, on peut avoir recours au module copy, et à sa fonction copy(). Ainsi, si je modifie maintenant un attribut de i2, l’attribut correspondant de i1 est inchangé.

#!div style="width:600px"


{{{
#!python
import copy
i2 = copy.copy(i1)
i2.masse = 2
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|0|2), (0|0|0|1)
</pre>

}}}

Mais il s’agit d’une copie de surface. L’équivalent d’une affectation des attributs un à un :

#!div style="width:600px"


{{{
#!python
i2 = Impulsion()
i2.vitesse = i1.vitesse # i2 = copy.copy(i1)
i2.masse = i1.masse # i2 = copy.copy(i1)
i2.masse = 2
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|0|2), (0|0|0|1)
</pre>

}}}

Comme au début, pour i2 = i1, l’instruction i2.vitesse = i1.vitesse ne duplique pas l’instance de Vecteur, mais fait en sorte que i2.vitesse et i1.vitesse désigne la même instance. Ainsi, si on change un attribut de i2.vitesse, on agit également sur i1.vitesse :

#!div style="width:600px"


{{{
#!python
i2.vitesse.z = 2
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|2|2), (0|0|2|1)
</pre>

}}}

Si on souhaite dupliquer intégralement un objet et tous ses attributs à tous les niveaux, on préfèrera la fonction deepcopy(). Dans l’exemple suivant, on modifie un sous-sous-attribut de i2, sans affecter i1.

#!div style="width:600px"


{{{
#!python
i2 = copy.deepcopy(i1)
i2.vitesse.y = 3
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|3|2|1), (0|0|2|1)
</pre>

}}}

Egalité et identité de deux objets

Malheureusement, l’opérateur de comparaison == ne va pas spontanément vérifier récursivement l’égalité de tous les données membres de l’objet. A la place, il se contente de vérifier que les deux variables désignent le même objet, à la façon d’un is :

#!div style="width:600px"


{{{
#!python
i1 = Impulsion(1,2,3,4)

import copy
i2 = copy.deepcopy(i1)

print(i1 is i2, i1==i2, i1!=i2)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False, False, True
</pre>

}}}

Pour obtenir le comportement « logique », il faut par exemple redéfinir les opérateurs eq et ne à tous les niveaux imbriqués :

#!div style="width:600px"


{{{
#!python
class Vecteur:
.....
def eq(self,other):
return ( ( self.x==other.x )
and ( self.y==other.y )
and ( self.z==other.z ) )

class Impulsion:
.....
def eq(self,other):
return ( ( self.vitesse==other.vitesse )
and ( self.masse==other.masse ) )

i1 = Impulsion(1,2,3,4)

import copy
i2 = copy.deepcopy(i1)

print(i1 is i2, i1==i2, i1!=i2)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False, True, True
</pre>
#!python
class Vecteur:
    .....
    def ne(self,other): return not(self==other)
        
class Impulsion:
    .....
    def ne(self,other): return not(self==other)


i1 = Impulsion(1,2,3,4)

import copy
i2 = copy.deepcopy(i1)

print(i1 is i2, i1==i2, i1!=i2)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False, True, False
</pre>

}}}

Travaux Pratiques

1. Dotez la classe Expression d’un opérateur + de concaténation.
1. Dotez la classe Expression d’une méthode de comparaison ==.
1. Ajouter le code suivant en fin de analyseur.py afin de vérifier les opérations ci-dessus :

#!python
    if name == "main":
        expr1 = Expression("+1+2")
        expr2 = Expression("+3+4")
        expr3 = expr1+expr2
        expr4 = Expression("+1+2+3+4")
        print(expr3)
        print(expr4)
        if (expr3==expr4): print("Concatenation et comparaison OK")
        else: print("PROBLEME de concatenation et comparaison")

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments

BibliosCalcul

[wiki: Calcul]

#!comment


http://matplotlib.org/faq/usage_faq.html#matplotlib-pylab-and-pyplot-how-are-they-related

VisPy ?
http://reseau-loops.github.io/journee_2014_06.html


Module math

Ce module fournit un ensemble de fonctions mathématiques pour les réels.

  • pi
  • sqrt
  • cos, sin, tan, acos, …
#!html
<br clear=right>

NumPy

Contenu

  • Tableaux multidimensionnels
  • Arithmétique et fonctions mathématiques sur les tableaux
  • Algèbre linéaire (LAPACK)
  • Transformée de Fourier (FFTPACK)
  • Nombres aléatoires
  • Outils pour intégrer du code Fortran, C/C++
  • Outils d’installation avec support tests unitaires

Remarques

  • Fonctions très rapides, proches des temps d’exécution en C sur les gros tableaux.
  • Code optimisé, de nombreux sous-modules disponibles.
  • Effort de vectorisation des algorithmes.
  • Tableaux multidimensionnels intégrés directement comme type (jusqu’à maintenant, seul le Fortran proposait cela).
  • Les matrices se comportent comme des tableaux, à part dans certaines opérations comme la multiplication, par exemple.
  • Distribueé au sein de !SciPy (voir ci-dessous).

Tableaux multidimensionnels

  • tous les élements du même type (entier, réel, …)
  • stockage compact des données, compatible C/Fortran
  • opérations efficaces
  • arithmétique
  • indexation flexible

Exemples d’opérations simples :

#!div style="float:left;width:48%"


{{{
#!python

author = 'Simon CHOLLET (simon.chollet@ens.fr)'
date = '18/08/2013'

import numpy

# Declaration des tableaux
int_array = numpy.array([1, 2, 4, 8, 16])
float_array = numpy.array([1.2, 2.4, 4.8, 8.16, 16.32])
complex_array = numpy.array(1, 2], [3, 4, dtype=complex)
array_2D = numpy.array(1, 2, 3],[4, 5, 6)
array_3D = numpy.array([1, 2, 3], [4, 5, 6, \
4, 5, 6], [7, 8, 9,7, 8, 9], [1, 2, 3])

# Opérations sur tous les éléments
print int_array ** 2
print 10.0 * numpy.sin(float_array)

# Recuperation du type de valeurs
print int_array.dtype
print float_array.dtype
print complex_array.dtype

# Les dimensions de tableau
print "Dim(complex_array) = %d" % (complex_array.ndim)
print "Dim(array_2D) = %d" % (array_2D.ndim)
print "Dim(array_3D) = %d" % (array_3D.ndim)

# Le nombre d'elements
print "complex_array = %d elements." % (complex_array.size)
print "array_2D = %d elements." % (array_2D.size)
print "array_3D = %d elements." % (array_3D.size)

# Les tailles en octets des elements
print "Element int_array = %d octets." % (int_array.itemsize)
print "Element float_array = %d octets." % (float_array.itemsize)
print "Element complex_array = %d octets." % (complex_array.itemsize)

# Creation d'un tableau contenant que des 0
print numpy.zeros(10)
print numpy.zeros(10, dtype=numpy.int64)
print numpy.zeros((3, 5))

# Creation de tableaux contenant des 1
print numpy.ones(10)
print numpy.ones(5, dtype=numpy.complex)
print numpy.ones((4, 2))

# Creation de tableau vide
print numpy.empty(4)
print numpy.empty(4, dtype=numpy.int32)
print numpy.empty((2, 3))

# Creation d'un tableau allant de 5 en 5
print numpy.arange(10, 50, 5)

# Creation d'un tableau avec des valeurs regulierement espacees
print numpy.linspace(0, 9, 10)

# Combinaison de tableaux
print int_array - int_array
print int_array + int_array
print float_array * float_array
print float_array / float_array

# Multiplication de matrices
mat1 = numpy.array(1, 1], [0, 1)
mat2 = numpy.array(2, 0], [3, 4)
print mat1 * mat2
print numpy.dot(mat1, mat2)


}}}

#!div style="float:right;width:48%"


{{{
#!html
<pre style="margin-right:20px;border: 1px solid palegoldenrod;padding:4px;background-color:lightyellow">

[ 1 4 16 64 256 ]
[ 9.32039086 6.75463181 -9.96164609 9.5354065 -5.74535674 ]

int64
float64
complex128

Dim(complex_array) = 2
Dim(array_2D) = 2
Dim(array_3D) = 3

complex_array = 4 elements.
array_2D = 6 elements.
array_3D = 18 elements.

Element int_array = 8 octets.
Element float_array = 8 octets.
Element complex_array = 16 octets.

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0 0 0 0 0 0 0 0 0 0 ]
0. 0. 0. 0. 0. ] ↵ [ 0. 0. 0. 0. 0. ] ↵ [ 0. 0. 0. 0. 0.

[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. ]
[ 1.+0.j 1.+0.j 1.+0.j 1.+0.j 1.+0.j ]
1. 1. ] ↵ [ 1. 1. ] ↵ [ 1. 1. ] ↵ [ 1. 1.

[ 6.93537601e-310 1.97731475e-316 1.75700415e-316 1.75700415e-316 ]
[ 39083840 0 39316480 0 ]
[[ 6.93537601e-310 1.94233015e-316 6.93537694e-310 ]
[ 1.80609056e-316 1.92870896e-316 6.93537601e-310 ]]

[ 10 15 20 25 30 35 40 45 ]

[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. ]

[ 0 0 0 0 0 ]
[ 2 4 8 16 32 ]
[ 1.44 5.76 23.04 66.5856 266.3424 ]
[ 1. 1. 1. 1. 1. ]

2 0 ] ↵ [ 0 4
5 4 ] ↵ [ 3 4
</pre>


}}}

Quelques manipulations avancées :

#!div style="float:left;width:48%"


{{{
#!python

author = 'Simon CHOLLET (simon.chollet@ens.fr)'
date = '18/08/2013'

import numpy
import pylab

# 1 si nous utilisons la methode du module 'matplot' pour l'histogramme
MATPLOT_HISTOGRAM = 0

# Declaration des tableaux
int_array = numpy.array([1, 2, 4, 8, 16])
float_array = numpy.array([1.2, 2.4, 4.8, 8.16, 16.32])
complex_array = numpy.array(1, 2], [3, 4, dtype=complex)
array_2D = numpy.array(1, 2, 3],[4, 5, 6)
array_3D = numpy.array([1, 2, 3], [4, 5, 6, \
4, 5, 6], [7, 8, 9,7, 8, 9], [1, 2, 3])

# Aplatissement des elements du tableau
for item in array_3D.flat:
print item,
print

# Retrouve les dimensions d'un tableau
print array_3D.shape
print array_2D.shape

# Decoupage de tableau en plusieurs
rand_array = numpy.floor(10 * numpy.random.random((2, 12)))
pritn rand_array
print numpy.hsplit(rand_array, 3)
print numpy.vsplit(rand_array, 2)

# Donne la reference du tableau
print id(rand_array)

# Copie du tableau
int_array2 = int_array.copy()
print int_array2

# Teste les elements
bool_array = int_array > 5
print bool_array
print int_array[bool_array]

# Construction d'un vecteur de 10000 valeurs avec
# une variance de 0.5² et une moyenne de 2
mu, sigma = 2, 0.5
v = numpy.random.normal(mu, sigma, 10000)

# Trace un histogramme ou une courbe
if MATPLOT_HISTOGRAM:
# Trace un histogramme
pylab.hist(v, bins=50, normed=1)
pylab.show()
else:
# Trace une courbe
(n, bins) = numpy.histogram(v, bins=50, normed=True)
pylab.plot(.5 * (bins[1:] + bins[:-1]), n)
pylab.show()


}}}

#!div style="float:right;width:48%"


{{{
#!html
<pre style="margin-right:20px;border: 1px solid palegoldenrod;padding:4px;background-color:lightyellow">

1 2 3 4 5 6 4 5 6 7 8 9 7 8 9 1 2 3

(3L, 2L, 3L)
(2L, 3L)

[[ 3., 7., 1., 1., 2., 5., 3., 9., 5., 1., 0., 1. ]
[ 4., 0., 8., 5., 5., 7., 1., 8., 0., 6., 5., 6. ]]

[array(3., 7., 1., 1. ], [ 4., 0., 8., 5.),
array(2., 5., 3., 9. ], [ 5., 7., 1., 8.),
array(5., 1., 0., 1. ], [ 0., 6., 5., 6.)]

[array(3., 7., 1., 1., 2., 5., 3., 9., 5., 1., 0., 1.),
array(4., 0., 8., 5., 5., 7., 1., 8., 0., 6., 5., 6.)]

38092448



[ 1 2 4 8 16 ]

[ False False False True True ]
[ 8 16 ]


}}}

#!html
<br clear=right>

Un exemple plus complexe :

author    = 'Simon CHOLLET (simon.chollet@ens.fr)'
date      = '18/08/2013'


import numpy

def mandelbrot(h, w, maxit=50):
'''
Fonction qui retourne une image de fractale de Mandelbrot
de taille (h, w).
-------------------------------------------------------------------------
Arguments :
- h : Hauteur de l'image a generer.
- w : Largeur de l'image a generer.
- maxit : Finesse.
-------------------------------------------------------------------------
Retour :
Image fractale.
'''
y, x = numpy.ogrid[-1.4:1.4:h * 1j, -2:0.8:w * 1j]
c = x + y * 1j
z = c
divtime = maxit + numpy.zeros(z.shape, dtype=numpy.int)

for i in xrange(maxit):
z = z ** 2 + c
diverge = z * numpy.conj(z) > 2 ** 2
div_now = diverge & (divtime == maxit)
divtime[div_now] = i
z[diverge] = 2

return divtime

if name == "main":

import pylab

MANDELBROT_NB_POINTS = 50
# 0 = version vectorisee
# 1 = version non vectorisee
# 2 = version par fonction
MANDELBROT_VERSION = 0

# Calculs preliminaires
divergence = numpy.zeros((MANDELBROT_NB_POINTS, MANDELBROT_NB_POINTS))
if MADELBROT_VERSION == 0:
# Version non vectorisee
for c_x in numpy.linspace(-1.5, 0.5, MANDELBROT_NB_POINTS):
for c_y in numpy.linspace(-1, 1, MANDELBROT_NB_POINTS):
c = c_x + c_y * 1j
z = 0
for i in range(50):
z = z ** 2 + c
if numpy.abs(z) > 10:
divergence[(c_x + 1.49) * MANDELBROT_NB_POINTS / 2,
(c_y + 0.99) * MANDELBROT_NB_POINTS / 2] = 50 - i
break
elif MANDELBROT_VERSION == 1:
# Version vectorisee
c_x, c_y = numpy.ogrid[-1.5:0.5:MANDELBROT_NB_POINTS * 1j,
-1:1:MANDELBROT_NB_POINTS * 1j]
c = c_x + c_y * 1j
divergence = numpy.zeros((MANDELBROT_NB_POINTS, MANDELBROT_NB_POINTS))
z = numpy.zeros((MANDELBROT_NB_POINTS, MANDELBROT_NB_POINTS))
masque = numpy.ones((MANDELBROT_NB_POINTS, MANDELBROT_NB_POINTS),
dtype=numpy.bool)
for i in range(50):
z[masque] = z[masque] ** 2 + c[masque]
masque = (numpy.abs(z) > 10)
divergence += masque

# Trace du resultat
if MANDELBROT_VERSION == 0 or MANDELBROT_VERSION == 1:
pylab.imshow(divergence, cmap=pylab.cm.spectral, extent=(-1, 1, -1, 1))
else:
pylab.imshow(mandelbrot(MANDELBROT_NB_POINTS, MANDELBROT_NB_POINTS))
pylab.show()


#!comment
Comment optimiser !NumPy :


  • utiliser l’API C directement,
  • utiliser Swig,
  • utiliser f2py,
  • utiliser cython,
  • ...

#!html
<br clear=right>

Matplotlib

Bibliothèque de visualisation scientifique, conçue comme une alternative à Matlab.

Contenu

  • Plots 2D
  • Plots 3D avec l’extension mplot3d
  • Affichage à l’écran, EPS, PDF, …
  • Intégrable dans un interface graphique

PyLab : distribué avec Matplotlib, ce module intègre dans le même espace de nom tout numpy et tout matplotlib.pyplot, afin d’offrir à l’utilisateur un environnement le plus proche possible de MatLab. Voir la FAQ.

#!html
<br clear=right>

SymPy

Pour le calcul symbolique.

#!html
<br clear=right>

SciPy

#!comment


PySide ? http://ipython.org/ipython-doc/stable/interactive/qtconsole.html ?
Matplotlib ? http://www.pyqtgraph.org/ ?


Description :

  • Comme numpy, fournit des fonctions mathématiques de base sur tableaux et matrices.
  • Intègre des algorithmes numériques : résolution d’équations, calculs statistiques, etc.
  • Il est vaste et grandit régulièrement.
  • Point d’entrée « unique » pour les scientifiques.
  • Utilise quasiment les mêmes méthodes que numpy.
  • Aussi optimisé en C.

Les composants principaux de la Distribution !SciPy :

  • Python
  • !NumPy
  • Bibliothèque !SciPy
  • Matplotlib
  • Pandas
  • !SimPy
  • IPython
  • Nose

Quelques paquets de la Bibliothèque !SciPy :

  • fft : Transformées de Fourrier.
  • integrate : Fonctions d’intégration numérique.
  • interpolate : Fonctions d’interpolation, linéaire, cubique, etc.
  • io : Fonctions d’entrées / sorties vers les fichiers.
  • linalg : Fonctions sur les fonctions d’algèbre linéaire.
  • ndimage : Fonctions de traitement d’images.
  • optimize : Fonctions d’optimisation de fonction.
  • sparse : Fonctions sur les matrices.
  • special : Série de fonctions spéciales.
  • stats : Fonctions pour les calculs statistiques.
  • tests : Fonctions qui permettent de tester la librairie, elle-même.
  • utils : Fonctions utilitaires diverses.

SciKits

  • scikit-aero Aeronautical engineering calculations in Python.
  • scikit-commpy Digital Communication Algorithms with Python
  • scikit-fmm An extension module implimenting the fast marching method
  • scikit-image Image processing routines for !SciPy
  • scikit-learn A set of python modules for machine learning and data mining
  • scikit-monaco Python modules for Monte Carlo integration
  • scikit-nano Python toolkit for generating and analyzing nanostructure data
  • scikit-rf Open Source RF Engineering
  • scikit-tensor Python module for multilinear algebra and tensor factorizations
  • scikit-vi Scikit providing Virtual Instruments
  • scikits-image Image processing routines for SciPy
  • ann Approximate Nearest Neighbor library wrapper for Numpy
  • audiolab A python module to make noise from numpy arrays
  • bootstrap Bootstrap confidence interval estimation routines for !SciPy
  • bvp1lg Boundary value problem (legacy) solvers for ODEs
  • bvp_solver Python package for solving two-point boundary value problems
  • cuda Python interface to GPU-powered libraries
  • datasmooth Scikits data smoothing package
  • eartho Earth Observation routines for !SciPy
  • example Scikits example package
  • fitting Framework for fitting functions to data with !SciPy
  • hydroclimpy Environmental time series manipulation
  • learn A set of python modules for machine learning and data mining
  • odes A python module for ordinary differential equation anddifferential algebraic equation solvers
  • optimization A python module for numerical optimization
  • samplerate A python module for high quality audio resampling
  • scattpy Light Scattering methods for Python
  • sparse Scikits sparse matrix package
  • statsmodels Statistical computations and models for use with !SciPy
  • talkbox Talkbox, a set of python modules for speech/signal processing
  • timeseries Time series manipulation
  • vectorplot Vector fields plotting algorithms.

Exemples :

#!div style="float:left;width:48%"


{{{
#!python

author = 'Simon CHOLLET (simon.chollet@ens.fr)'
date = '18/08/2013'

import scipy
import scipy.special
import scipy.interpolate
import scipy.linalg
import pylab

# Declaration des tableaux
int_array = scipy.array([1, 2, 4, 8, 16])
float_array = scipy.array([1.2, 2.4, 4.8, 8.16, 16.32])
array_2D = scipy.array([[1, 2, 3],
[4, 5, 6]])
array_3D = scipy.array([1, 2, 3], [4, 5, 6,
4, 5, 6], [7, 8, 9,
7, 8, 9], [1, 2, 3])

tab = scipy.array(1, 2], [3, 4)
mat = scipy.mat(tab)
print "* Valeurs propres mat = ", scipy.linalg.eigvals(mat)
print "* Determinant mat = ", scipy.linalg.det(mat)
print "* Matrice inverse = \n", mat.I

# Calcul de FFT
rate = 1000.0
t = scipy.r_[0:0.6:1 / rate]
nbT = len(t)

# Le signal a analyser
s = scipy.sin(2.0 * scipy.pi * 50.0 * t) + \
scipy.sin(2.0 * scipy.pi * 70.0 * t + scipy.pi / 4.0)

# Calcul de la FFT
fftS = scipy.fft(s)

# Calcul des limites de graphiques
f = rate * scipy.r_[0:(nbT / 2)] / nbT
n = len(f)
pylab.plot(f, abs(fftS[0:n]) / nbT)
pylab.show()


}}}

#!div style="float:right;width:48%"


{{{
#!html
<pre style="margin-right:20px;border: 1px solid palegoldenrod;padding:4px;background-color:lightyellow">

Valeurs propres mat = [-0.37228132+0.j 5.37228132+0.j]
Determinant mat = -2.0
[[-2. 1. ]
[ 1.5 -0.5]]


}}}

#!html
<br clear=right>

Intégration et interpolation :

#!div style="float:left;width:48%"


{{{
#!python

author = 'Simon CHOLLET (simon.chollet@ens.fr)'
date = '18/08/2013'

import scipy
import scipy.special
import scipy.integrate
import scipy.interpolate
import scipy.linalg
import pylab

def f(x):
return x 4 + x 3 - x ** 2 - 10

# Calcul de l'intergrale de x^5 de 0 a 1
value, err = scipy.integrate.quad(func=pow, a=0., b=1., args=(5,))
print "* Integrale de x^5, entre 0 et 1 = %f" % (value)

# Calcul de l'intergrale de f(x) de 0 a 1
value, err = scipy.integrate.quad(func=f, a=0., b=10., args=())
print "* Integrale de f(x), entre 0 et 10 = %f" % (value)

# Interpolation
# Axe X
x = scipy.linspace(0, 1, 10)
# Axe Y
y = scipy.sin(2 * scipy.pi * x)
# Interpolation lineaire
linear_interp = scipy.interpolate.interp1d(x, y)
# Interpolation cubique
cubic_interp = scipy.interpolate.interp1d(x, y, kind='cubic')

# Affichage des courbes
fine_x = scipy.linspace(0, 1, 50)
y_fromlinearinterp = linear_interp(fine_x)
y_fromcubicinterp = cubic_interp(fine_x)
pylab.plot(fine_x, scipy.sin(2 * scipy.pi * fine_x), 'b-',
fine_x, y_fromlinearinterp, 'g-^',
fine_x, y_fromcubicinterp, 'r-.')
pylab.show()


}}}

#!div style="float:right;width:48%"


{{{
#!html
<pre style="margin-right:20px;border: 1px solid palegoldenrod;padding:4px;background-color:lightyellow">

Integrale de x^5 = 0.166667

Integrale de x^5 = 0.166667


}}}

#!html
<br clear=right>

Traitement d’image :

#!python


author = 'Simon CHOLLET (simon.chollet@ens.fr)'
date = '18/08/2013'

import scipy
import scipy.ndimage
import pylab

img = scipy.misc.lena()
imgFloue = scipy.ndimage.gaussian_filter(img, 1)
imgRotate = scipy.ndimage.rotate(img, 45)

#pylab.imshow(img)
#pylab.imshow(imgFloue)
pylab.imshow(imgRotate)
pylab.show()


VPython

VPython regroupe le langage de programmation Python et le module graphique 3D visual, développé par David Scherer. Il repose également sur !NumPy et wxPython (interface utilisateur).
Exemple de quelques lignes : faisons tourner une boule grise sur un anneau, qui serait dessiné autour d’une plus grosse boule bleue :

#!python


author = 'Simon CHOLLET (simon.chollet@ens.fr)'
date = '17/08/2013'

import visual
import math

# Creation d'une grosse sphere
s1 = visual.sphere()
# Changement de sa couleur : bleue
s1.color = (0, 0, 1)
# Creation d'un anneau
r = visual.ring(radius=2)
# Creation d'une petite sphere de couleur verte
s2 = visual.sphere(pos=(0, 0, 2), color=(0, 1, 0), radius=0.7)
# Mise en scene des objets
visual.scene.autoscale = 0
# Animation ... tout le temps
t = 0
while True:
# Temporisation de 30 images par seconde
visual.rate(30)
# Changement de la position de la petite sphere
s2.pos = (0,
2 * math.cos(t * math.pi / 30),
2 * math.sin(t * math.pi / 30))
# Increment du temps
t += 1


RPy

Interface Python pour le langage de programmation R, dédié aux traitements et analyses statistiques.

  • Implémentation reposant sur le module numpy.
  • Fonctionnalités intéressantes : traiter des séries de données, statistiques, dessiner des images au format PNG, etc.
  • Mais aussi : unique(), sort(), table(), barplot(), etc.
  • ATTENTION : ne fonctionne actuellement que sous Linux, un peu plus dur sous Windows …

Exemple de code :

#!python


import random
from rpy import r as R

#==========
# PARAMETRES
#==========

# Nombre d'elements a generer dans les exemples
RPY_NB_ITEMS = 500
# Fichier image a generer
RPY_OUTPUT_IMAGE_1 = '../output/code_rpy1.png'
RPY_OUTPUT_IMAGE_2 = '../output/code_rpy2.png'

#==========
# GENERATION ALEATOIRE D'ELEMENTS
#==========

# Construction d'une liste de plusieurs elements croissants
x = range(1, RPY_NB_ITEMS + 1)

# Construction d'une liste de plusieurs elements aleatoires
y = []
for i in x:
y.append(random.random())

# Enregistrement du graphique dans un fichier PNG
print '* Creation du fichier Image : %s' % (RPY_OUTPUT_IMAGE_1)
R.png(RPY_OUTPUT_IMAGE_1)

# Dessine les points sur l'image
R.plot(x, y, xlab="Position", ylab="Coordonnées", col="black", pch=3)

# Fin du graphique
R.dev_off()

#==========
# GENERATION D'HISTOGRAMME
#==========

# La sequence de donnees a analyser
dataList = [1, 2, 3, 1, 2, 3, 4, 5, 5, 6, 7, 3, 1,
8, 4, 6, 9, 9, 2, 1, 5, 6, 8, 9, 4, 3,
3, 2, 7, 5, 9, 7, 5, 6, 4, 3, 3, 2, 1]
print '* Donnees a analyser :', dataList

# Enregistrement du graphique dans un fichier PNG
print '* Creation du fichier Image : %s' % (RPY_OUTPUT_IMAGE_2)
R.png(RPY_OUTPUT_IMAGE_2)

# Tri des donnees
dataSort = R.sort(dataList)
print '* Donnees triees :', dataSort

# Extrait les bases
bases = R.unique(dataSort)
print '* Bases pour histogramme :', bases

# Effectif de chaque base
effectifs = R.table(dataSort)
print '* Effectifs dans chaque classe :', effectifs

# Dessine l'histogramme et sauvegarde de la position des abscisses
coords = R.barplot(effectifs, ylab="Nombre")

# Ajout du texte pour l'axe des abscisses
R.text(coords, -0.5, bases, xpd=True, cex=1.5, font=2)

# Fin du graphique
R.dev_off()


#!comment


== ScientificPython ==


Conclusions

  • Le module numpy est largement utilisé.
  • Scipy est un très gros module.
  • Nous pouvons le compléter avec des ‘!SciKits’ …
  • Nous avons parcouru qu’une infime partie des possibilités de ces modules.
  • Il existe sûrement des algorithmes et exemples qui peuvent aider à résoudre certains de vos problèmes spécifiques.
  • De nombreux autres domaines sont traités : aéronautique, simulation, biologie, chimie, médical, etc.
  • Vous pouvez aussi y apporter votre pierre avec notamment l’interfaçage de vos propres bibliothèques de calculs.
#!html
<br clear=right>

References

LangageClasses

[wiki: Classes] [#license ©]

PageOutline

Généralités

#!comment
Les types prédéfinis sont des classes ?
Les littéraux des objets ?


QUID DES METACLASSES ?


La création d’une nouvelle classe peut être considérée comme la création d’un nouveau type de données, défini par le développeur. Par convention, les noms de classe sont généralement typographié en « CamelCase ». Dans l’exemple ci-dessous, l’exécution de l’instruction class provoque la création d’un objet de type class, et lui assigne comme nom Vecteur. Comme n’importe quelle entité de Python, la classe créée est placée dans un module (ici main puisque l’instruction class est soumise directement à l’interpréteur).

#!div style="width:600px"


{{{
#!python
class Vecteur:
pass

print(Vecteur)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<class 'main.Vecteur'>
</pre>

}}}

De même qu’un type prédéfini de Python (par exemple float ou list) possède de multiples valeurs (par exemple 3.14 ou []), une classe peut donner naissance à de multiples objets. On dit de chacun de ces objets qu’il est une « instance » de la classe. Pour créer une telle instance, on utilise le nom de la classe comme si c’était un nom de fonction (utilisation de parenthèses). Dans l’exemple ci-dessous, la première instruction provoque la création d’une instance de type Vecteur, et lui assigne comme nom v. La deuxième instruction créera une deuxième instance nommée w.

#!div style="width:600px"


{{{
#!python
v = Vecteur()
w = Vecteur()
print(v,w)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<main.Vecteur object at ...> <main.Vecteur object at ...>
</pre>

}}}

#!comment
Il est possible de mettre des parenthèses après le nom de la classe, mais ceci est tout à fait optionnel : class Name(): est tout aussi correct que class Name:.

Données membres

Tout comme un nombre flottant est composé de sous-parties (mantisse, exposant, signe…), une instance est appelée à contenir des données nommées, accessibles individuellement par l’opérateur . . Les données membres peuvent être lues, créées et modifiées à tout moment et par n’importe qui.

#!div style="width:600px"


{{{
#!python
v.x, v.y, v.z = 1, 2, 3
print(v.x,v.y,v.z)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
1 2 3
</pre>
#!python
del v.z
print(v.x,v.y,v.z)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
AttributeError: 'Vecteur' object has no attribute 'z'
</pre>

}}}

Méthodes

Imaginons ci-dessous que l’on souhaite doter nos nouveaux vecteurs d’un calcul de norme et d’une transformation de type homothétie. Pour uniformiser, on définit également une fonction pour initialiser les attributs d’une instance, et une fonction d’affichage :

#!div style="width:600px"


{{{
#!python
class Vecteur:
pass

def init(v,x=0,y=0):
v.x = x
v.y = y

def norme(v):
x2 = v.x**2
y2 = v.y**2
return (x2+y2)**(1./2.)

def homothetie(v,n):
v.x = v.x * n
v.y = v.y * n

def affiche(v):
print(v.x,v.y)

v = Vecteur()
init(v,3,-4)
print(norme(v))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
5.0
</pre>
#!python
homothetie(v,3)
affiche(v)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
9 -12
</pre>

}}}

On peut être rapidement confronté à des conflits de noms avec d’autres fonctions similaires pour d’autres classes, et on aimerait que nos nouvelles fonctions ne soient utilisées qu’avec notre nouvelle classe Vecteur. Pour ce faire, nous allons en faire des « méthodes » en les insérant dans la classe.

Une méthode est une fonction « attachée » à une classe particulière. La définition des méthodes est faite à l’intérieur de la définition de la classe.

Dans la définition d’une méthode, le premier paramètre désigne l’instance courante sur laquelle on veut appliquer la méthode. Pour exécuter une méthode sur une instance particulière, on utilise l’opérateur « . », comme pour les données membres, puis des parenthèses pour signifier qu’il s’agit d’une fonction. L’instance à gauche du « . » est alors passée à la méthode en tant que premier paramètre.

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self,x=0,y=0):
self.x = x
self.y = y
def norme(self):
x2 = self.x**2
y2 = self.y**2
return (x2+y2)**(1./2.)
def homothetie(self,n):
self.x = self.x * n
self.y = self.y * n
def affiche(self):
print(self.x,self.y)

v = Vecteur()
v.init(3,-4)
print(v.norme())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
5.0
</pre>
#!python
v.homothetie(3)
v.affiche()
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
9 -12
</pre>

}}}

Encapsulation

Données membres et méthodes sont collectivement appelés « attributs ». On place généralement les données dans les instances (et elles varient d’une instance à l’autre), et les méthodes dans la classe.

En programmation orientée objet orthodoxe, il est préconisé d’interdire l’accès direct aux données par les codes clients extérieurs, et de forcer ces clients à passer par l’intermédiaire de méthodes de lecture (qu’on appelle « getters ») et de méthodes de modification (qu’on appelle « setters »).

Python ne permet pas vraiment d’interdire l’accès aux données, mais seulement de les masquer. Pour ce faire, donnez leur un nom qui commence par __. les données sont alors dites « pseudo-privées ».

Dans l’exemple ci-dessous, les attributs x et y de Vecteur ont été rendus pseudo-privés, la méthode init() fait office de « setter » global (méthode pour changer les valeurs de x et y), et les méthodes getx() et gety() font office de « getters ».

#!div style="width:600px"


{{{
#!python
class Vecteur:

def init(self,u=0,v=0):
self.__x = u
self.__y = v

def getx(self):
return self.__x

def gety(self):
return self.__y

v = Vecteur()
v.init(3, -4)
print(v.getx(),v.gety())


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3 -4
</pre>
#!python
print(v.x,v.y)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
...AttributeError: 'Vecteur' object has no attribute 'x'
</pre>
#!python
print(v.x,v.y)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
...AttributeError: 'Vecteur' object has no attribute '__x'
</pre>

}}}

Documentation

Les « docstrings » habituelles fonctionnent avec les classes.

#!div style="width:600px"


{{{
#!python
class MaClasse:
"Documentation de MaClasse"
def ma_methode():
"documentation de ma_methode()"
pass

print(MaClasse.doc)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
Documentation de MaClasse
</pre>
#!python
print(MaClasse.ma_methode.doc)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
documentation de ma_methode()
</pre>
#!python
help(MaClasse)
#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
Help on class MaClasse in module main:


class MaClasse(builtin.object)
| Documentation de MaClasse
|
| Methods defined here:
|
| ma_methode()
| documentation de ma_methode()
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| dict
| dictionary for instance variables (if defined)
|
| weakref
| list of weak references to the object (if defined)
</pre>


}}}

#!comment


Exercice

Écrire une classe qui permettra de gérer un fichier (dont le nom sera passé au constructeur). Elle devra comporter plusieurs méthodes qui réaliseront des opérations sur le fichier :

  • count : renvoie le nombre de lignes, de mots et de lettres.
  • head(n) : renvoie les n premières lignes.
  • tail(n) : renvoie les n dernières lignes.
  • find(str) : afficher toutes les lignes contenant la chaine str.
  • replace(str1, str2) : afficher à l’écran toutes les lignes du fichier, en remplaçant chaque occurrence de str1 par str2.
  • tout ce qui pourra vous passer par la tête (mais qui ne modifie pas le fichier lui-même).

Il faudra veiller à ne pas charger en mémoire tout le contenu du fichier, ni à le conserver ouvert trop longtemps.

Travaux Pratiques

Reprenez comme point de départ la solution des travaux pratiques de la demi-journée précédente (calculatrice).

1. On veut transformer la fonction analyse() de telle sorte qu’elle retourne une instance de la classe Expression, contenant deux données membres : opList, liste des opérateurs, et numList la liste des nombres.

  • Placez la définition de classe dans le même fichier que analyse().
  • Dotez la classe Expression des méthodes suivantes :
  • init() : pour affecter des valeurs aux attributs opList et numList.
  • paires() : retourne une liste de paires (opérateur,nombre), chaque paire étant constituée du énième élément de opList et du enième élément correspondant dans la liste numList (ce que fait la fonction zip()).
  • display() : affichage le contenu de l’expression.
  • On pourra alors réécrire la fonction calcule() ainsi :
#!python


def calcule(chaine):
global res
expression = analyse(chaine)
expression.display()
for op, num in expression.paires():
if op=='=': res = num
else:
fct = operateurs.operations[op]
res = fct(res,num)
print(res)


1. Comme le code client n’a plus besoin d’accéder directement aux attributs opList et numList, profitez-en pour rendre ces attributs pseudo-privés.
1. Crééz une classe Calculatrice, dotée d’une variable res initialisée à 0 par une méthode init(), et d’une méthode calcule() qui évalue une instance d’Expression et stocke le résultat dans res (en plus de l’afficher).
1. Dotez la classe Expression d’une constante PI. Modifiez Expression.init() afin qu’elle reconnaisse la valeur PI, et essayez d’évaluer l’expression 2*PI avec votre calculatrice.

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments

LangagePersistance

[wiki: Persistance] [#license ©]

PageOutline

Généralités

  • Les objets sont dit « persistants » lorsqu’ils ne disparaissent pas à la fin du programme Python en cours. Cela consiste en général à les « sérialiser », c’est à dire en produire une représentation binaire compacte, et à stocker cette représentation dans un fichier.
  • En Python, la persistance repose sur des modules de la libraire standard, tels que pickle et shelve.

Pickle / cPickle

  • Ce module est capable de transformer en flot d’octets n’importe quel objet en mémoire. Ces octets peuvent être écrits dans un fichier, envoyés par réseau, etc.
  • A l’inverse, le module peut lire et recrée en mémoire les objets d’un fichier, l’un après l’autre.
  • Quand c’est disponible, préférez cPickle, écrit en C et plus rapide.
  • ATTENTION : pas de garantie de portabilité de vos données entre des plateformes différentes.
#!python


class Vecteur:
def init(self,u=0,v=0):
self.x = u
self.y = v
def str(self):
return "({:.0f},{:.0f})".format(self.x,self.y)

v1 = Vecteur(1,2)
v2 = Vecteur(3,4)

import pickle

fw = open('vecteurs.pkl','wb')
pickle.dump(v1,fw)
pickle.dump(v2,fw)
fw.close()

fr = open('vecteurs.pkl','rb')
print(pickle.load(fr))
print(pickle.load(fr))
fr.close()


Shelve

  • Ce module permet de ranger des objets dans un fichier, non pas séquentiellement, mais en les associant à des noms.
  • Le fichier est manipulé comme si il s’agissait d’un dictionnaire.
#!python


class Vecteur:
def init(self,u=0,v=0):
self.x = u
self.y = v
def str(self):
return "({:.0f},{:.0f})".format(self.x,self.y)

import shelve

s = shelve.open('vecteurs.db')
s['v1'] = Vecteur(1,2)
s['v2'] = Vecteur(3,4)
s.close()

#...

s = shelve.open('vecteurs.db')
print(s.keys())
print(s['v1'])
print(s['v2'])
s.close()


#!python

Travaux Pratiques

1. Faites en sorte que le programme principal stocke l’état courant de la calculatrice avant de se terminer, et la restaure telle quelle à la prochaine éxecution. Servez vous par exemple d’un fichier main.pkl.

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments

LangagePaquets

[wiki: Paquets]

PageOutline

Définition d’un paquet (package)

  • Python appelle « paquet » tout répertoire contenant des fichiers python.
  • On peut l’utiliser comme préfixe lors des importations :
#!python
>>> import pkg.mod2
>>> from pkg.mod2 import *
  • Comme pour les fichiers, Python recherche les paquets au sein des répertoires listés dans sys.path.
  • Les noms de packages, donc de répertoires, doivent respecter les règles de nommage des identificateurs python, donc pas d’espace ou de caractères exotiques, ou de syntaxe spécifique à tel ou tel système d’exploitation. Si on veut importer un fichier dont le chemin contient un espace, il faut que le répertoire correspondant soit intégré à sys.path.
#!html
<br clear=right>

!init!.py

  • Tout package doit contenir un fichier !init!.py, exécuté lors de la première traversée de ce package.
  • Pour chaque package, un objet de type module est créé en mémoire, qui recoit les variables et fonctions créées par l’!init!.py correspondant.
#!comment
La règle de l'init.py ne s'applique pas aux répertoires de base figurant dans sys.path.
ATTENTION au from…*
A Regarder : qu'est-ce qui se passe avec import <package> ??
#!python
# fichier pkg/init.py
x = 1
print 'pkg init'
#!python
# fichier pkg/mod.py
y = 2
print 'in mod.py'
#!python
>>> import pkg.mod
pkg init
in mod.py
>>> import pkg.mod
>>> reload(pkg)
pkg init
>>> pkg.x
1
>>> pkg.mod.y
2
#!html
<br clear=right>

from/as …

…permet d’allléger l’accès ultérieur au module, mais accroît les risques de conflits de noms, et perd l’accès au package intermédiaire

#!python
# fichier pkg/init.py
x = 1
print 'pkg init'
#!python
# fichier pkg/mod.py
y = 2
z 3
print 'in mod.py'
#!python
>>> from pkg import mod
pkg init
in mod.py
>>> import pkg.mod as m
>>> mod.y
2
>>> m.z
3
#!html
<br clear=right>

Importer tous les modules

Aucun moyen implicite et automatique.Trois possibilités explicites :

1.

#!python
# fichier pkg/init.py
from . import mod1
from . import mod2
#!python
>>> import pkg
>>> pkg.mod1...

1.

#!python
# fichier pkg/init.py
from . import mod1
from . import mod2
#!python
>>> from pkg import *
>>> mod1...

1.

#!python
# fichier pkg/init.py
all = [ 'mod1', 'mod2' ]
#!python
>>> from pkg import *
>>> mod1...
#!comment
pas de "from *" au niveau des paquets
le all se comporte toujours de facon relative
#!html
<br clear=right>

Hiérarchie de paquets

On peut imbriquer autant de niveaux de répertoires que désiré, avec autant de fichiers !init!.py .

#!python
# fichier dir1/init.py
x = 1
print 'dir1 init'
#!python
# fichier dir1/dir2/init.py
y = 2
print 'dir2 init'
#!python
# fichier dir1/dir2/mod.py
z = 3
print 'in mod.py'
#!python
>>> import dir1.dir2.mod
dir1 init
dir2 init
in mod.py
>>> import dir1.dir2.mod
>>> reload(dir1)
dir1 init
>>> reload(dir1.dir2)
dir2 init
#!html
<br clear=right>

Modules en mémoire

#!python
>>> dir1
<module 'dir1' from 'dir1/init.pyc'>
>>> dir1.dir2
<module 'dir1.dir2' from 'dir1/dir2/init.pyc'>
>>> dir1.dir2.mod
<module 'dir1.dir2.mod' from 'dir1/dir2/mod.pyc'>
>>> dir1.x
1
>>> dir1.dir2.y
2
>>> dir1.dir2.mod.z
3
#!html
<br clear=right>

Noms des modules

#!python
>>> dir1.name
'dir1'
>>> dir1.dir2.name
'dir1.dir2'
>>> dir1.dir2.mod.name
'dir1.dir2.mod'
>>> name
'main'
#!html
<br clear=right>

Attachments