Aller au contenu

Python

De Banane Atomic

Liens

Astuces

# Définit l'interpréteur à utiliser
#!/usr/bin/env python

print('Text', value, sep = ' _ ')  # default sep is space

import os
scriptFolderPath = os.path.realpath("")

""" Docstring
Multi-lignes """

# Quitte le script
exit("texte")

# déclarer une variable nulle
variable1 = None

# Mettre le programme en attente pendant N secondes
import time
time.sleep(1)

# Exécute un autre script
execfile(r'chemin vers le fichier de script')

# test si sudo a été utilisé pour lancer le script
import os
if os.getuid() == 0:
    # ok
else:
    print("Lancer le script avec sudo.")

Conventions de nommage

Package et modules (fichiers *.py) lower_case_with_underscores
Classes CapWords (PascalCase, UpperCamelCase)
Fonctions lower_case_with_underscores
Méthodes “privées” _start_with_underscores

import

Fichier:Py.svg
import my_file
my_file.my_function()

from my_file import my_function
my_function()

import os, sys
sys.path.append(os.path.abspath('..'))
from folder.file import function  # folder is a parent folder

Console I/O

# Affiche le texte et attend une saisie
nombre = input('Entrez un nombre > ')
print(nombre)

# Effacer la console
os.system('cls')

# Afficher du texte dans la console. %r est remplacé par la variable correspondante.
print('Texte ', variable1, ' suite')
print('Texte ' + str(variable1) + ' suite')
print('Texte {} suite'.format(variable1))

# Afficher du texte sans retour à la ligne mais avec un espace à la fin
print("Texte", end="")

# Afficher du texte sans retour à la ligne et sans espace à la fin
import sys
sys.stdout.write("Texte")

Arguments passés au script

optparse est obsolète, utiliser argparse
from argparse import ArgumentParser

parser = ArgumentParser(prog='myprogram', description='Courte description du programme')
# prog permet de redéfinir le nom du programme (défaut: sys.argv[0])

# argument positionnel
parser.add_argument('action')

# argument optionnel avec une valeur
parser.add_argument('-f', '--file')

# argument optionnel associé à une constante booléenne
parser.add_argument("-r", "--recursive", action="store_true", help="description de l'argument. [default: %(default)s]"))

# -h est déjà inclus par défaut
parser.add_argument('-v', '--version', action='version', version='%(prog)s v1.0 (2017-07-03)')

# Process arguments
args = parser.parse_args()

file = args.file            # -f file.txt → file.txt
recursive = args.recursive  # -r → True
usage: myprogram [-h] [-f FILE] [-r] [-v] action

Courte description du programme

positional arguments:
  action

optional arguments:
  -h, --help             show this help message and exit
  -f FILE, --file FILE
  -r, --recursive        description de l'argument. [default: False]
  -v, --version          show program's version number and exit

sys.argv

import sys

# nombre d'arguments (+1 pour le nom du script)
if len(sys.argv) != 2:
    exit('Utilisation: {} argument1'.format(os.path.basename(sys.argv[0])))

sys.argv[0]    # nom du script
sys.argv[1]    # premier argument

String

Python 3 utilise la table unicode pour les string.
# longueur de la chaîne
longueur = len(string1)  # 2

# r : raw string literals. \ n'est pas interprété
print(r'test \n test')  # test \n test

# rechercher un sous-élément, retourne l'index du sous-élément sinon -1
string2.find('deux')

# remplacer les . par des !
string2 = string1.replace(".", "!")

Concatenation and interpolation

Fichier:Py.svg
# concatenation
string1 = 'un'
string2 = string1 + ' - deux'

# l'opérateur += est déjà optimisé, un StringBuilder est inutile
string2 += ';'

# interpolation
string2 = f'{string1} - deux'

Comparison

Fichier:Py.svg
string3 = ''
# test si une chaîne est vide: empty strings are "falsy"
if not string3:
    # chaine1 est vide

# comparaison de string en ignorant la casse
if string1.lower() == string2.lower():

if string1.endswith('xxx'):

Split

Fichier:Py.svg
# split et rsplit
url = 'http://www.domain.fr/fichier.txt'
url.split('/')      # ['http:', '', 'www.domain.fr', 'fichier.txt']
url.split('/')[-1]  # fichier.txt

# maxsplit = 1. Nb de groupes: maxsplit + 1 (le reste)
url.split('/', 1)   # ['http:', '/www.domain.fr/fichier.txt']
url.rsplit('/', 1)  # ['http://www.domain.fr', 'fichier.txt']
url.rsplit('/', 1)[1]  # fichier.txt

Nombres

entier = 1
décimal = 2.0

somme = entier + décimal  # 3.0
int(décimal)   # 2
float(entier)  # 1.0

Booléen

vrai = True
faux = False

int(vrai)  # 1
int(faux)  # 0

datetime

import datetime
from dateutil.parser import parse

ma_variable = f'text-{datetime.date.today()}-text}'  # text-yyyy-mm-dd-text
ma_variable = "text-%s-text" % ( datetime.date.today() )

parse('03/08/2017')                 # 2017-03-08 00:00:00
parse('03/08/2017', dayfirst=True)  # 2017-08-03 00:00:00

Python: Get Today’s Current Date and Time

If else

if a == b and a == c or b != c:
    commande1
    commande2
elif not b == c:
    commande3
else:
    commande4

if myInteger:
    print('myInteger is defined and not equal to 0')
if myString:
    print('myString is defined and not an empty string')

# sur un ligne
commande1 if a == b else commande2

Liste / Tableau / Array

myList = []
# une liste peut contenir des éléments hétérogènes: int, string
myList = ['zéro', 1, 'deux', 'trois']

myList[1]      # 1
myList[1] = 'un'
myList[:2]     # éléments 0 ≤ index < 2: zéro 1
myList[2:]     # éléments 2 ≤ index : deux trois
myList[1:3]    # éléments 1 ≤ index < 3: 1 deux
myList[-1]     # dernier élément: trois
myList[-2:]    # 2 derniers éléments: deux trois
myList[-3:-1]  # 3 derniers éléments moins le dernier: 1 deux
myList[0:4:2]  # 1 élément sur 2: zéro deux

myList.append(4)  # ['zéro', 'un', 'deux', 'trois', 4]

del myList[1]  # supprime l'élément à l'index 1
myList.pop(1)  # supprime et retourne l'élément à l'index 1
myList.remove('deux')  # supprime la première valeur correspondante

# nombre d'éléments dans myList
len(myList)  # 5

# test si myList contient 4
if 4 in myList:

# trie de la liste
myList.sort()
myList.sort(reverse=True)
# trier des caractère utf-8
import locale
# forcer la locale
locale.setlocale(locale.LC_ALL, '')
#print(locale.getlocale())
myList.sort(key=locale.strxfrm)

# concaténation des éléments d'une liste
''.join(myList)

# générer une liste d'entier
range(6)         # [0, 1, 2, 3, 4, 5]
range(5, 11)     # [5, 6, 7, 8, 9, 10]
range(0, 11, 2)  # [0, 2, 4, 6, 8, 10]

List Comprehensions: filtre et projection

# filtre des nombres pair à partir de [0, 1, 2, 3, 4, 5]
[x for x in range(6) if x%2 == 0]  # [0, 2, 4]
# équivalent avec filter
filter(lambda x: x%2 == 0, range(6))

# projection de [0, 1, 2, 3, 4, 5] où chaque élément est multiplié par 2 (x*2)
[x*2 for x in range(6)]  # [0, 2, 4, 6, 8, 10]
# équivalent avec map
map(lambda x: x*2, range(6))

yield

Avec yield, le code n'est pas exécuté à l'appel de la méthode mais à la demande.

def my_method(max_value):
    for i in range(max_value):
        yield i*2

my_generator = my_method(10)  # le code de my_method n'est pas exécuté ici
for i in my_generator:        # mais ici, à la demande
  print(i)

Affiche multi-colonnes

for a,b,c in zip(myList[::3], myList[1::3], myList[2::3]):
    print('{:<30}{:<30}{:<}'.format(a,b,c))

Tuple

# équivalent d'une liste mais
# - on ne peut ni ajouter ni supprimer des éléments
# - les valeurs des éléments ne peuvent être modifiées
myTuple = ('un', 'deux', 'trois')

Dictionnaires

dico = {}
dico = {'key a': 'value a', 'key b': 'value b'}

dico['key a']  # value a
dico['key c']  # KeyError: 'key c'
dico.get('key c')  # None
dico.get('key c', 'Unknown')  # Unknown

dico['key c'] = 'value c'
del dico['key b']
    
for k, v in dico.items():
    print(k, v)

Set

Collection d'éléments unique indexés. Utilisé pour les opérations telles que intersection ou difference

Fichier:Py.svg
my_set = set(my_list)

diff = my_set.difference(my_other_set)

For

animaux = ['chat', 'serpent']
for animal in animaux:
    print(animal)
    break # sort de la boucle for
    continue # passe à l'itération suivante
else:
    # on entre dans else une fois toutes les itérations parcourues
    # donc pas si break a été utilisé
    print("fin sans break")

# for avec une condition if
list = ['aa', 'ab', 'ac']

[print(x[1]) for x in list if x.endswith('b')]

for y in (x[1] for x in list if x.endswith('b')):
    print(y)

# for avec une condition lambda expression
z = lambda x: print(x[1]) if x.endswith('b') else None
[z(x) for x in list]

for folder in filter(lambda folder: os.path.isdir(os.path.join(cwd, folder)), os.listdir()):

While

i = 0
while i < 10:
    i += 1

list = [1, 2, 3]
while 3 in list:

Exceptions

try:
    raise Exception('EEE')
except ImportError:  # capture les exceptions de type ImportError
    pass
except (RuntimeError, TypeError, NameError):  # capture les exceptions de type RuntimeError, TypeError et NameError
    pass
except Exception as e:  # permet d’accéder à l'instance de l'exception
    print(e)  # affiche le message d'erreur
except:  # capture toutes les exceptions restantes
    raise  # relance l'exception précédemment capturée
else:
    # code exécuté si aucune exception n'est lancée
finally:
    # code exécuté avec ou sans exception

Expressions rationnelles

Méthodes Description
match() Determine if the RE matches at the beginning of the string.
search() Scan through a string, looking for any location where this RE matches.
findall() Find all substrings where the RE matches, and returns them as a list.
finditer() Find all substrings where the RE matches, and returns them as an iterator.
split() Split the string into a list, splitting it wherever the RE matches
sub() Find all substrings where the RE matches, and replace them with a different string
subn() Does the same thing as sub(), but returns the new string and the number of replacements
Compilation Flags
DOTALL, S Make . match any character, including newlines
IGNORECASE, I Do case-insensitive matches
MULTILINE, M Multi-line matching, affecting ^ and $
^ devient le début de ligne au lieu du début du texte
$ devient la fin de ligne au lieu de la fin du texte

search

import re

# compiler l'expression rationnelle si elle est utilisée plusieurs fois
regex = re.compile('expression rationnelle')
match = re.search(regex, 'texte')
if match:
    print('Une correspondance a été trouvée: ', match.group(0))
else:
    print "l'expression %r n'a pas été trouvée" % (regex)

# Utilisation des groupes
match = re.search(r"(\w+) (\w+)", "John Smith")
print match.group(0) # 'John Smith', retourne la correspondance complète
print match.group(1) # 'John', retourne la correspondance avec le groupe 1
print match.group(2) # 'Smith', retourne la correspondance avec le groupe 2

finditer

for match in re.finditer('(?P<groupe1>\d+) (?P<groupe2>\d+)', texte):
    # regroupe les résultats dans un dictionnaire au lieu d'un tableau
    match.groupdict()
    # match.group('groupe1')

Substitution

import re

nouveauTexte = re.sub('expression rationnelle', 'substitution', 'texte')

# Avec le drapeau MULTILINE
nouveauTexte = re.sub(re.compile('expression rationnelle', re.MULTILINE), 'substitution', 'texte')
# Normalement avec Python 2.7 on devrait pouvoir écrire
nouveauTexte = re.sub('expression rationnelle', 'substitution', 'texte', flags=re.MULTILINE)
# mais ça ne marche pas avec IronPython 2.7

# Exemple avec les groupes
re.sub('(texte) (intéressant)', '\g<2> \g<1>', 'un texte intéressant')
# un intéressant texte

Fonctions

Define the function before you call it.
def my_function(arg1, arg2):
    '''
    Description de la fonction.
    :param arg1: Description de l'argument.
    :param arg2: Description de l'argument.
    :return: Description du contenu retourné par la fonction.
    '''
    return f'{arg1} - {arg2}'

# type hinting, permet d'informer sur les types attendus des arguments des fonctions ainsi que du type de retour
def my_function(arg1: str, arg2: str) -> str:
    return f'{arg1} - {arg2}'

# appel de la méthode
my_function('start', 'end')  # start - end

# argument optionnel
def my_function(arg1, arg2 = 'end'):
    return f'{arg1} - {arg2}'

# appel de la méthode
my_function('start')  # start - end
my_function('start', 'next')  # start - next

# liste d'arguments. args est un tuple.
def my_function(*args):
    return ' - '.join(args)

# appel de la méthode
my_function('start', 'next', 'end')  # start - next - end

# dictionnaire d'arguments (keywords arguments).
def my_function(**kwargs):
    for k, v in kwargs.items():
        yield f'{k} - {v}'

# appel de la méthode
'\n'.join(my_function(un = 1, deux = 2))
# un - 1
# deux - 2

Modules et Packages

  • Les modules sont des fichiers *.py
  • Les packages sont des dossiers qui représentent les namespaces contenant des modules (fichier *.py). Ils contiennent un fichiers __init__.py
__init__.py
# par défaut tous les modules d'un package sont accessible
# pour rendre des modules inaccessibles, il faut redéfinir __all__ en listant seulement les modules accessibles
__all__ = ["module1"]

Calling a module as a script

my_module.py
if __name__ == "__main__": # if the module is executed directly as a script
    main()
  • if the module is imported, __name__ is set to the module's name my_module
  • if the module is executed directly as a script, __name__ is set to main

POO

Classe

my_class.py
class MyClass(Class2):  # hérite de Class2
    """ documentation pour la classe
    multi-lignes """

    # accessible depuis la classe et ses instances.
    # tant que l'instance ne modifie pas la valeur elle est égale à celle de la classe
    # l'instance peut modifier la valeur sans que la valeur de la classe soit modifiée
    classAttribute = 0

    # Initialiseur
    def __init__(self, someValue):
        """ documentation du constructeur """
        self.MyAttribute = someValue

    # méthode d'instance, self représente l'instance de l'objet courant
    def myMethod(self, Arg1, Arg2 = 0):
        self.MyAttribute = Arg1
        return Arg2

    def __privateMethod(self, Arg1, Arg2 = 0):
        self.MyAttribute = Arg1
        return Arg2

    # méthode de classe, cls représente la classe de l'objet courant
    @classmethod
    def myClassMethod(cls):
        cls.classAttribute += 1

    # méthode statique
    @staticmethod
    def myStaticMethod():
        MaClass.classAttribute += 1
main.py
from my_class import MyClass

if __name__ == '__main__':
    
    myObject = MyClass("Arg1")
    
    # Afficher tous les attributs d'un objet
    print(dir(myObject))
    
    print(myObject.MyAttribute)  # Arg1
    
    print(myObject.classAttribute)  # 0
    print(MyClass.classAttribute)  # 0
    
    retour = myObject.myMethod("Arg2", 10)
    print(myObject.MyAttribute)  # Arg2
    print(retour)  # 10

    MyClass.myClassMethod()
    print(MyClass.classAttribute)  # 1
    
    MyClass.myStaticMethod()
    print(MyClass.classAttribute)  # 2
Tous les arguments sont passés par référence

Héritage

class A():
    def __init__(self):
        self.Attribute1 = 10
    
    def Who(self):
        print("A")

class B():
    def Who(self):
        print("B")

# il est possible d'hériter de plusieurs classes, l'héritage se fait dans l'ordre d'écriture A puis B
class C(A, B):
    # si aucun constructeur n'est définit, c'est celui de la première classe parente qui est appelé (A)
    def __init__(self):
        # appel explicite du constructeur de la classe parente A
        A.__init__(self)
        # code équivalent
        super(B).__init__(type(self))

    # surcharge de la méthode Who
    def Who(self):
        # super().Who()
        # A.Who(self)
        print("B")

Variables globales

globvar = 0

def modifyGlobvar1():
    # globvar est ici zûne variable locale à la fonction modifyGlobvar1
    globvar = 1

def modifyGlobvar2():
    # pour pouvoir modifier la variable globale, il faut utiliser le mot-clé global
    global globvar
    globvar = 2

def printGlobvar():
    # comme aucune variable globvar n'existe au niveau de la fonction, on va chercher un niveau plus haut pour l'accès en lecture
    print(globvar)

printGlobvar()  # 0

modifyGlobvar1()
printGlobvar()  # 0

modifyGlobvar2()
printGlobvar()  # 2

Fichiers

Création - suppression - copie

import os

# déplacer un fichier, le fichier de destination est écrasé s'il existe
os.rename('/dossier1/fichier1', '/dossier2/fichier2')

# supprimer un fichier
os.remove(r'chemin vers le fichier')

Lecture - écriture

# mode d'ouverture:
# r → read
# w → write, écrase le contenu du fichier
# a → append, ajoute à la fin du fichier, s'il n'existe pas le fichier est créé
with open('chemin vers le fichier', 'r') as fichier:

    # lit tout le contenu du fichier et renvoie une chaîne de caractères unique
    contenuFichier = fichier.read()

    # lecture ligne par ligne
    for line in fichier.read_lines():
    
    fichier.write('texte')

Télécharger un fichier

import requests
import shutil

url = 'http://www.doamin.fr/file.zip'

local_filename = url.split('/')[-1]  # file.zip

response = requests.get(url, stream=True)
    
with open(local_filename, 'wb') as f:
    shutil.copyfileobj(response.raw, f)

Répertoires

Création - suppression - copie

import os, shutil
# création d'un répertoire
os.mkdir(r'chemin vers un dossier')
# supprimer un dossier et son contenu récursivement
shutil.rmtree(r'chemin vers le dossier')
# copier un dossier et son contenu récursivement
shutil.copytree('source', 'destination')
# ignore permet de filtrer les fichiers et dossiers à ne pas copier
shutil.copytree('source', 'destination', ignore=shutil.ignore_patterns('*.log', '*.py'))

Parcourt

import os, glob

# renvoie la liste des noms des dossiers du répertoire passé en paramètre
folder_names = [x.name for x in os.scandir('/media/data') if x.is_dir()]
# renvoie la liste des chemins des fichiers du répertoire passé en paramètre
file_names = [x.path for x in os.scandir('/media/data') if x.is_file()]

# renvoie la liste des noms des fichiers et dossiers du répertoire passé en paramètre
for name in os.listdir('/path/folder'):
# no parameter in listdir means current directory
for filename in [x for x in os.listdir() if re.search('*.mp4', x)]:

# utilisation des caractères * et ? dans le chemin
for path in glob.glob(r'/chemin/vers/un/dossier/*.ext'):

# parcourt récursif du dossier passé en paramètre
#  root contient le chemin complet vers le dossier parcourut
#  dirs contient la liste des sous-répertoires du dossier parcourut
#  files contient la liste des fichiers du dossier parcourut
# le paramètre topdown=False permet de parcourir l'arbre de bas en haut
for root, dirs, files in os.walk(r'chemin vers un dossier'):

Path

import os
from os import path

# test si le chemin correspond à un fichier ou à un dossier
if path.exists(r'chemin vers un dossier'):
# test si le chemin correspond à un fichier
if path.isfile(r'chemin vers un fichier'):
# test si le chemin correspond à un dossier
if path.isdir(r'chemin vers un dossier'):

# obtient le chemin vers le répertoire contenant le fichier spécifié
directoryPath = path.dirname(r'chemin vers le fichier')
# obtient uniquement le nom du fichier spécifié
fileName = path.basename(r'chemin vers le fichier')
# get the filename without the extension and the extension
path.splitext(r'/path/file.ext')  # [0]=file [1]=.ext

# donne le chemin courant
os.getcwd()
# change le chemin courant
os.chdir('/path')
# concatène intelligemment deux chemins
path.join('/path', 'file-name.ext')
# renvoit le chemin absolu courant concaténé au chemin spécifié
path.realpath('/path')
# renvoit le chemin vers le dossier personnel de l'utilisateur courant (C:\Users\Nicolas)
path.expanduser('~')
# teste si le chemin est un point de montage
path.ismount('/path')

# chemin vers le script courant tel qu'il a été passé dans la ligne de commande
__file__
sys.argv[0]

logging

Logger objet qui permet d'appeler les méthodes de log
Handler envoie le message de log vers une destination: console, fichier
Filter filtre les messages de log
Formatter décrit la mise en forme du message de log
import logging

logging.basicConfig(
  # changement du format d'affichage
  format = '%(asctime)s %(levelname)s - %(filename)s,%(funcName)s,%(lineno)d: %(message)s',
  # changement du format de date
  datefmt = '%d/%m/%Y %H:%M:%S'
  # changement du niveau de log (défaut: WARNING)
  level = logging.DEBUG,
  # log dans un fichier (défaut: console)
  filename = 'app.log')
# 06/08/2017 17:46:30 WARNING - main.py,<module>,15: LOG !!!

# logging représente le logger root
logging.warning('LOG !!!')  # WARNING:root:LOG !!!

try:
    # ...
except Exception, e:
    logging.error('Failed', exc_info=True)  # affiche la Traceback

Handlers

Permet de loguer dans plusieurs destinations en même temps.

import logging.handlers

logger = logging.getLogger()  # logger root
logger.setLevel(logging.NOTSET)  # par défaut à WARNING

filehandler = logging.handlers.RotatingFileHandler('main.log', maxBytes=1024, backupCount=1)
filehandler.setLevel(logging.ERROR)
filehandler.setFormatter(
	logging.Formatter(
		'%(asctime)s %(levelname)s - %(name)s,%(filename)s,%(funcName)s,%(lineno)d: %(message)s',
		datefmt = '%d/%m/%Y %H:%M:%S'))
logger.addHandler(filehandler)

consolehandler = logging.StreamHandler()
consolehandler.setLevel(logging.INFO)
consolehandler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
logger.addHandler(consolehandler)

logger.error('Test !!!')

Loggers

Sélectionne le logger à utiliser en fonction du besoin.

# __name__ correspondant à Package.Module, permet de récupérer le logger du module courant
logger = logging.getLogger(__name__)
# si aucun logger ne correspond à Package.Module, on cherche son parent Package, sinon root

Fichier de configuration

import yaml  # installer python-yaml

with open('logging_configuration.yaml', 'rt') as f:
    config = yaml.safe_load(f.read())

logging.config.dictConfig(config)
logging_configuration.yaml
version: 1  # obligatoire
disable_existing_loggers: False  # True par défaut, désactive les loggers définis avant l'appel dictConfig
formatters:
    file_formatter:
        format: "%(asctime)s %(levelname)s - %(name)s,%(filename)s,%(funcName)s,%(lineno)d: %(message)s"
    console_formatter:
        format: "%(levelname)s - %(message)s"

handlers:
    console:
        class: logging.StreamHandler
        level: INFO
        formatter: console_formatter
        stream: ext://sys.stdout

    file_handler:
        class: logging.handlers.RotatingFileHandler
        level: ERROR
        formatter: file_formatter
        filename: info.log
        maxBytes: 1024
        backupCount: 1
        encoding: utf8

root:
    level: NOTSET  # par défaut à WARNING, ignore donc DEBUG et INFO
    handlers: [console, file_handler]

coloredlogs

pip install coloredlogs
# Installe coloredlogs et sa dépendance humanfriendly
logging_configuration.yaml
formatters:
    colored_console_formatter:
        (): coloredlogs.ColoredFormatter
        level_styles:  # black, blue, cyan, green, magenta, red, white, yellow
            debug:
                color: green
            verbose:
                color: magenta
            info:
                color: cyan
            warning:
                color: yellow
            error:
                color: red
            critical:
                color: red
                bold: True
        field_styles:
            hostname:
                color: magenta
            programname:
                color: cyan
            name:
                color: blue
            levelname:
                color: white
                bold: True
            asctime:
                color: green
        format: "%(levelname)s - %(message)s"

python-sh

Permet d'appeler les commandes bash sous forme de méthodes python.

from sh import pgrep

print(pgrep('firefox'))
# installation
pacman -S python-sh

Appel système

import subprocess

# appel d'une commande bash
subprocess.run(["ls", "-a", "-l"])  # permet de gérer les espaces dans les arguments
subprocess.run("ls -a -l", shell=True)
# capture output
completedProcess = subprocess.run(["ls", "-a", "-l"], capture_output=True)
print(completedProcess.stdout.decode("utf-8")) # decode: bytes → string
# redirige stdout
subprocess.run(["ls", "-l"], stdout=subprocess.DEVNULL)

# appel d'un script bash, inutil d'ajouter shell=True si le script contient #!/usr/bin/bash
completedProcess = subprocess.run("./my-script.sh")
print(completedProcess.returncode) # 0

# lance une subprocess.CalledProcessError si le script retourne une valeur ≠ de 0
try:
    subprocess.run("./my-script.sh", check=True)
except subprocess.CalledProcessError as e:
    print(e)  # Command './my-script.sh' returned non-zero exit status 127.

Pipe

import subprocess
from subprocess import PIPE

# ls ~ | tail -n 3 | xargs

output = subprocess.check_output("ls ~ | tail -n 3 | xargs", stderr=subprocess.STDOUT, shell=True)
# check_output is equivalent to run(..., check=True, stdout=PIPE).stdout

ls = subprocess.Popen(['ls', '~'], stdout=PIPE, stderr=PIPE)
tail = subprocess.Popen(['tail', '-n', '3'], stdin=ls.stdout, stdout=PIPE, stderr=PIPE)

xargs = subprocess.Popen(['xargs'], stdin=tail.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = xargs.stdout.read().strip().decode("utf-8")

xargs = subprocess.run(['xargs'], stdin=tail.stdout, stdout=PIPE, stderr=PIPE)
output = xargs.stdout

check_call

Si le code de retour n'est pas 0, lève une CalledProcessError.

import subprocess
from subprocess import CalledProcessError, DEVNULL

try:
    subprocess.check_call(['ping', '-c', '1', 'remote'], stdout=DEVNULL)
except CalledProcessError as e:
    print(f'{e.returncode} - {e.cmd} - {e.output} - {e.stdout} - {e.stderr}')
    exit(f'{e.cmd[3].upper()} is not reachable.')

HTTP request

Fichier:Py.svg
import requests  # pip install requests

response = requests.get('http://api.open-notify.org/astros.json')  # type: requests.models.Response
json = response.json()  # type: dict

JSON data

Fichier:Py.svg
import json

# load json content from a file
with open('/path/file.json') as my_file:
    my_dict = json.load(my_file)

# load json content from a string
my_dict = json.loads(json_content)

# access content
my_dict['my_key']

Serialization

Fichier:Py.svg
data = {"name": "John", "age": 30}

# json serialization
import json
json_serialized = json.dumps(data)
json_deserialized = json.loads(json_serialized)

# binary serialization with pickle
import pickle
pickle_serialized = pickle.dumps(data)
pickle_deserialized = pickle.loads(pickle_serialized)

Process

Fichier:Py.svg
while ('firefox' in (p.name() for p in psutil.process_iter())):
    input('Close Firefox to continue.')

ASQ - LINQ

Préférer l'utilisation native de List Comprehensions

Équivalent de LINQ pour Python.

pip install asq
from asq.initiators import query

words = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]

# premier mot commençant par f: four
query(words).first(lambda w: w.startswith('f'))

SSH avec Fabric

from fabric import Connection

result = Connection('remote-hostname').run('ls', hide=True)
msg = "Ran {0.command!r} on {0.connection.host}, got stdout:\n{0.stdout}"
print(msg.format(result))

# ferme la connection à la sortie du with
with Connection('remote-hostname', user='usr', password='pwd') as c:
    c.get('/path/file.ext', local='/path/folder')

Archives

zip

import zipfile

# extraire tous le contenu de l'archive dans le dossier courant
with zipfile.ZipFile(path) as z:
    z.extractall()

gzip

import gzip

with gzip.open('/chemin/fichier.gz', 'rb') as f:
    content_in_bytes = f.read()
content = content_in_bytes.decode("utf-8")

CSV

with open(path, newline='') as csvfile:
    reader = csv.reader(csvfile, delimiter=';')
    for row in reader:
        print(row)

    # utilise la première ligne comme clés pour le dictionnaire
    reader = csv.DictReader(csvfile, delimiter=';')
    for row in reader:
        print(row['nom_colonne_1'], row['nom_colonne_5'])

Virtual Environments

Self-contained directory tree that contains a Python installation for a particular version of Python, plus a number of additional packages.

# create the virtual environment
python3 -m venv my-env --prompt '|> my-app <| '
# activate the virtual environment
source my-env/bin/activate
# create the virtual environment in the folder my-env
python -m venv my-env --prompt 'my-app'
# default prompt is my-env

# activate the virtual environment
.\my-env\Scripts\activate
# deactivate the current env
deactivate
# delete an env
rm -r my-env

# from the env, export the requirements
pip freeze > requirements.txt
# from the env, install the requirements
pip install -r requirements.txt

pip

Gestionnaire de paquets pour python.

Il est déconseiller d'installer des paquets avec sudo.
Use python3 -m pip instead of pip
# chercher un paquet
pip search "query"

# install a package
pip install [package-name]
# uninstall a package but not their dependencies
pip uninstall [package-name]

# mettre à jour un paquet
pip install -U paquet
# lister les paquets à mettre à jour
pip list --outdated


# lister tous les paquets installés avec pacman et pip
pip list --format=columns

# lister les paquets installés avec pip seulement
find /usr/lib/python3.9/site-packages -maxdepth 2 -name __init__.py | xargs pacman -Qo | grep 'No package'
find ~/.local/lib/python3.9/site-packages -maxdepth 2 -name __init__.py | xargs pacman -Qo | grep 'No package'
# Ubuntu
find /usr/local/lib/python3.6/dist-packages -maxdepth 2 -name __init__.py | xargs realpath | xargs dpkg -S 2>&1 | grep 'no path found'

# afficher des infos sur un paquet
pip show [package_name]

# lister les paquets et leurs dépendances
pipdeptree
# afficher pourquoi un paquet est installé
pipdeptree --reverse --packages [package_name]
Ubuntu:

Les paquets sont téléchargés dans $HOME/.cache/pip

Les paquets sont installés dans $HOME/.local/lib/pythonX.X/site-packages ou /usr/local/lib/pythonX.X/dist-packages
ArchLinux:

Les paquets à installer se nomment python-pip ou python2-pip
Et les commandes sont pip ou pip2.
Les paquets sont installés dans ~/.local/lib/pythonX.X/site-packages ou /usr/lib/pythonX.X/site-packages

Les exécutables sont installés dans ~/.local/bin ou /usr/bin ?

requirements.txt

# generate the list of required packages
pip freeze > requirements.txt

# install the required packages
pip install -r requirements.txt

Erreurs

TypeError: '>' not supported between instances of 'Version' and 'Version'

Désinstaller la version pip du système et installer celle fournie par pypa.

# upgrade pip (ubuntu)
pip3 install --upgrade pip

pip-safe

Safe and easy pip package manager. Automatically install packages into python environments to not mess up with system packages.

Add /usr/local/bin to your PATH
# system-wide installation of a package
sudo -H pip-safe --system install <package>  
# installs a package to /opt/pip-safe/<package> and symlinks its executable to /usr/local/bin

# list installed packages
pip-safe list

# system-wide installation
sudo mkdir -p /opt/pip-safe
sudo chown [current-user]:[current-group] /opt/pip-safe
python3 -m venv /opt/pip-safe/pip-safe
/opt/pip-safe/pip-safe/bin/pip install pip-safe
sudo chown root:root -R /opt/pip-safe
sudo ln -s /opt/pip-safe/pip-safe/bin/pip-safe /usr/local/bin/pip-safe

uv

A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more

pip install uv

# create MyProject folder with .gitignore, .python-version, pyproject.toml
uv init MyProject

# create .venv then add ruff package
uv add ruff

# run the Ruff linter
uv run ruff check

# create or update the project's dependency lockfile named uv.lock
uv lock

# create .venv, install and remove dependencies
uv sync

VS Code

Extensions
Name Description
Python intelliSense (Pylance), debugging (Python Debugger), formatting, linting, code navigation, refactoring, variable explorer, test explorer
autopep8 formatter
  • Select an interpreter: Ctrl + Shift + PPython: Select Interpreter

Windows installation and configuration

You can install multiple versions side by side and use the Python Launcher for Windows to select the version to run.
  • installation path without admin rights: AppData\Local\Programs\Python
$home\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
function py312 {
    $arguments = (($MyInvocation.Line -replace ('^.*?' + [regex]::Escape($MyInvocation.InvocationName)) -split '[;|&]')[0] -replace '[0-9*]?>>?(&[0-9]| *\S+)').Trim()
    & "$env:LOCALAPPDATA\Programs\Python\Launcher\py.exe" -3.12 $arguments
}