Programmation python courante

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 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 PYTHONPATH. Mais comment peut-on savoir ou ils sont physiquement (sur le disque dur) ?

`sys.modules`
>>> 'twisted' in sys.modules
False
>>> import twisted
>>> 'twisted' in sys.modules
True
>>> sys.modules['twisted']
<module 'twisted' from '/usr/lib/python2.7/dist-packages/twisted/__init__.pyc'>
>>>

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']
<module 'email' from '/usr/lib/python2.7/email/__init__.pyc'>
>>> dir(email)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'email' is not defined
>>>

Pour récupérer le chemin du module

print(os.path.abspath(<module>.__file__))

Pour importer un module qui n’est pas dans le sys.path

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 :

__init__(self, *args, **kwargs)
le constructeur de l'instance d'objet
__add__(self, other)

correspond à la notation +

exemple :

class Zone(object):
    def __init__(self, name, level=0):
        self.name = name
        self.level = level

    def __add__(self, other):
        return Zone(self.name + other.name, level=self.level+other.level)

    def __cmp__(self, other):
        return cmp(self.level, other.level)
>>> from specialmethods import *
>>> z = Zone("titi", 10)
>>> z2 = Zone("tutu", 40)
>>> z > z2
False
>>> z + z2
<specialmethods.Zone object at 0x7f02d95fb190>
>>> 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 "<stdin>", 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 "<stdin>", line 1, in <module>
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