formations/python2/formation/specialmethods.txt

251 lines
5.8 KiB
Plaintext

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']
<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
.. code-block:: python
print(os.path.abspath(<module>.__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
<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