Programmation python courante ================================ .. _namespaces: les espaces de nommage ----------------------- L'espace de nommage le plus courant est l'organisation en modules et en packages. Packages et modules:: package/ __init__.py module1.py subpackage/ __init__.py module2.py A utilser pour organiser votre projet Permet de minimiser les risques de conflits de nome Permet de diminuer les entrées dans le :envvar:`PYTHONPATH` :: import package.module1 from package.subpackage import module2 from package.subpackage.module2 import name - le fichier `__init__.py` - `reload(module)` au prompt - dangereux : l'import "*", utiliser l'attribut spécial `__all__` pour l'import sélectif :: from os import * lance un module en tant que script : :: if __name__ == "__main__": main() Organisation modulaire - construire des composants élémentaires - combiner ces composants - utiliser une structure pyramidale : les composants sont les éléments de composants plus complexes. - découplage de l'ensemble en composants indépendants (gros programmes réalisables) - donner de la structure (rendre les gros programmes compréhensibles) - spécifier les liens entre les composants (rendre les programmes maintenables) - identifier les sous-composants indépendants (rendre les programmes réutilisables) - forcer l'abstraction (augmenter la sureté du programme) modules chargés et modules importés -------------------------------------- Les modules susceptibles d'être chargés sont dans le :envvar:`PYTHONPATH`. Mais comment peut-on savoir ou ils sont physiquement (sur le disque dur) ? .. envvar:: `sys.modules` >>> 'twisted' in sys.modules False >>> import twisted >>> 'twisted' in sys.modules True >>> sys.modules['twisted'] >>> .. attention:: un module présent dans `sys.modules` n'est pas forcément importé dans l'espace de nommage usuel. Il faut importer le module pour pouvoir l'utiliser. >>> sys.modules['email'] >>> dir(email) Traceback (most recent call last): File "", line 1, in NameError: name 'email' is not defined >>> Pour récupérer le chemin du module .. code-block:: python print(os.path.abspath(.__file__)) Pour importer un module qui n'est pas dans le `sys.path` .. code-block:: python fch = open('/path/to/mymodule/custom.py', 'r') my_module = imp.load_module('dhcp_custom', fch, '/path/to/mymodule.py', ('.py', 'U', 1)) Connaître la version d'un module ------------------------------------- Exemple : le module ``datetime`` C'est suivant la version de python car c'est la librairie standard. Sinon, en général il y a un attribut __version__ >>> import sqlalchemy >>> sqlalchemy.__version__ '0.9.8' >>> Les méthodes spéciales ----------------------- méthodes spéciales correspondants aux interfaces des types de bases : .. function:: __init__(self, *args, **kwargs) le constructeur de l'instance d'objet .. function:: __add__(self, other) correspond à la notation `+` exemple : .. literalinclude:: snippets/specialmethods.py >>> from specialmethods import * >>> z = Zone("titi", 10) >>> z2 = Zone("tutu", 40) >>> z > z2 False >>> z + z2 >>> z3 = z + z2 >>> z3.name 'tititutu' >>> z3.level 50 >>> Attributs et accesseurs --------------------------- python est un langage à attributs, c'est-à-dire que le protocole d'accès aux attributs est règlable. >>> class C(object): ... classattr = "a class attribute" ... >>> cobj = C() >>> cobj.classattr 'a class attribute' >>> cobj.insattr = "an instance attribute" >>> cobj.insattr 'an instance attribute' >>> C.__dict__['classattr'] 'a class attribute' >>> cobj.__dict__['insattr'] 'an instance attribute' les attributs ne sont pas systématiquement encapsulées en python. pour contrôler l'accès aux attributs, on utilise les méthodes spéciales:: __getattr__(self, name) __setattr__(self, name, value) class AnObject(object): ...... def __setattr__(self, name, val): if name == 'src': #do something # this will assign the real object.name, #despite __setattr__ self.__dict__[name]=val def __getattr__(self, name): # ... try: func = getattr(obj, "method") except AttributeError: ... deal with missing method ... else: result = func(args) func = getattr(obj, "method", None) if callable(func): func(args) - un **attribut** spécial : `__slots__` permet de fixer les attributs possibles d'une classe :: >>> class Bar(object): ... __slots__ = ("a","b","c") ... >>> b = Bar() >>> b.a = "toto" >>> b.a 'toto' >>> b.d = "titi" Traceback (most recent call last): File "", line 1, in ? AttributeError: 'Bar' object has no attribute 'd' les slots ~~~~~~~~~~~ .. important:: l'encapsulation n'est pas une condition de base de la programmation par objects, surtout que le contrôle nuit à l'agilité. >>> class Point(object): ... __slots__ = 'x', 'y' ... >>> p = Point() >>> p.x = 2 >>> p.y = 3 >>> p.z = 4 Traceback (most recent call last): File "", line 1, in AttributeError: 'Point' object has no attribute 'z' >>> - notation `|` et notation `>` :: class Test: nombre = 1 def __or__(self, other): return self.nombre + other.nombre def __lshift__(self, other): self.nombre = self.nombre + other.nombre t1 = Test() t2 = Test() t2.nombre = 2 print t1 | t2 t1 << t2 print t1.nombre