Python

-

Sinds ik als softwareontwikkelaar werk gebruik ik voornamelijk Java met zo af en toe een uitstapje naar een andere taal. Nog niet eerder heb ik een uitstapje gemaakt naar Python. Over het algemeen ben ik een fan van sterk en statisch getypeerde talen zoals Java, Haskell, Typescript en Kotlin. Kort door de bocht wil dat zeggen: als de types in mijn programma niet kloppen klaagt de compiler en start de boel niet, lekker duidelijk 🙃.

Python is sterk en dynamisch getypeerd in plaats van statisch, dat wil zeggen dat ik wel mag schrijven:

Maar niet:

De types worden op runtime gecontroleerd en waardes worden niet zomaar stilletjes van type gewijzigd, de code crasht dus op runtime. Omdat ik graag snel gewaarschuwd wordt voor fouten in mijn code was dit tot nog toe een reden voor mij om zoveel mogelijk bij statisch getypeerde talen te blijven.

Al lange tijd staat Python hoog op lijstjes met populaire talen, omdat het een eenvoudige taal met veel standaardfunctionaliteit is (batteries included zoals ze dat zeggen). Vooral op het gebied van machine learning en data science is Python de voorkeurstaal. En dit zijn twee onderwerpen die mij erg leuk lijken om wat mee te gaan doen.

Om niet direct de weg kwijt te raken leek het me goed om eerst Python te leren kennen voordat ik aan de slag ga met machine learning en/of data science. Python is ‘simpel’ dus het dynamische type stukje moet ik maar voor lief nemen. Natuurlijk kan ik machine learning ook prima in Java doen, maar veel materiaal is voor Python bedoeld. De enkele keer dat ik zoiets in Java heb gedaan kon ik een getting started volgen waarin stond uitgekauwd wat ik moest doen, maar waarom ik dat moest doen werd me niet altijd duidelijk. Daar wil ik iets aan doen, ik wil met machine learning aan de slag en snappen waarom ik iets doe. Stap 1: Python leren.


Een programmeertaal leren is vaak niet moeilijk, meestal maar een handje vol keywords en constructies. De moeilijkheid zit hem vaak in wegwijs worden in de (standaard) libraries en tooling eromheen. Python komt standaard al met een package manager (Pip), een tool voor virtuele omgevingen (zodat je per project dependencies kan installeren) en in de standaard libraries zit een test framework en praktisch alles waar je in Java Apache Commons libraries en Jackson voor nodig hebt. Pip en virtuele omgevingen zijn niet heel ingewikkeld maar ik ben er nog niet helemaal uit hoe al die andere dingen moeten die Maven normaal voor me doet, er lijken veel verschillende tools te zijn…

Om het iets moeilijker te maken dan de tutorials voor mensen die nog nooit hebben geprogrammeerd heb ik als eerste Python-project ervoor gekozen om m’n Lego Mindstorm EV3 robot met Python te programmeren. Dat heb ik een aantal jaar terug al eens met Java gedaan, dus de logica van het programma zelf zou niet zo moeilijk moeten zijn.

Om Java aan de gang te krijgen op de Mindstorm robot moest ik aan de slag met EV3Dev en in het geval van Java 8 moest ik zelf een mini JRE bouwen/genereren. Voor Python is het iets makkelijker: Lego heeft het PyBricks project omarmt en levert een kant en klare Linux distributie met Python (hier te downloaden). Lego heeft zelf een plugin voor Visual Studio Code gemaakt waarmee je code direct op de Robot kan deployen en starten. De console output komt dan direct in het log venster uit.

Het doel van de programmeer-exercitie was dat de robot een rode lijn volgt. Omdat de robot toch ook een afstandssensor heeft wil ik ook dat hij achteruitrijdt als hij ergens te dichtbij komt. Easy toch?

In de Java-versie was een API voor subsumption aanwezig. Dat wil zeggen dat je een lijstje van gedragingen (‘behaviours’) mee kan geven in volgorde van belang. Ieder gedrag kan aangeven of het vindt dat het actief moet worden, het gedrag met hoogste rang dat actief wil worden krijgt de controle toegewezen. Voor deze robot zijn er drie behaviours:

  1. Als een obstakel te dichtbij is, ga achteruit;
  2. Als de kleurensensor geen rood ziet, ga draaien tot er weer rood gevonden is;
  3. Rijd vooruit;

Zolang de kleurensensor rood ziet en de afstandsensor geen obstakel gaat de robot dus vooruit.

PyBricks komt met een iets andere opzet dan de Java library voor EV3 en mist het behaviour framework. Deze drie gedragingen zijn natuurlijk ook prima in 1 klasse te vangen met een paar methodes en een heleboel if-then-else blokken maar dat is niet heel mooi of makkelijk uit te breiden. Gelukkig had iemand dit type API al eens ontwikkeld voor Python op een Mindstorm robot en kon ik daarmee aan de slag. Helaas deed dit framework initieel helemaal niets. Het programma werd geladen maar vervolgens gebeurde er niets, het gaf zelfs geen fouten. Wat blijkt, er zijn meerdere soorten Python. Er is de reguliere versie van Python en een uitgebreide distributie zoals Anaconda met veel extra tools en libraries. Maar ook MicroPython. En dat was precies waar PyBricks mee werkt. MicroPython is Python met uitgeklede standaard libraries, vergelijkbaar met Java voor EV3. In dit geval bleek dat de threading library niet volledig geïmplementeerd is en dus vastliep op lege methodes. Met wat kleine wijzingen heb ik het framework toch aan de gang gekregen.

Alles bij elkaar viel het me niet tegen om de code over te zetten. Python is een leesbare taal. Het dynamisch typeren van programma’s is af en toe wat lastig, of misschien vooral wennen. Iets anders waar ik vaak ingetrapt ben zijn haakjes, ik type bij iedere methode definitie uit automatisme nog ‘{}’. In plaats van ‘{}’ om code blokken in Java te definiëren gebruik je in Python whitespace. Alles wat net zover is ingesprongen is 1 code blok. Voordeel hiervan is dat je geen haakjes hoeft te tellen en code altijd netjes geïntendeerd is. Een ander bijzonder iets is dat je in methode definities in klassen het object zelf als argument meegeeft:

De naam voor wat in Java ‘this’ is kan je op deze manier aanpassen. Gebruikelijk is het om dit argument ‘self’ te noemen. Als je de methode aanroept op een object hoef je dit argument niet mee te geven:

Een overbodig keyword als ‘new’ kom je in Python ook niet tegen. Het is mogelijk de volgorde van argumenten aan te passen bij aanroep als je de naam van het argument opgeeft. En ook is het mogelijk om een default waarde aan een argument toe te wijzen zodat deze bij aanroep weggelaten kan worden (dat scheelt een hoop overloaded methode definities). Een methode kan ook als waarde worden meegegeven aan andere methoden.

Het gebrek aan type validatie in de editor en bij het starten van programma’s is me meegevallen. Er zijn wel wat subtiele zaken in Python die voor het nodige zoekwerk kunnen zorgen. De meeste tijd ben ik kwijt geweest aan het zoeken naar waarom mijn programma niet deed wat ik had verwacht. Het volgende is valide code:

De functie someFunction wordt hier als waarde aangeroepen en niet zozeer uitgevoerd, de while lus gaat oneindig door. Het duurde even voor ik door had dat de haakjes voor de functie aanroep ontbraken (het moest dus zijn:  Dit is correcte code want ieder object in Python heeft een boolean waarde, dus ook functie referenties. Voor lege collecties en 0 is de boolean waarde False anders is deze True tenzij een klasse een speciale methode __bool__ implementeert. In dit geval had een strenge type controle me dus niet gered, ik had beter de handleiding moeten lezen.

En jawel, uiteindelijk volgde de robot keurig de rode lijn 🙂.  Met deze oefening heb ik nog lang niet alles van Python gezien, maar tot zover vind ik het een leuke taal en levert het leesbare programma’s op. Ik ga zeker meer met Python doen! De volgende stap: een boek over Machine Learning doorwerken.

De code van mijn Python experiment kun je hier vinden, de Java-code hier.