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!):

Utiliser Dropbox sur son Raspeberry Pi

Dropbox sur son Raspeberry Pi

Dropbox est un service très pratique qui permet de sauvegarder des données et de les synchroniser via le cloud.
C’est un service gratuit jusqu’à une limite de 2Go de données.
C’est également sans doute le service de cloud, le plus populaire actuellement avec le Drive de google.
Comme tout bon service de cloud, la synchronisation est automatisée à l’aide d’un client qui tourne en tâche de fond et dont la seule visibilité est la présence d’information dans la barre de tâche.

Des versions de ce client existe pour :
– windows
– Mac
– Linux

Malheureusement, la version Linux gère seulement les processeurs de type Intel et non le processeur ARM que possède notre Raspberry Pi.

Cependant, il existe une solution même si elle est moins user Friendly que le client de chez Dropbox. En effet, Andrea Fabrizi
a développé une série de scripts qui permettent de téléverser (ça, c’est la version validée pas l’Académie française d’uploader) et de télécharger des données de son espace cloud.

Sans rentrer dans le détail, sachez que l’auteur des scripts manipule en fait l’api proposé par Dropbox aux développeurs. Cela vous parait peut être un détail, mais cela a son importance, car, il vous faudra une clé de développement pour faire fonctionner le bousin!

Prérequis

Avant toute manipulation, faites les sauvegardes nécessaires.
Mettez également à jour votre système :

sudo apt-get update
sudo apt-get upgrade

Vous devez également installer git (git est un outil de versionning) si ce n’est pas déjà fait :

sudo apt-get install git

Installation

Rendez-vous sur cette page : https://github.com/andreafabrizi/Dropbox-Uploader

puis dans une console, placez-vous dans un dossier spécifique (on va dire ~/Documents/Code)

cd ~/Documents/Code

puis clonons le repo de github

git clone https://github.com/andreafabrizi/Dropbox-Uploader

placez-vous dans le dossier qui vient d’être créé :

cd Dropbox-Uploader

Changer les droits :

chmod +x dropbox_uploader.sh

puis lancer le script :

./dropbox_uploader.sh

Ce premier lancement se fait sans paramètres puisque vous aurez besoin de saisir une clé d’API dropbox. Cette étape de configuration est faite pas à pas et très bien documentée.

votre premier upload

Vous pouvez maintenant uploader un dossier, imaginons ~/Document/Cours

Pour cela, il suffit de saisir la commande :

./dropbox_uploader.sh upload ~/Documents/Cours /

Ce qui va permettre de téléverser le dossier Cours en racine de Dropbox.

La stratégie pour le download est équivalente.

Pour aller plus loin

Je joins ici quelques exemples de l’auteur :

> ./dropbox_uploader.sh upload /etc/passwd /myfiles/passwd.old
> ./dropbox_uploader.sh upload *.zip /
> ./dropbox_uploader.sh -x .git upload ./project /
> ./dropbox_uploader.sh download /backup.zip
> ./dropbox_uploader.sh delete /backup.zip
> ./dropbox_uploader.sh mkdir /myDir/
> ./dropbox_uploader.sh upload "My File.txt" "My File 2.txt"
> ./dropbox_uploader.sh share "My File.txt"
> ./dropbox_uploader.sh list

ainsi que la documentation : ici

ma config

  • RPi 3
  • Raspbian Jessie

Alimentation solaire pour Raspberry Pi

Lipo Alimentation solaire RPi

Alimentation solaire pour Raspberry Pi

Votre Raspberry Pi est peu être le compagnon idéal pour des applications outdoor pour le data logging de données en tout genre :
– station météo
– mesure de la quantité de Co2 dans l’air
– mesure de la présence de poussière

Ou tout ce que vous voudrez…

Cependant, dès le début de la conception de ce genre d’application, vous vous poserez sans doute la question de l’alimentation du Raspberry Pi.

Naturellement, la première solution est de penser à une batterie par exemple une Lipo qui ont l’avantage d’être légère pour une autonomie importante.
On va trouver des solutions pour pour 5000mAh pour une centaine de gramme et quelques dizaines d’euro (je ne donne pas de prix exact car le prix varie énorméément selon le sourcing).
Avec une 5000mhA vous pourrez sans problème alimenter votre Raspberry Pi une quinzaine d’heures un Pi Zero une vingtaine d’heure. On peut même envisager encore plus avec des optimisation de consommation.

Vient ensuite la problématique de la recharge de la batterie Lipo. Alors attention, on recharge pas une batterie Lipo n’importe comment.

Un peu de sécurité sur les batteries Lipo

Il faut respecter une tension de charge et avoir un courant de charge en adéquation avec votre modèle de batterie.
Il faut également que la batterie ne soit pas trop chaude pour être rechargée.

Pour le courant ou la tension c’est votre chargeur qui devra remplir ce rôle.
Pour la température, vous devez vous assurez que la batterie est équipé d’un composant qui coupe la possibilité de charge si la température est trop importante (Les batteries Lipo 3.7V sont généralement équipées de ce genre de composants)

Pour résumé, il vous faudra donc un module de charge de batterie.

Il vous faudra également une source d’alimentation pour votre chargeur. Étant à l’extérieur les panneau solaires constitue une bonne source d’alimentation.
Il faudra seulement veiller à avoir une puissance crête en adéquation avec votre application et une régulateur de tension en amont de votre chargeur de batterie.

Quel montage?

La vidéo youtube disponible ci-dessous montre le montage que j’utilise :

  • 2 panneaux solaires 12V-1.5W
  • 1 régulateur DC-DC 5V
  • Une carte de charge Lipo et alimentation Rpi
  • un consomètre USB
  • Batterie Lipo 5000mAh en 3.7V

Charge et alimentation simultanée?

Oui et non. On ne peut pas charger et «tirer du jus» sur une batterie simultanément.
Cependant le panneau solaire peut charger la batterie et en cas de puissance suffisante, alimenter le RPi avec le courant disponible.

Un peu de lecture sur le sujet : ICI

Oui mais …

Et oui, il y a un mais! La carte que j’utilise n’a pas la capacité de couper le circuit du panneau solaire en cas de puissance trop peu suffisante pour alimenter le Rpi.
Du coup, la batterie étant en charge elle ne peut alimenter le RPi ce qui a pour effet de passer le RPi hors tension…

Dans ma quête d’autonomie, il me sera dons nécessaire de commander le circuit d’alimentation en fonction du la charge disponible sur la ligne des panneaux solaires.
Comment ? Mesure de courant ? Commande de relais ?
À voir, je suis encore en cours de réflexion.

Et vous? avez vous déjà réalisé l’alimentation solaire pour Raspberry Pi?
Lipo Alimentation solaire RPi

Bouton reset raspberry

reset, start raspberry

Bouton reset raspberry

Quelques lignes pour pour montrer comment solutionner le problème de reset du Rpi afin d’éviter le «vieux unplug à chaud et bien dégueulasse» qu’on est tenter de faire si on conserve la configuration du raspberry.

En effet, débrancher et rebrancher votre raspberry pi à chaud risque d’endommager de façon définitive le système d’extension. De plus d’un point électriques, vous mettez à mal l’intégrité de votre nano-ordinatteur.

Mais, il existe des solution. Voyons en délais.
Cas du raspberry Zero 1.3 et Zero W

Le raspberry Zéro (1.2, 1.3 et w) possède juste coté du port GPIO, 2 broches intitulés «run» (voir photo). En fermant le circuit au niveau de ces broches, vous faites un reset de votre Rpi. Pour réaliser cette fermeture de circuit, c’est très simple : il suffit d’ajouter un bouton poussoir qui est à l’état ouvert par défaut 1.

reset, start raspberry
Près du GPIO les bornes «run» permettant de réaliser un reset

Attention cependant, un reset de type hard n’est pas vraiment meilleur. Je vous conseille donc de réaliser :

sudo reboot

autant que faire se peut avant d’employer l’arme nucléaire!

De la même façon, ce bouton permet de démarrer le Rpi après l’avoir éteint à l’aide de la commande système :

sudo halt

Pour finir une petite démo en vidéo :

Cas du raspberry pi 3

Pour le raspberry Pi 3 (et pour le 2 également), vous pouvez réaliser cette manip/modification en connectant le bouton poussoir sur le RPi a l’aide des broches nommées P16 que vous pouvez trouver au dos de la carte de votre mini-ordinateur.

Vous trouverez ICI un article détaillé et publié sur le site framboise314.fr
Et si on le commandait électronique ce bouton?

Il faudra être patient c’est dans les tiroirs car je dois solutionner ce problème pour redémarrer de façon périodique mon RPi qui est en charge des acquisitions de données météo de la maison!

Patience ça viendra dans quelques semaines.

Bon alors prêt à ajouter ce bouton sur votre raspberry?


  1. Ce serait bête de devoir maintenir le bouton enfoncer pour que votre Rpi reste allumé… 

Module RTC ds1307 : installer et configurer une horloge externe pour votre Raspberry Pi

Module RTC ds1307 :

Un problème récurrent des utilisateurs de Raspberry Pi est que celui ci ne possède pas d’horloge interne.
Ceci a pour effet de ne pas pouvoir réaliser d’application utilisant des informations sur l’heure absolue (par exemple faire une actions tous les jours à 20h) sans être connecté à un internet. En effet, votre connection internet, vous permet de récupérer leur sur un serveur donné.

Pour pouvoir réaliser ce genre d’opération, il faut avoir recours à un module RTC (Real-Time Clock) qu’on peut traduire « horloge temps réel ».
Ce genre de module n’est ni plus ni moins une horloge externe alimentée par une pile bouton, qui après une première initialisation, peut être interrogée par le raspberry pi pour obtenir l’heure.

Dans mon cas, j’ai choisi le Module RTC ds1307. Ce choix est expliqué pour plusieurs raisons :

  • le module est très courant
  • le module ce trouve pour moins 5€ (par exemple sur aliexpress)
  • ce module a un support natif par la version Jessie du système raspbian
  • il est i2c ce qui réduit le nombre de boches mobilisées sur le port GPIO

Connection du module

Le module est très simple à brancher. Vous devez uniquement connecter :

  • VCC
  • GND
  • SCL
  • SDA

du module sur les broches du même nom du Raspberry Pi.

Vérification de la détection du nodule sur le Bus i2c

Si votre port i2c n’est pas activé, rien de plus simple. Il suffit d’exécuter :

 sudo raspi-config

Dans le menu Advenced, il faut rendre actif le port i2c.
Nous allons vérifier que le module est bien détecté mais , au péralable, il faut installer les outils nécessaire à cette tâche.

sudo apt-get install python-smbus i2c-tools

Puis nous pouvons utiliser l’utilitaire i2cdetect :

sudo i2cdetect -y 1

L’instruction est différente suivant les versions du Raspberry Pi1

Cette instruction devrait vous retourner l’équivalent de l’écran suivant :

On voit que le RPi a détecté un composant à l’adresse 0x68 qui d’après la doc constructeur l’adresse par défaut.

Installation du Module RTC ds1307

On va ajouter la gestion du module en éditant le fichier de configuration de boot.

sudo nano /boot/config.txt

Auquel on va ajouter un la ligne suivante à la fin du fichier :

dtoverlay=i2c-rtc,ds1307

A ce stade, vous devez redémarrez votre système :

sudo reboot

Une fois le système de nouveau actif, il vous pouvez éxécuter de nouveau:

sudo i2cdetect -y 1

Vous devriez observer si le prise en charge de l’horloge est effective en obtenant les caractères UU à l’adresse 0X68

Il vous est maintenant nécessaire de désactiver l’horloge qui émule l’horloge hardware «fake hwclock> avec les instructions suivantes :

sudo apt-get -y remove fake-hwclock
sudo update-rc.d -f fake-hwclock remove

Puis décommenter les lignes suivantes du fichier (en début de fichier) /lib/udev/hwclock-set :

#if [ -e /run/systemd/system ] ; then
# exit 0
#fi

Configuration de l’horloge du Module RTC ds1307

Pour configuer votre horloge RTC, à partir de l’heure système (nécessite donc une connexion à Internet):

sudo hwclock -w

NB : W pour Write

Pour vérifier que votre horloge est bien à l’heure, il suffit cette fois-ci de lire

sudo hwclock -r

NB : R pour Read

Vérification du fonctionnement

Pour vérifier, le bonctionnement du système, couper la connexion internet (faite éventuellement un reboot) et exécuté :

date

Si tous c’est bien passé vous devriez obtenir l’heure exact!

Alors prêt à utiliser le Module RTC ds1307?


  1. Si votre raspberry est une version antérieur au Pi 3 et Pi Zero, l’instruction est sudo i2cdetect -y 0