{{tag>dev python}}
====== Python: introduction ======
Quelques notes à propos des spécificités du langage Python basées sur mes écueils.
Python a été conçu avec l'objectif d'être lisible, compréhensible faciitant son accès et les échanges de codes. Les choix de conceptions ont été guidés par pragmatisme et non par dogmatisme.
Langage portable avec une grosse base de code: calcul scientifique, traitement de données, web, embarqué etc.
Python permet de devélopper/ concevoir rapidement (PoC):
* types de base puissants (listes, dictionnaires, ensembles etc)
* gestion automatique de la mémoire
* langage interprété et EDI divers (notebooks IDLE etc)
===== Droits et licences =====
Python appartient a la PSF (Python Software Fondation) qui est une organisation à but non lucratif. La licence est permissive et permet de l'utiliser à des fins commerciales.
===== Évolution du langage =====
Toute proposition concernant l'évolution du langage est débattue démocratiquement au travers des PEPs puis tranchée par le BDFL((Benevolent Dictator For Life)) qui se traduit par Dictateur bienveillant à vie.
===== Bref historique =====
Première version python 1.0 sortie en 1994. Version 2 en 2000. Grosse rupture de compatibilité entre la version 2 et la version 3. Langage stable et maintenu.
Librairie standard: ensemble des utilitaires packagés, distribués et maintenus avec Python.
===== EDI et Interpréteurs =====
IDLE (prononcer Aïdeul) est l'EDI proposé par la librairie standard mais il existe de nombreux EDI supportant python on pourra citer: PyCharm,Syder,Emacs etc
**ipython** est interpréteur proposant des couleurs, de l'autocomplétion, une documentation en ligne. Il offre un mode interactif plus user friendly que l’interpréteur python original.
$ sudo apt-get install ipython
# ou sous les versions plus récentes de Debian/xUbuntu
$ sudo apt-get install ipython3
Pour plus de détails concernant l'installation voir le wiki [[dev:python:core:installer_interpreteurs]].
===== Jeux de caractères =====
Par défaut l’interpréteur python2.7 attend des scripts utilisant le jeu de caractères ASCII. Si l'on utilise un autre jeu de caractère il faut le spécifier. Pour cela insérer au plus tôt (avant que la première conversion ait lieu) dans le fichier la ligne suivante:
# coding: utf8
L'encodage utilisé pour écrire le fichier de script est à paramétrer dans l'éditeur ou l'EDI. Il faut toujours savoir quel encodage on utilise pour écrire son code et privilégier utf-8 (mise en œuvre de l'encodage universel unicode).
Généralement pour un fichier script l’entête comprend également le chemin de l’interpréteur à appeler:
#!/usr/bin/python
# coding: utf8
Cet entête permet d'appeler le script depuis le bash (si les droits de lecture et d’exécution sont correctement positionnés) directement:
./monscript.py
===== Variables =====
Une variable référence un objet. Python gère deux espaces:
* l'espace de nom
* l'espace des objets
Lors d'une déclaration telle que:
>>> msg = 'Bonjour!'
3 étapes:
* L'objet de type str avec la valeur 'Bonjour!' est instancié dans l'espace des objets.
* Création de la variable "msg" dans l'espace des variables.
* La référence vers l'objet est associé à la variable "msg".
**La valeur de toute variable est une référence à un objet**. Le typage est dynamique mais le langage est fortement typé. En python le type n'est pas lié à la variable qui référence l'objet mais il est lié à l'objet. L'objet garde son type durant toute l’exécution du programme. La variable par contre peut référencer différents objets qui vont
En python les noms des variables ne peuvent pas commencer par un chiffre. Le nom se compose des caractères alphanumériques sans caractères spéciaux. Les noms sont sensibles à la casse de caractère.
Prendre l'habitude de nommer correctement ses variables avec des labels explicites. Python intègre un système de génération automatique de la documentation à partir du code.
Par convention les variables sont en minuscules. Par convention également les variables "privées" sont préfixées par le caractère tiret bas '_' (confère PEP 008 http://legacy.python.org/dev/peps/pep-0008/#descriptive-naming-styles).
Dans tous les cas il est fortement déconseillé d'utiliser le formalisme **%%__variable__%%** car utilisé par le langage.
===== Types de base =====
En Python tout est objet. Le type d'une variable est déterminé dynamiquement, pas de déclaration de variable avant l'utilisation. La fonction **type()** renvoi le type de l'objet passé en paramètre:
Le type est important puisqu'il détermine quelles méthodes peuvent être appelées, quel comportement peut avoir l'objet. Parmi les types de base (built-in types) ont distingue les types mutables et les types immuables:
^ Type ^ Mutablilité |
| int, float | immuable |
| complex,bool | immuable |
| str | immuable |
| list | mutable |
| dict | mutable |
| set | mutable |
| frozenset | immuable |
Un objet mutable peut être modifié en place. Un objet immuable ne peut être modifié, python doit réallouer un espace mémoire différent et mettre à jour la référence.
>>> # initialisation d'une variable de type entier:
...
>>> a = 3
>>> type (a)
>>> # une variable flottante
...
>>> f = 2.1
>>> type (f)
>>> # une variable chaine de caractères
...
>>> c = 'c'
>>> type(c)
>>> c = 'ma chaine'
>>> type(c)
Remarques:
- La fonction **type()** retourne le type de l'objet référencé par la variable passée en paramètre.
- Python ne distingue pas caractère et chaîne de caractères, le type est 'str' (string).
La fonction **isinstance()** est plus largement utilisée pour la mise en œuvre des mécanismes d'héritage. Elle permet de savoir si un objet est d'un type donné.
De plus comme python est un langage à typage dynamique, isinstance() permet de s'assurer qu'une variable passée à une fonction à l’exécution est du type attendu, puisque contrairement à un langage typé statiquement, aucune garantie ne peut être apportée en amont.
Depuis python 3.5 les **Type hints** ou annotations de types permettent d'expliciter les suppositions qui sont faites par le programmeur pour le bon fonctionnement de son code. Totalement optionnelles et meme si elles sont présentes elles ne sont pas utilisées à l'exécution par l'interpréteur. Cependant elles permettent a des outils externes comme mypy d'effectuer des contrôles poussés de correction de code.
==== La liste ====
Une liste est un ensemble **ordonné** d'objets pouvant être de différents types. Les éléments de l'ensemble sont accessible via leur **index**.
>>> ma_liste = ['toto', 2, 2.1, 'z']
Le contenu d'une liste peut être modifié, on peut ajouter/retirer des éléments à l'aide des méthodes pop() et insert(). On peut également réaffecter un élément de la liste via l'index:
>>> ma_liste[0] = 'tata'
>>> ma_liste.pop(2)
2.1
>>> ma_liste.insert(3, 2.5)
>>> print (ma_liste)
['tata', 2, 'z', 2.5]
Notes plus détaillées à propos des [[dev:python:core:listes|listes en Python]].
==== Le tuple ====
Le tuple est un objet séquence très proche de la liste, sa principale différence est d'être immuable. Le tuple contient un ensemble ordonné et figé d'objets pouvant être de types différents. On ne peut pas ajouter/supprimer/modifier les éléments d'un tuple. Le principal avantage d'un tuple est le temps d’accès à l'information bien plus court que la liste.
# La syntaxe de déclaration d'un tuple utilise les parenthèse:
>>> tuple = (1, 'toto', 2.2, True);
# La syntaxe abrégée de déclaration d'un tuple
>>> t2 = 1, 'tata', 'tutu'
>>> print t2
(1, 'tata', 'tutu')
# Si le tuple a un seul élément, bien terminer la déclaration par la virgule lors de l'utilisation de la notation abrégée
>>> t3 = 'test',
>>> print t3
('test',)
>>> tuple[1] = 'tata' # Réaffecter un élément d'un tuple provoque une erreur.
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object does not support item assignment
Chaines, listes et tuples sont des **séquences**, c'est à dire des **structures de données ordonnées** dont les éléments sont indexés et accessibles via leur index.
==== Slicing ====
Le slicing permet de sélectionner un sous ensemble dans une séquence (liste, tuple, string, etc) en spécifiant:
- l'index du premier élément
- l'index du dernier élément+1
- le pas
Remarque 1: Si les arguments ne sont pas spécifiés, ils ont des valeurs par défaut (min, max et pas de 1).
Remarque 2: si le pas est négatif on parcourt la variable en sens inverse, pratique pour inverser une chaîne.
>>> texte="un joli texte"
>>> sous_chaine=texte[3:7]
>>> print(sous_chaine)
joli
>>> sous_chaine=texte[:7]
>>> print(sous_chaine)
un joli
>>> sous_chaine=texte[::-1]
>>> print(sous_chaine)
etxet iloj nu
Notes plus détaillées à propos des [[dev:python:core:tuples|tuples]].
==== Le dictionnaire ====
Un dictionnaire est un ensemble dynamique non ordonné (on parle aussi de tableau associatif). On accède aux élément via la clé.
# déclaration d'un dictionnaire
>>> d = {'prenom': 'yoann', 'nom': 'BEZILLE', 'age' : 32, 42: 'test'}
Remarque: Pour copier un dictionnaire utiliser la méthode **copy()**
Confère wiki [[dev:python:core:type_dictionnaire|dictionnaires]] pour plus de détails.
===== Définition d'un objet =====
class monType(object):
"""Toute classe par defaut herite du type de base object"""
def __init__(self,...):
"""Le constructeur de la classe monType"""
def methode_1(self,...):
def methide_2(self,...):
Remarques:
- Attention le premier argument des méthodes est toujours self.
- Toute classe de base hérite d'**object**.
===== Instanciation d'une objet =====
mon_objet = maClasse(valeur1, valeur2, valeur3)
Pour instancier un objet de la classe maClasse, j'utilise ne nom de la classe et entre parenthèses je place les valeurs à transmettre au constructeur.
Exemple
Se placer dans le dossier ~/dev/python contenant le fichier compte.py et lancer l’interpréteur python
>>> import compte
>>> mon_compte = compte.compte("2221");
>>> mon_compte.depot(2000000000);
>>> mon_compte.affiche()
le solde du compte '2221'est 2000000000.00
>>>
===== Paradigme objet =====
En python tout est objet, quelques méthodes d'utilité générale:
* **dir()** pour lister objets et méthodes
* **help()** pour afficher l'aide intégrée au code
* **type()** pour connaître le type d'un objet
* **id()** retourne l'adresse mémoire d'un objet
* **eval()** pour évaluer une chaîne de caractères
>>> # La variable c1 référence un objet str
>>> c1 = 'say'
>>> # création d'une variable c2 référencant le meme objet
>>> c2 = c1
>>> c2 is c1
True
>>> print(f"adresse c1: {id(c1):0x}")
adresse c1: 7fc861d4b0b0
>>> print(f"adresse c2: {id(c2):0x}")
adresse c2: 7fc861d4b0b0
>>> # Comme un objet str est immuable la concaténation
>>> # ci-dessous produit une nouvelle référence associée a c1
c1 += ' hello'
>>> c2 is c1
False
>>> print(f"adresse c1: {id(c1):0x}")
adresse c1: 7fc8535cf6b0
===== Import et modules =====
Pour utiliser une fonction présente dans un fichier, utiliser la directive import avec le nom de fichier sans le suffixe .py
Remarque: dans un module, les variables globales sont accessibles. Ils est possible de modifier la valeur de la variable du module, cela peut être problématique: pour éviter cela on utilisera les objets.
===== Tester si le script est directement appelé ====
Il est possible de tester si le script est appelé directement ou via un import avec le code ci-dessous:
# Déclaration variables et directives importables
# par d'autres scripts
if __name__ == "__main__":
# Le Bloc ci dessous n'est exécuté que
# lorsque le script est appelé directement
# mais pas lorsqu'il est importé
Ce branchement conditionnel est souvent utilisé pour des tests unitaires.
===== L' introspection =====
Les objets python savent qui ils sont, c'est l'introspection. La fonction **dir()** permet de lister les attributs/méthodes de tout objet.
>>> dir (ma_liste)
['__add__', '__class__', '__contains__', '__delattr__',..., 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
===== La documentation =====
Python intègre nativement la documentation au code (c'est la documentation en ligne contenue dans l'attribut __doc__ d'une instance). Lors de la définition d'une classe ou d'une méthode, ajouter un texte entre triple guillemets sous la définition pour qu'elle soit intégrée (c'est la "doc string")
Remarque: La position du commentaire est importante. Seuls les commentaires entre triples guillemets sous la déclaration seront exportés. Attention a bien indenter le commentaire.
Pour obtenir de l'aide, depuis l’interpréteur utiliser la commande **help()**, cela fonctionne meme sur les types de base:
help(str)
La commande **dir()** permet de lister les symboles existants au sein d'un module:
>>> import math
>>> dir(math)
'__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
>>>
La commande pydoc se charge d'extraire et de formater la doc incluse dans le code. Pour consulter la documentation d'un module nommé test.py
Remarque: Pour les scripts python3 utiliser pydoc3
$ pydoc test.py
===== Débogage des scripts =====
Python fournit un le module **pdb**.
import pdb
si vous utilisez l’interpréteur ipython, ipdp
pip install ipdb
===== Tests et tests unitaires =====
Pour les tests le mot clé **assert** permet de lever une exception **AssertionError** lorsque l'expression évaluée n'est pas vraie.
===== Références =====
* https://docs.python.org/dev/howto/index.html
* http://wiki.deimos.fr/index.php?title=Introduction_au_Python
* http://sametmax.com/debugger-en-python-les-bases-de-pdb/