Python is een programmeertaal die begin jaren ‘90 is ontwikkeld door de Nederlander Guido van Rossum. Het heeft de laatste jaren een sterke groei doorgemaakt. Python is een general purpose programming language die relatief gemakkelijk te leren is. Een andere reden voor de stijgende populariteit is dat Python zeer geschikt is voor data analyse en machine learning. Dit artikel beschrijft Python vanuit een Java perspectief. Het beschrijft de verschillen en overeenkomsten van beide programmeertalen, met een focus op de programmeertaal zelf en niet zozeer op alle (overigens mooie!) packages en frameworks die er zijn. We geven je een aantal korte code voorbeelden hoe je bepaalde zaken typisch in Python programmeert. De voorbeelden werken allemaal in Python 3.7.

Auteur: Jan-Hein Bührman

Oorsprong en belangrijkste kenmerken

Python vindt zijn oorsprong in een aantal talen: ABC, C/C++ en Modula-3. Het is in de basis een imperatieve programmeertaal. De taal leent zich prima voor een procedurele-, object-georiënteerde en een functionele programmeerstijl.

Indentering bepaalt code block grouping

Misschien wel het meest opvallende kenmerk van Python is hoe code sections/blocks worden aangegeven. Daar waar bijvoorbeeld vanuit C/Java de bekende curly braces ({, }), worden gebruikt, wordt bij Python de blokvorming bepaald door indent level. Zie bijvoorbeeld:

Listing 1

 

Door de dedent van het print statement, hoort deze niet bij de for-loop. De standaard Style Guide for Python code (PEP-8) adviseert om een indent-level van 4 posities te gebruiken, gebruikmakend van spaties, geen tabs.

Read-Eval-Print Loop

Python heeft vanaf het begin een read-eval-print loop (REPL). Een expressie/statement of constructie wordt door de REPL gelezen, geëvalueerd en eenduidig afgedrukt, mits die expressie een object oplevert anders dan None (waarover later meer). De meeste voorbeelden in dit artikel zullen getoond worden als een interactieve sessie in de REPL.

Dynamically-typed en Strongly-typed

Python is dynamically-typed. Dus variabelen zijn, in tegenstelling tot Java, niet statisch geassocieerd met een type. Python is echter wel strongly-typed; elk object heeft een specifiek type aan zich verbonden. Dit in tegenstelling tot bijvoorbeeld JavaScript. Expliciete conversie is dus nodig waar dat vereist is. Zie ter illustratie de volgende sessie:

Listing 2

 

Je ziet dat je niet zomaar een int bij een string kunt optellen. Dit wordt run-time bewaakt. Wanneer een fout wordt gedetecteerd, dan wordt een Exception gegenereerd. Je zult ook opgemerkt hebben dat # gebruikt wordt voor commentaar.

Static type checkers

In recente versies van Python is het mogelijk om de code van type-annotaties te voorzien. Daarmee kunnen compile time inconsistenties gesignaleerd worden en veel IDE’s spelen hier ook op in. Dit helpt enorm bij onderhoudbaarheid en leesbaarheid.

Mypy is de meest gebruikte statische type checker.

Gecompileerd versus geïnterpreteerd versus Virtual Machine

Veruit de meest gebruikte Python (en tevens referentie) implementatie is CPython. Dit is een in C/C++ geschreven compiler/interpreter die zo’n beetje op elk denkbaar platform beschikbaar is. Het laden van een module gebeurt in CPython in twee stappen:

  1. Allereerst wordt de code ingelezen en gecompileerd naar een machine en Operating System onafhankelijke zogenaamde byte code, vergelijkbaar met hoe in Java een .java file gecompileerd wordt naar een .class file. Deze gecompileerde modules worden, indien mogelijk, in een cache directory opgeslagen als files met een .pyc extensie.
  2. Vervolgens wordt de byte code geïnterpreteerd door de CPython (byte code) interpreter, of – in andere woorden – geëxecuteerd door de CPython Virtual Machine.

In tegenstelling tot Java wordt in de CPython implementatie code niet verder naar native object code doorgecompileerd. Er zijn echter andere Python implementaties waar dat anders werkt. Zo is er Python implementatie voor het Java platform, Jython genaamd. Deze runt in de JVM en er is directe interoperability tussen Java en Python. En er bestaat ook een implementatie met een Just-in-Time (JIT) compiler, PyPy. Deze voert momenteel de Python benchmarks gemiddeld genomen 7.6 keer sneller uit.

Geen source file per class

In tegenstelling tot Java is er geen 1-op-1 relatie tussen classes en source files (Python modules). Je kunt een willekeurig aantal functies en classes (inclusief geen enkele) in een module definiëren.

Types

De meest gebruikte ingebouwde types met voorbeelden worden hieronder beschreven.

Numeriek

Numerieke types zijn int, float, en complex, (met notatie 1+2j). Ints kunnen willekeurig grote waarden bevatten. Anders dan in Java, worden octale getallen niet geprefixed met 0, maar met 0o.

Verder hebben we de bekende operatoren, zoals +, -, * , / en %. De operator / (truediv) op int objecten levert in tegenstelling tot Java een float op; de floordiv operator // is toegevoegd voor integer division. Verder hebben we nog ** voor machtsverheffen, als equivalent voor de pow functie.

Sequence types

Sequence types zijn, onder andere, list ([41, 42, 43]), tuple ((“foo”, 42, 1.5)) en range (range(4, 8)). Zoals je ziet, is het syntactische verschil tussen list- en tuple initialisers de vierkante haak ([…]) versus de ronde haak ((…)).

Lists zijn enigszins vergelijkbaar met Java’s ArrayList en ze zijn mutable. Dit soort containers worden gewoonlijk gebruikt voor homogene verzamelingen, net zoals in Java.

Tuples kun je vergelijken met een immutable variant van Java’s Pair, maar dan voor een willekeurig aantal entries in plaats van 2.  () representeert een lege tuple, en (3,) een tuple met een enkel element.

Hieronder volgen een paar voorbeelden om elementen uit de sequence te selecteren, gevolgd door nog een paar andere operaties:

Listing 3

 

Je ziet de conventie, net als bij Java, dat indexen starten op 0, startwaarden inclusief zijn, en eind-/stop-waarden gewoonlijk exclusief zijn.

Alle bovenstaande operaties kunnen ook op immutable sequences uitgevoerd worden, dus dit werkt evengoed op tuples en (op + en * na) ook op ranges. Op de mutable varianten zoals list, kunnen vanzelfsprekend ook nog allerlei operaties, methods en expressies worden losgelaten om ze te wijzigen:

Listing 4

 

List comprehensions

Lists kunnen ook door zogenaamde list comprehensions gedefinieerd worden. Dit is het eenvoudigste uit te leggen door een voorbeeld. In dit geval wordt een lijst met de eerste 8 machten van twee samengesteld, gevolgd door een tweede voorbeeld waar het opnemen van de elementen in de lijst wordt bepaald door een toegevoegde conditional:

Listing 5

 

Dictionaries

Dicts zijn vergelijkbaar met Java’s HashMap. De syntax van de constant initialiser zijn door komma gescheiden key: value entries tussen curly braces, zoals bijvoorbeeld {“pi”: 3.14, “e”: 2.71}, of {3.14: “pi”, 2.71: “e”}.

Maar er zijn meer manieren om een dict te initialiseren:

Listing 6

 

Sets

Verder hebben we ook nog sets met als syntax {“pi”, “e”} of (net zoals bij de Sequence types) via de constructor (set(…)) met als argument een zogenaamde Iterable. Op sets zijn operaties beschikbaar als elem in s, elem not in s, s1 <= s2 (issubset), s1 | s2 (union), s1 & s2 (intersection), – (difference) en ^ (symmetric_difference).

Strings

Strings in Python zijn, net als in Java, immutable sequences van Unicode elementen. Zo zijn “foo” en ‘bar’ voorbeelden van strings. De double- en single quotes hebben geen verschillende betekenis, behalve dat je in een double-quoted string de single quote niet hoeft te escapen en vice versa. De vanuit Java bekende escape sequences zoals \b, \n, \t, \x.., etc. worden herkend. Via andere escape sequences zijn ook unicode characters te coderen.

De speciale betekenis van de backslash vervalt als je de string prefixt met r, als in r”raw\text”. Dit is vooral handig wanneer je reguliere expressies wilt definiëren.

Triple-quoted strings, als in “””Some text.”””, kunnen newlines bevatten en deze worden letterlijk overgenomen, inclusief eventuele leading white space op de volgende regel. Strings kunnen net zo geïndexeerd en gesliced worden als de eerder beschreven immutable sequences. Een enkele index levert echter gewoon een string op, geen char of iets dergelijks.

Er zijn veel handige en leuke methods gedefinieerd voor string objecten; hier vind je slechts een zeer kleine selectie:

Listing 7

 

bytes

Bytes lijken op strings, maar bevatten binaire data. Strings hebben een encode method om bytes te genereren en bytes een decode method om strings te genereren. Byte constants hebben een b voor de openings-aanhalingstekens (b”…”).

None, Booleans

None wordt gebruikt om een ‘Null’-achtig object te identificeren. Functies/methods die niets retourneren, retourneren eigenlijk None.

Booleans kennen we als False en True, zijnde instances van het bool type. De logische operators verschillen qua symbool/naam van C/Java: dat zijn hier de reserved keywords and, or en not. Maar gelijk aan Java is er de short-circuit evaluatie van de boolean operatoren and en or. De ternary operator cond ? a : b wordt in Python geschreven als a if cond else b.

Wrap-up, waarom Python?

Waarom zou je als Java-developer Python willen leren? Bijvoorbeeld hierom:

  • Python heeft een compacte, zeer expressieve syntax. Met weinig woorden en met krachtige expressies en statements kan in korte tijd veel functionaliteit gecreëerd worden. Functionaliteit die er ook nog eens elegant uitziet, goed leesbaar is en goed onderhoudbaar is.
  • Python komt met een zeer uitgebreide, complete, standard library.
  • En als dat niet volstaat, dan is voor vrijwel elk denkbare functionaliteit een extern package beschikbaar. Packages kunnen eenvoudig geïnstalleerd worden. Er is een doorzoekbare Python Package Index beschikbaar die alle Python packages van enige betekenis indexeert.
  • Python maakt programmeren gewoon nog leuker dan het al was!

Het hierboven getoonde is slechts een beperkte greep van alle aspecten en mogelijkheden van de Python programmeertaal. Desondanks hoop ik dat ik je voldoende nieuwsgierig hebt gemaakt om nader kennis te maken met de taal. Ik wens je veel programmeerplezier met Python!