Copier des listes et des objets en python

Copier des listes et des objets en python

La copie de liste est une erreur courante pour le développeur Python. En effet, le débutant en python qui n’a pas appris un langage comme C ou C++ et qui n’a pas de notions de pointeur, référence et valeur de la mémoire risque de commettre l’erreur suivante lors qu’il a besoin de copier 2 listes.

In [6]: l1 = [1, 2, 3, 4]

In [7]: l2 = l1

In [8]: l1
Out[8]: [1, 2, 3, 4]

In [9]: l2
Out[9]: [1, 2, 3, 4]

Ce qui ne provoque aucune erreur puisque c’est autorisé par le langage et que ça peut avoir un sens.
Cependant, si vous faites des modifications à l1 après l’affectation, la liste l2 sur également modifier :

In [10]: l1 = [1, 2, 3, 4]

In [11]: l2 = l1

In [12]: l2
Out[12]: [1, 2, 3, 4]

In [13]: l1[0] = 1000

In [14]: l2
Out[14]: [1000, 2, 3, 4]

In [15]: l1
Out[15]: [1000, 2, 3, 4]

Ici j’ai « copié » les listes et j’ai modifié la première valeur de la liste L2 a été également modifiée et vaut 1000 :

In [14]: l2
Out[14]: [1000, 2, 3, 4]

Mais qu’est ce que c’est que ce merdier!?!

C’est là qu’il faut faire intervenir la notion de pointeur et la notion de valeur.
NB : attention ici je vais simplifié au maximum

La mémoire de votre ordinateur est un ensemble de cases dans lequel vous allez mettre vos données. C’est un peu un ensemble de boites à chaussure où vous auriez rangé vos chaussures.
Pour accéder à une paire de chaussures particulière, il faut préciser dans quelle boite se trouve cette paire. Imaginons que vous soyez un peu psychopathe sur les bords et que vous auriez numéroté vos boites à chaussures.
Le plus simple pour retrouver vos chaussures, est de chercher le numéro de la boite.
En informatique c’est exactement pareil. Pour accéder à une donnée en mémoire on précise le numéro de la boite à chaussure qu.on nomme « adresse » afin d’accéder à la donnée.

Dans le cas d’un exemple de données placées linéairement en mémoire, c’est-à-dire que les données, d’un tableau par exemple, sont les unes à la suite des autres pour faciliter leur manipulation, il est plus simple de manipuler à partir de la première case mémoire que de manipuler toutes les données concernées.

À ce stade, il faut également comprendre qu’un objet qui possède un certain nombre d’attributs et de placer en mémoire et accessible grâce à un numéro de boite, une adresse.

C’est exactement le cas de notre liste qui est un objet et quand je fais :

l2 = l1

En fait, je ne fais pas une copie de la liste, je crée un deuxième objet qui pointe sur le premier objet.
Par conséquent, toutes les modifications de l’un entrainent une modification de l’autre, car les deux objets pointent sur les mêmes données : « la même boîte à chaussure »

Bon, comment on fait une copie de liste ?!

Pour une liste simple, c’est simple (jolie répétition… Je la garde…), il suffit de dire qu’on veut explicitement une copie de la liste :

l2 = l1[:]

Et là plus de problèmes d’objet qui pointe sur les mêmes données puisse que les données ont été dupliquées. L’exemple ci-dessous illustre le procédé :

In [16]: l1 = [1, 2, 3, 4]

In [17]: l2 = l1[:]

In [18]: l1[0] = 1000

In [19]: l2
Out[19]: [1, 2, 3, 4]

In [20]: l1
Out[20]: [1000, 2, 3, 4]i

Ici seul l1 a été modifié, les données de l2 étant complètement indépendantes…

OK donc c’est simple, vous me direz, il y a que les culs à pieds qui se font prendre…

Euh…, non!!!

Les références aux données et les copies dans les objets

Le même genre de problème peut survenir dans la recopie d’objets possédant des listes, par exemple.

Imaginons que je définisse la classe suivante :

class DataConso(object):
    def __init__(self):
        self.l_conso = []

qui possède donc comme attribut une liste. Le problème de copie de donnée se posera de la même manière si on utilise l’opérateur « = », par exemple :

if __name__ == '__main__':
    maconso = DataConso()
    maconso.l_conso.append(10)
    maconso.l_conso.append(20)
    maconso.l_conso.append(30)
    maconso.l_conso.append(40)

    maconso_copie = maconso
    maconso_copie.l_conso[2] = 100

    print('affichage liste originale : ', maconso.l_conso)
    print('affichage liste modifiée : ', maconso_copie.l_conso)

Dans cet exemple, je mets des valeurs dans la liste définit comme attribut de la classe, puis créer un nouvel objet que je pense copier avec l’opérateur « = ».
Puis, je modifie un élément de liste de l’objet original et enfin j’affiche la valeur de la liste des deux objets.
Et là c’est le drame… Les deux listes ont été modifiées

In [5]: %run exemple.py
affichage liste originale :  [10, 20, 100, 40]
affichage liste modifiée :  [10, 20, 100, 40]

Pourquoi ?

C’est simple, on a copié l’adresse des objets et non le contenu de l’objet, donc quand on modifie les données de l’un, on modifie également l’autre…

Mais, contrairement à la liste, on ne peut pas un truc dans ce genre-là obj2 = obj1(:)…
Non, on ne peut pas, c’est de l’informatique pas de la magie…

Je vous mets donc une solution dans la section qui suit.

Deepcopy

Deepcopy est une fonction qui permet de copier explicitement un objet et son contenu dans un autre objet.

L’exemple précédent se modifie donc de la manière suivante :

from copy import deepcopy


class DataConso(object):
    def __init__(self):
        self.l_conso = []


if __name__ == '__main__':
    maconso = DataConso()
    maconso.l_conso.append(10)
    maconso.l_conso.append(20)
    maconso.l_conso.append(30)
    maconso.l_conso.append(40)

    maconso_copie = deepcopy(maconso)
    maconso_copie.l_conso[2] = 100

    print('affichage liste originale : ', maconso.l_conso)
    print('affichage liste modifiée : ', maconso_copie.l_conso)

Il y a seulement 2 modifications dans ce code :

  • l’import de la fonction deepcopy
  • l’usage de la fonction deepcopy() pour explicitement copier dans l’objet

Ce qui donne donc le résultat souhaité :

In [21]: %run exemple_deepcopy.py
affichage liste originale :  [10, 20, 30, 40]
affichage liste modifiée :  [10, 20, 100, 40]

Bon, voilà, vous n’aurez plus d’excuses pour mélanger les données d’objets ou de listes!

Pour aller plus loin

Les sources des exemples

PiRDF #2 : Préparation du matériel

PiRDF #2 : Préparation du matériel

Cet article permet de lister tous les éléments nécessaires à la préparation du Raspberry Pi pour l’application PiRDF que sous avons préciser l’article de présentation générale.
Nous allons voir point par point comment réaliser l’installation de toutes les dépendances nécessaires.

Préparation du système d’exploitation

Dans il est nécessaire d’installer un système Raspbian sur une carte SD. On ne le répétera jamais assez, il est très important que votre carte SD soit de qualité et ait des propriétés de vitesses importantes. J’utilise personnellement ce modèle des SanDisk classe 10 comme celle-ci :

On notera que 16GB est suffisant dans ce cas particulier.

Pour installer le système d’exploitation, vous devez télécharger la dernière image de Raspbian, ici celle du 09-10-2018 ce qui m’a permis de tester la dernière version.

Pour la copier, sur la carte, il suffit d’effectuer une copie sur la carte de l’img iso après l’avoir formatée. La copie s’effectue à l’aide de l’instruction dd selon le formalisme suivant :

dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sdb conv=fsync

l’argument if est utilisé pour les données d’entrée (ici l’image) et of pour la destination ici, /dev/sdb.

NB : l’installation les instructions ici sont celle d’un système Linux. Si vous effectuez l’installation depuis un système Windows (pratique que je déconseille), reportez-vous à rubrique d’aide spécifique du site officiel de Rapberry Pi’ c’est à dire :

Après avoir réaliser ces opérations de copie de l’image, il ne vous reste plus qu’à placer la carte SD dans le Raspberry Pi et de le démarrer.

Enfin, pour finir votre installation, il faut faudra configurer :

  • la langage du système
  • la timezone
  • le tytpe de clavier
  • le WIFI

Toutes ces informations vous seront demandées par le biais d’un assistant au premier démarrage. C’est une des nouveautés de la nouvelle version de Raspbian.

Après cela, il est temps de passer à l’installation des éléments liés à python.

La stack python

Outils de développement

Raspbian arrive avec des versions de python installer : la version de python 2.7.XX (je sais plus trop et j’en moque, je l’explique dans quelques lignes) et la version 3.5.3.
Je ne reviendrais par sur les raisons qui ont poussé la communauté de développeurs de maintenir pendant de trop nombreuses années 2 versions de python, mais le fait est que Python ne sera plus maintenu à partir de 2020 (voir le python countdown) et les librairies feront de même rapidement. Certaines librairies comme sci-kit-learn (que nous allons utiliser) l’ont déjà annoncé.
Pour ces raisons évidentes, je ne développe que du code compatible Python 3 et je n’assure aucune compatibilité pour Python!

A Python 3, j’ajoute deux outils de développements dont je considère qu’ils améliorent ma productivité :

  • ipython
  • ipdb

ipython

Ipython est environnement permettant d’exécuter du code de manière interactif, ce qui permet en d’autres choses d’accéder aux valeurs des différentes variables après exécutions, d’exécuter des commandes(dont certaines commandes consoles) directement et d’avoir accès à la coloration syntaxique et l’autocomplétion.

Pour l’installer, il suffit d’exécuter la ligne suivant :

pip3 install ipython

ipdb

Ipdb est un débugger un peu plus évolué que pdb (le débugger officiel). Il est surtout plus ergonomique que celui-ci. Pour installer, il faut également passer par pip :

pip3 install ipdb

Librairie scientifique

Pour réaliser les tâches de caractérisations des images et de prises de décisions automatiques, nous aurons besoin d’utiliser un certain nombre de librairies qu’on qualifie souvent de scientifique.

scipy et numpy

scipy et numpy sont inséparables, ce sont les deux librairies qui permet de réaliser tout ce qui est lié au calcul scientifique dont le calcul matriciel et l’algèbre linéaire.
Il ne faut pas envisager de les installer via pip car le risque de swap est très important et l’installation risquerait de ne pas aboutir.
Il faut mieux passer par le gestionnaire de paquet apt. Pour cela il faut saisir les commandes suivantes :

sudo apt-get install python3-scipy

sudo apt-get install python3-numpy

sci-kit-image

Sci-kit-image est la librairie qui permet d’accéder à un grand nombre de méthodes de traitement d’images disponible dans la littérature scientifique. Cette librairie possède également une couche IO (entrée/sortie) qui permet d’ouvrir de nombreux format d’images et de stocker les données dans des matrices de type numpy. De la même manière sci-kit-image permet un enregistrement de données brut en de nombreux formats d’images (png, jpeg, bmp…).
Pour l’installation, ça se passe pas apt :

sudo apt-get install python3-skimage

sci-kit-learn

Sci-kit-learn est une librairie spécifique d’outils d’intelligence artificielle. Nous utiliserons pour les phases d’apprentissage et de décision automatique. Pour l’installation, on passe encore une fois par apt :

sudo apt-get install python3-sklearn

Interfaçage de la caméra et éléments graphiques

Il ne nous reste plus qu’à installer les deux librairies permettant d’une part l’accès à la camera et à ses données grâce à picamera et la librairie pygame qui nous permettra de sans trop développement d’afficher le flux vidéo dans une interface graphique.

Pour l’installation, on passe cette fois par pip:

pip3 install picamera

et

pip3 install pygame

NB : On notera que dans la dernière version de Raspbian, ces deux librairies sont déjà installées

Et le hardware là-dedans

Après, le software, il nous est nécessaire de préparer le hardware. Il n’est pas nécessaire de préparer le hardware final pour ce qui est des switches. J’ai préféré passer par une phase de prototypage avec des microswitches, une « plaque lab » et quelques fils.

Pour aller un peu plus loin

Les autres articles du projet PiRDF :

Le matériel utilisé (liens affiliés — si vous commandez via ce lien, j’ai quelques % en commission pour payer l’hébergement de ce site l!):

PiRDF #1 : Présentation du projet

PiRDF #1 : Présentation du projet

Ce billet marque le début d’une série d’articles concernant la documentation d’un projet que je présenterai à la Maker Faire Paris sur le stand des éditions ENI qui est l’éditeur de mon livre intitulé « Python. Libérez le potentiel de votre Rapsberry Pi ».

Ce projet, je l’ai intitulé PiRDF. Pi en référence au Raspberry Pi et RDF pour le Reconnaissance De Formes. Le but de ce projet sera donc de construire une petite application qui permet d’apprendre et de reconnaitre des objets simples.
Tous les éléments logiciels seront développés en langage Python et publiés sur le « repository » Github : Projet PiRdf sur github. De cette manière quiconque pourra utiliser le code et/ou proposer des évolutions.

Le but de cette démo est montré au public qu’il est possible en très peu de lignes de construire des applications dont les fonctionnalités peuvent être complexes.

Enfin, on notera que l’ensemble de ces articles est une consignation des notes au cours de la réalisation. En effet, à l’heure où j’écris ces lignes le développement du code est loin d’être abouti, vous verrez sans doute des erreurs de conception qui vont nécessiter des refactoring d’un article à l’autre, j’essayerai donc de documenter toutes ces évolutions.

Dans la suite des cet article, je vais détailler le cahier des charges après une courte introduction de ce qu’est la reconnaissance de formes.

C’est quoi la reconnaissance de formes

La reconnaissance de forme est un des sous-domaines de la vision par ordinateur qui est elle-même une discipline à la croisée de nombreuses disciplines que sont :

  • l’informatique
  • les mathématiques appliquées
  • le traitement du signal et des images.

La reconnaissance de formes a pour but de reconnaitre au sein des images un ou des motifs appris au préalable. Ce motif peut être, par exemple, un objet, un visage…
La reconnaissance de formes fait donc appelle à des techniques comme l’apprentissage automatique et à la modélisation statistique. Pour le profane, on pourrait résumer le tout en disant que la reconnaissance de formes est une branche de l’intelligence artificielle même si le terme a en partie perdu de sa teneur à cause de la médiocrité de la vulgarisation de certains médias ces dernières années… Bref, ça, c’est un autre sujet…

Historiquement en sépare la reconnaissance d’objet en 2 familles :

  • l’approche globale
  • l’approche locale

l’approche locale

L’approche locale regroupe les différents techniques qui permettent de détecter et reconnaître un objet au sein d’une scène composée d’un ensemble d’objets.
Pour illustrer, on peut citer la détection de piéton (voir l’image ci-dessous) dans une scène urbaine qui trouve des applications dans le développement des véhicules autonomes.


Crédits : By Milwaukee_(WIS)N_5th_St« Tree,_Rain,_Wind« Pedestrian_1.jpg: vincent desjardinsderivative work: Indif (talk) – Milwaukee(WIS)N_5th_St« Tree,_Rain,_Wind« _Pedestrian_1.jpg, CC BY 2.0, https://commons.wikimedia.org/w/index.php?curid=11912052

Cette approche qui a beaucoup évolué en matière de performances depuis le début des années 2010, n’est toujours pas parfaite et le taux d’erreur reste toujours trop élevé pour certains types d’applications.
De plus, les dernières méthodes nécessitent, le plus souvent, des puissances de calcul très élevées. À titre d’illustrations, la voiture Tesla autonome possède la puissance de calcul de plusieurs MacBook Pro…
Il est par conséquent quasiment impossible d’espérer faire tourner ce genre de manip sur un Raspberry Pi qui possède des ressources matérielles limitées tant au niveau du processeur que de la mémoire de type RAM.

l’approche globale

À l’inverse, l’approche globale est plus simple et historiquement antérieure à l’approche globale.
Le but, ici, est de reconnaitre un unique objet au sein d’une image ou d’une région d’intérêt au sein d’une image.
Dans ce cas, le nombre de méthodes disponibles dans la littérature est plus nombreux et les performances atteintes par les meilleurs sur les bases de test sont de plus en plus proches de 100% de réussites.
Enfin, ils existent une multitude de méthodes qui présentent une complexité calculatoire diverse et variée. Dans tous les cas, de nombreuses méthodes peuvent être envisagées pour une implémentation en python sur le Raspberry Pi.

Cahier des charges — c’est quoi donc le projet PiRDF

L’application sera réduite au maximum en termes de fonctionnalités et d’interface graphique.
Dans tous les cas, elle sera découpée en 2 phases : 1- l’apprentissage 2- la décision. Les sections qui suivent vont permettre de définir plus précisément les éléments fonctionnels de chaque tâche.

Phase d’apprentissage

La phase d’apprentissage correspond à l’ajout d’un nouvel élément à une base de connaissance.
En pratique cela correspond à calculer un ensemble de caractéristiques à partir de l’image qui contient l’objet de calculer un modèle d’apprentissage à partir de la base de connaissance.

D’un point de vue pratique, on aura à disposition un bouton branché sur le GPIO qui permet de démarrer l’apprentissage.
L’appui sur ce bouton permet de commencer à calculer les caractéristiques pour les images arrivant de la caméra.
Toutes images ne serviront pas à l’apprentissage.
Nous réaliserons un suréchantillonnage du signal vidéo de l’ordre d’une image par seconde voir moins si le temps de traitement est trop long.

Pour mettre fin, à la phase d’apprentissage un second bouton sera installé sur le port GPIO.
Un appui sur ce bouton permet d’arrêter la phase de création et d’enrichissement de la base de connaissances et de réaliser la phase d’apprentissage à proprement dites et permet de créer un modèle de décision.

À partir de cette étape, nous possédons l’information nécessaire pour réaliser une décision vis-à-vis de l’objet présent devant la caméra.

Phase de décision

La phase de décision consiste à déterminer la classe d’appartenance d’un objet devant la caméra aux classes existantes dans le modèle.
Pour réaliser cela, on va calculer un ensemble de caractéristiques à partir de l’image et utiliser un modèle décisionnel pour déterminer la classe d’appartenance.

Enfin, le nom de la classe d’appartenance sera transmis un module de synthèse vocale qui permettra de restituer le nom de la classe d’un point de vue audio.

Par ailleurs, 2 boutons permettront de démarrer et d’arrêter la phase de décision.

À venir prochainement

Si vous jetez un oeil sur le repo Github vous verrez que le coeur du projet est bien avancé.
Vous pouvez donc fouiller pour voir l’évolution du projet en termes de code.
L’article qui suivra détaillera la configuration nécessaire du raspberry à partir d’une configuration vierge.

Le matériel utilisé (liens affiliés — si vous commandez via ce lien, j’ai quelques % en commission pour payer l’hébergement de ce site l!):

[Python] Interruption sur le port GPIO

[Python] Interruption sur le port GPIO

Avant d’aller plus loin, il est nécessaire de définir ce qu’est une interruption.
En informatique, informatique embarquée, ou électronique, une interruption est un processus qui est déclenché de manière asynchrone afin de permettre un éventuel traitement temps-réel des données.

Afin d’illustrer concrètement le phénomène, voici un exemple d’usage. Imaginez que vous vouliez allumer une LED lorsque vous appuyez sur un bouton qui est relié à une des bornes de votre GPIO.
Il existe pour cela plusieurs possibilités la manière brutale :

1- mettre une boucle de test dans le programme principale qui va tester l’état du bouton à chaque itération du programme principale
2- si l’état du bouton passe à l’état attendu qui peut être True(Vrai) ou False(Faux) alors on appelle une fonction de traitement

Le problème de ce genre de méthode est que si les tâches de votre programme principal sont alors le traitement de l’état du bouton sera alors effectué une fois par tâche.
Par conséquent, si le traitement de la tâche associée au bouton, il est impossible d’avoir une réaction immédiate (« temps-réel »).
Dans le pire des cas, on pourra même rater des phénomènes se produisant sur le bouton.
En effet, imaginons que le bouton sur le GPIO soit un bouton poussoir. L’appui sur le bouton produit un signal d’état positif (+5V. NB : Cela pour être 0V suivant le câblage de votre bouton) pendant quelques millisecondes, or si le traitement dans votre programme principal est de quelques secondes, la probabilité de rater le phénomène d’appui est plus importante que la probabilité de l’attraper au vol!!!!

Pour limiter tous ces problèmes, il est préférable d’utiliser le mécanisme d’interruption.
En pratique, cela va permettre d’exécuter une portion de code dès que le bouton sera pressé en quittant temporairement le programme principal puis en y retournant dès que cette portion de code sera exécutée.

Pour mettre en œuvre cela en python, on va utiliser l’instruction « add_event_detect » de la librairie RPi.GPIO qui permet de configurer une interruption.
Plus particulièrement, on va associer une broche à une fonction qui sera appelée que l’évènement va se produire sur la broche en question pour exemple :

 GPIO.add_event_detect(7, GPIO.RISING, callback=my_callback, bouncetime=300)

Dans ce sa on va déclencher l’appel à la fonction my_callback lors d’un passage à l’état haut (GPIO.RISING) sar la broche 7.
On notera que la paramètre bouncetime=300 correspond au temps pendant lequel l’interruption ne sera pas sensible [aux rebonds du bouton](TODO: mettre un lien vers un article de définition du rebond d’un bouton).

Par conséquent, on obtient à minima le code suivant :

import RPi.GPIO as GPIO


def init():
    # configuration de la broche 7 en entree
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(7, GPIO.IN)
    # definition de l'interruption
    GPIO.add_event_detect(7, GPIO.RISING, callback=my_callback, bouncetime=300)


def my_callback():
    # callback = function which call when a signal rising edge on pin 7
    print("Vous avez appuyez sur le bouton de la patte 7")


if __name__ == '__main__':

    # 1- initiation de la l'interruption
    init()
    # 2- boucle infini = tache principale
    while True:
pass

La fonction init initialise le GPIO et «accroche» la fonction my callback() à la broche 7.

Attention cependant, dans l’exemple qui précède, tous les traitements sont réalisés dans la fonction de callback.
Cette façon de faire est intéressante pour gagner un maximum de temps.
Cependant il est nécessaire d’avoir un minimum de chose à réaliser dans cette boucle.
En effet, le risque de freezer le programme dans son ensemble est important.

D’une manière générale, une bonne pratique est (lorsque l’exécution de la fonction n’est pas cruciale et que le temps-reél tient) de mettre un flag à True(Vrai) dans le callback et de lancer l’exécution de la fonction de traitement à partir du programme principal.

Le programme ci-dessous illustre le fonctionnement :

import RPi.GPIO as GPIO


# variable globale qui sera vue dans toutes les fonctions
flag_callback = False

def init():
    # configuration de la broche 7 en entree
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(7, GPIO.IN)
    # definition de l'interruption
    GPIO.add_event_detect(7, GPIO.RISING, callback=my_callback, bouncetime=300)
    # initialisation du flag
    flag_callback = False


def my_callback():
    # function qui sera appelé lorsque le programme sur interrompu
    flag_callback = True


def process_ny_callback():
    print("Interruption détectée sur la broche 7")

if __name__ == '__main__':

    # 1- initiation de la l'interruption
    init()
    # 2- boucle infini = tache principale
    while True:
        # 3- si une interruption c'est produite alors on lance le traitement c
        # adéquat
        if flag_callback == True:
            process_ny_callback()
    pass

Vous pourrez retrouver les sources sur le repo github suivant : https://github.com/clemaitre58/20181002_interruption.

Alors prêt à attaquer les interruptions avec Python?

[Python] — Google met à jour son « Style Guide Python »

Google met (enfin…) à jour son style guide Python!

Pour celles et ceux qui lisent un peu ce site ou le livre que j’ai écrit sur Raspberry et Python, ils ont conscience que de l’importante d’avoir un code python cohérent et respectant quelques règles.
La recommandation PEP 8 qui regroupe un ensemble de consignes de formes permet d’avoir un code lisible par la plus grande partie des développeurs Python, qu’ils soient débutants ou experts.
Une autre de mes sources favorites quant aux bonnes pratiques à adopter est le google « Guide Style » qu’on trouvait anciennement ici : liens.

Les « guide style » de Google sont un regroupement d’instruction sur les bonnes pratiques à adopter selon le point de vue du géant de Mountain View pour la rédaction de code informatique. Il est à noter que les « guide style » ne sont pas seulement dédiés au langage Python, mais il en existe pour de nombreux langages, on peut par exemple citer C++, javascript…

Comme indiqué, quelques lignes plus hautes, les instructions et les exemples contenus dans les fiches permettent d’avoir une certaine cohérence lors de la création de code qu’on travaille en équipe ou seul. Pour vous illustrer ce qu’on y trouve, voici un exemple pour le nommage des différents éléments :


module_name,
package_name,
ClassName,
method_name,
ExceptionName,
function_name,
GLOBAL_CONSTANT_NAME,
global_var_name,
instance_var_name,
function_parameter_name,
local_var_name.

Function names, variable names, and filenames should be descriptive; eschew
abbreviation. In particular, do not use abbreviations that are ambiguous or
unfamiliar to readers outside your project, and do not abbreviate by deleting
letters within a word.

Always use a .py filename extension. Never use dashes.


C’est exactement le genre de convention que j’utilise lorsque je développe et lors d’une hésitation, je n’hésite pas à utiliser le Style Guide.

Bon bref… Ce billet n’est pas là pour vous convaincre du bienfait de ce genre de recommandations, mais plutôt pour vous informer que Google après de nombreuses années sans activité a mis à jour ces dépôts et que l’URL du « Style Guide Python » a changé. Vous pourrez donc le retrouver ici : liens vers le repo.

En ce qui concerne le contenu et l’organisation, la structure a l’air d’avoir changé un peu et l’on trouve de nouvelles recommandations liées aux évolutions des versions de Python 3. Vous pouvez donc avantageusement mettre à jour vos bookmark!!!

C++ / un squelette type pour compiler vos projets

C++ / un squelette type pour compiler vos projets

Programmer en C++ pour votre Raspberry Pi

Le développement d’application en langage python n’est pas la seule solution pour la création d’application pour votre Raspberry Pi.
C++ est également un très bon choix même si la rapidité de développement est plus faible que pour le langage Python.
Il existe plusieurs façons de développer.
La première est d’utiliser un environnement de développement complet qui permet la prise en charge de l’intégrité du projet.
C’est à dire d’édition des sources à la compilation en passant par la gestion de la configuration de la compilation et l’outil de débogage.
La seconde est l’édition d’un éditeur performant (vim, emacs…) et un outil de compilation par ailleurs.

La première méthodologie décrite ci-dessus nécessite un IDE de bonne qualité. Pour les utilisateurs d’un système basé sur Linux, le choix le plus populaire actuellement (2018) est sans doute QT Creator. Cependant sur le Raspberry Pi, cette solution est un peu couteuse en ressources.
La seconde option est, à mon sens, plus légère et permet d’avoir un environnement de développement performant et sur multiplateforme.
En effet, la manière conventionnelle de compiler sous Linux est d’utiliser un makefile, un fichier reprend en charge la compilation de votre projet en seulement une instruction.
Cependant un makefile ne vous garantit pas une compilation multiplateforme.
Il permet juste de réaliser une succession d’instruction de compilation.
Néanmoins, un outil permet de générer des makefile en fonction de la configuration des outils disponibles sur votre machine de développement.
Cet outil, c’est CMake!

CMake

Cmake vous permettra de générer des makefile pour votre projet mais également pour la copilation sur votre machine mais permettra le déployement de votre projet sur d’autres machines.

Afin de pouvoir réaliser la compilation, j’ai créé une base de projet qui me permet de démarrer rapidement un projet en langage C++ et ceci, quelle que soit la plateforme sur laquelle je développe.

Cette base de projet est disponible ici : https://github.com/clemaitre58/ProjSkeletonCpp

L’architecture du projet est la suivante :

+-- data               --> pour contenir d'éventuelles données
+-- src                --> pour vos sources
|   +-- CMakeModules   
|   +-- apps           --> sources de votre application (main.cc par exemple)
|   +-- cmake
|   +-- common         --> sources d'api, librairie, etc
|   +-- tests          --> vos tests GoogleTests ici

Pour l’utiliser, il suffit de mettre vos sources dans les dossiers commons ou apps et ensuite de réaliser l’ensemble des instructions disponible dans le README :

Compilation

Create a build folder:

mkdir build

  • Move to the created folder:

cd build

  • Create the MakeFile via cmake:

cmake ../src

To configure to be in debug mode :

cmake -DCMAKE_BUILD_TYPE=Debug ../src

  • Compile the code to generate the executable:

make or make -j n where n is the number of cores to use for the compilation

  • A folder bin will be created at the same level as the build directory.

Remarks

  • If you add new file or remove some other, you have to create the makefile again:

cmake ../src

Un exemple d’utilisation est disponible ici : https://github.com/clemaitre58/BSCVDemoCpp1718

Alors c’est quoi votre prochain projet en C++ pour votre Raspberry Pi?