Zoeken op website

8 Python-fouten die zelfs senior ontwikkelaars maken


Zelfs de meest ervaren seniorontwikkelaars, uitgerust met jarenlange ervaring, struikelen regelmatig over valkuilen in Python. Het is niet moeilijk en het komt niet voort uit een gebrek aan technische vaardigheid, maar uit een ingebakken vertrouwen op programmeerintuïties die gewoonweg niet waar blijken te zijn in het Python-ecosysteem.

Als iemand die de hele tijd talloze fouten maakt, weet ik hoe gemakkelijk het is om in de val van zelfgenoegzaamheid te trappen, of gewoon aan te nemen dat sommige dingen soepel tussen talen overgaan. Dit zijn niet de enige fouten die iedereen maakt, maar het zijn de meest voorkomende die ik heb gezien en opgemerkt.

Wijzigbare standaardargumenten

Je schrijft een functie en plakt een lege lijst of woordenboek als standaardparameter, in de veronderstelling dat Python op magische wijze elke keer dat de functie wordt aangeroepen zonder dat specifieke argument een nieuwe, lege container zal maken. Dit mentale model klinkt helemaal logisch, vooral als je uit andere programmeertalen komt waar standaardparameters meestal dynamisch worden geëvalueerd wanneer je de code uitvoert.

Helaas werkt Python fundamenteel anders. Wanneer je een functie definieert met een veranderlijke standaardwaarde, wordt dat object slechts één keer aangemaakt, namelijk op het moment dat je het definieert, niet wanneer je het aanroept. In plaats van een veranderlijk object direct in de functiedefinitie te plaatsen, moet je het argument standaard instellen op een onveranderlijke sentinel-waarde, meestal None. Binnen de functie controleer je eenvoudig of het argument None is en maak je daar vervolgens je nieuwe lege lijst of woordenboek aan.

De val van late binding closure

Wanneer je een groep functies binnen een lus maakt, zou je kunnen verwachten dat elk van hen het specifieke nummer of item waarmee het is gemaakt onthoudt. In Python nemen deze functies echter geen permanente momentopname van de waarde op dat moment. In plaats daarvan houden ze een live verbinding met de variabele van de lus.

Omdat de lus blijft draaien en diezelfde variabele blijft bijwerken, wijzen alle functies uiteindelijk naar wat de uiteindelijke waarde was toen de lus klaar was. Om dit op te lossen, moet je een manier vinden om de huidige waarde vast te zetten, zodat de functie deze correct onthoudt.

Het verkeerd gebruiken van __del__ voor middelenbeheer

Voor degenen die afkomstig zijn van talen zoals C++, zijn oude gewoonten vrij moeilijk af te leren. In C++ is RAII (Resource Acquisition Is Initialization) een fundamenteel idee. Ontwikkelaars vertrouwen sterk op deterministische destructors om op betrouwbare wijze bestanden te sluiten, databaseverbindingen vrij te geven en netwerksockets vrij te maken zodra een object uit de scope verdwijnt. Dus wanneer deze ervaren programmeurs overstappen naar Python, zoeken ze natuurlijk naar iets soortgelijks, zoals de __del__-methode.

Dit is een groot misverstand omdat __del__ geen deterministische destructor is, maar een finalizer die direct verbonden is met Pythons automatische garbage collection. De huidige oplossing van Python voor resourcebeheer zijn contextmanagers. Gebruik de __enter__ en __exit__ dunder-methoden in je klassen en gebruik regelmatig de with-instructie. Schrijf in feite gewoon code op de Python-manier.

Overmatige afhankelijkheid van is versus ==

Het fundamentele probleem van zwaar vertrouwen op is komt voort uit het misverstand over wat deze twee operatoren eigenlijk doen. De == operator controleert op waarde-gelijkheid, wat betekent dat het evalueert of twee objecten exact dezelfde gegevens bevatten. De is operator controleert op objectidentiteit en evalueert of twee variabelen naar hetzelfde object in het geheugen verwijzen.

Om geheugen te besparen en de prestaties te verbeteren, reserveert en cachet Python automatisch kleine gehele getallen en bepaalde korte strings. Dus, als je de waarde 100 aan twee verschillende variabelen toewijst, wijst Python beide variabelen naar hetzelfde gecachte gehele-getalobject in het geheugen. Dit leidt tot onvoorspelbare logische fouten en sluwe bugs die ongelooflijk moeilijk op te sporen zijn.

Een collectie wijzigen tijdens het itereren

Het wijzigen van een lijst ter plaatse rommelt met de lengte ervan en verschuift al zijn interne indexen, wat de iterator in de war brengt. De interne teller van de lus heeft geen idee dat de lijst is veranderd, dus de iterator gaat gewoon blindelings naar de volgende index. Je blijft achter met gedeeltelijk verwijderde of overgeslagen elementen, en enkele verwarrende resultaten die moeilijk op te sporen en te debuggen zijn.

Gebruik lijstcomprehensie of de filter()-functie om een gloednieuwe, gefilterde lijst te maken. Een lijstcomprehensie omzeilt dat probleem met verschuivende indexen en voorkomt ongewenste bijwerkingen. Python-lijsten bevatten alleen verwijzingen naar objecten, in plaats van de daadwerkelijke objecten te dupliceren, dus het geheugenverbruik hiervan is klein.

Het opvangen van BaseException of Exception

Wanneer je een kale except-clausule gebruikt zonder specifiek te zijn over de fout, behandelt Python het net alsof je BaseException opvangt. Het zal elke uitzondering die verschijnt opslokken, werkelijke bugs verbergen en je app tot een nachtmerrie maken om te debuggen. Dit is gewoon luiheid.

De gouden regel voor foutafhandeling is om alleen te vangen wat je daadwerkelijk op een nette manier kunt afhandelen. Je zou altijd specifieke uitzonderingen moeten nemen die daadwerkelijk relevant zijn voor wat je aan het doen bent. Als je niet precies kunt uitleggen waarom een stukje code kan falen en hoe het zou moeten herstellen, dan zou je die uitzondering eigenlijk helemaal niet moeten vangen.

Klassevariabele Verduistering

Het veranderen van een classvariabele naar een specifieke instantie met syntax zoals self.variable=value wijzigt het gedeelde classattribuut niet. In plaats daarvan maakt Python een gloednieuwe instantievariabele alleen voor de scope van dat specifieke object. Het attribuutzoekpad van Python controleert altijd eerst het instanceniveau voordat het terugvalt op het classniveau. Dus deze nieuwe instantievariabele onderschept dingen, waardoor het de onderliggende classvariabele effectief overschaduwt of verbergt, alleen voor dat specifieke object.

In plaats daarvan is het altijd een goed idee om te verwijzen naar en klassevariabelen te wijzigen met de expliciete klassenaam zelf, zoals ClassName.variable=value, in plaats van self te gebruiken.

Logging verkeerd gebruiken

Het is een grote fout om f-strings te gebruiken binnen logging-aanroepen. Wanneer je een logbericht maakt met een f-string, evalueert Python die string onmiddellijk en creëert deze direct, wat een enorme prestatievermindering veroorzaakt. Omdat f-strings dwingend vroege evaluatie afdwingen, interpolateert je app variabelen en kan zelfs dure functieroutines uitvoeren om een bericht op te maken dat de logger toch gewoon weggooit.

De juiste manier om dit te doen is door een stringliteral te gebruiken met C-stijl plaatsaanduidingen en de argumenten gewoon afzonderlijk aan de logger door te geven. Vasthouden aan de oudere formatteringsstijl met afzonderlijke argumenten in je logberichten is geen stap terug. Het is eigenlijk een heel belangrijke best practice om je applicaties performant en observeerbaar te houden.


Python-fouten zijn gemakkelijk te maken vanwege de manier waarop de taal werkt. Veel kernconcepten, zoals hoe Python geheugen beheert of hoe het namen aan objecten koppelt, werken anders dan in andere populaire programmeertalen. Het interne systeem van Python is ontworpen om efficiëntie en specifieke technische regels te prioriteren, het kan soms resultaten opleveren die contra-intuïtief aanvoelen.

Door je te concentreren op veelvoorkomende Python-fouten en problemen die vaak voorkomen, kun je voorkomen dat je steeds dezelfde fouten maakt. Je kunt runtime-fouten en verwarrende problemen met statusbeheer vermijden door gewoon te onthouden dat wij allemaal in veelvoorkomende fouten kunnen vallen.

Gerelateerde artikelen