Onderscheid tussen klasse- en instantievariabelen uitgelegd.
Variabelen zijn in wezen symbolen die staan voor een waarde die we in een programma gebruiken. Object-georiënteerd programmeren maakt het mogelijk variabelen te gebruiken op klassenniveau of op instance-niveau. Het doel van dit artikel is een duidelijk onderscheid te maken tussen de soorten variabelen die door het objectmodel van Python worden aangeboden en verder in te gaan op een eigenaardig gedrag dat tot verrassende resultaten zou kunnen leiden, als het wordt verwaarloosd.
Volgens het Python objectmodel zijn er twee soorten gegevensattributen op Python objecten: klassevariabelen en instance-variabelen.
Klassevariabelen – Aangegeven binnen de klasse-definitie (maar buiten de instance-methodes). Zij zijn niet gebonden aan een bepaald object van de klasse, en worden dus gedeeld over alle objecten van de klasse. Wijziging van een klassevariabele heeft op hetzelfde moment invloed op de instantie van alle objecten.
Instantievariabele – Aangegeven in de constructormethode van de klasse (de __init__
methode). Zij zijn gebonden aan de specifieke objectinstantie van de klasse, vandaar dat de inhoud van een instantievariabele volledig onafhankelijk is van de ene objectinstantie naar de andere.
Laten we beginnen met een kort en gemakkelijk te verteren voorbeeld:
class Car:
wheels = 4 # <- Class variable def __init__(self, name):
self.name = name # <- Instance variable
Hierboven is de basis, no-frills Car klasse gedefinieerd. Elke instantie van deze klasse heeft een klasse variabele wielen samen met de naam van de instantie variabele. Laten we de klasse instantiëren om toegang te krijgen tot de variabelen.
>>> jag = Car('jaguar')
>>> fer = Car('ferrari')>>> jag.name, fer.name
('jaguar', 'ferrari')>>> jag.wheels, fer.wheels
(4, 4)>>> Car.wheels
4
Toegang krijgen tot de naam van de instantie-variabele is vrij recht-toe-recht-aan. Er is echter een beetje meer flexibiliteit als het gaat om toegang tot de klasse variabele. Zoals hierboven, kunnen we de wielen benaderen via de instantie van het object of via de klasse zelf.
Ook het benaderen van de naam via de klasse zal resulteren in een AttributeError, omdat instantievariabelen object-specifiek zijn en worden gecreëerd wanneer __init__
de constructor wordt aangeroepen. Dit is het centrale onderscheid tussen de klasse en de instance-variabelen.
>>> Car.name
AttributeError: type object 'Car' has no attribute 'name'
Nu, laten we er voorlopig van uitgaan dat onze Jaguar auto 3 wielen heeft. (Ja, het is stom, maar heb er geduld mee!! 😅). Om dat in onze code weer te geven, kunnen we de wielen variabele wijzigen.
>>> Car.wheels = 3
Wat we hierboven deden zal alle auto’s met 3 wielen maken, omdat we een klasse variabele hebben gewijzigd, die van toepassing zal zijn op alle instanties van de klasse Auto.
>>> jag.wheels, fer.wheels
(3, 3)
Dus het wijzigen van een klasse variabele op de klasse namespace heeft invloed op alle instanties van de klasse. Laten we de wijziging terugdraaien en de wielen variabele wijzigen met behulp van het jag-object.
>>> Car.wheels = 4
>>> jag.wheels = 3
Dit zal ons de output geven die we wensen.
>>> jag.wheels, fer.wheels, Car.wheels
(3, 4, 4)
Hoewel we het resultaat kregen dat we wilden, maar wat er achter de schermen gebeurde is een nieuwe wielen variabele die is toegevoegd aan het jag-object en deze nieuwe variabele schaduwt de klasse variabele met dezelfde naam, overriding en verbergt het. We kunnen beide variabelen benaderen zoals hieronder.
>>> jag.wheels, jag.__class__.wheels
(3, 4)
Hieruit kunnen we concluderen dat jag.wheels = 3
een nieuwe instantie-variabele heeft aangemaakt met dezelfde naam als de klasse-variabele (wielen). Dit is een belangrijk concept om bewust van te zijn.
Het gebruik van klasse- en instantiespecifieke variabelen kan ervoor zorgen dat onze code zich houdt aan het DRY-principe om herhaling binnen code te verminderen.
Weten dat dit zo is, kan erg handig zijn en op een dag heel wat uren debuggen besparen.
Refereer naar deze post om verder te lezen over de verschillende soorten methoden (instance, class & static) in Python.
Conclusies
- Klassevariabelen worden gedeeld over alle objecten, terwijl instance-variabelen voor gegevens zijn die uniek zijn voor elke instantie.
- Instance-variabele overschrijft de klassevariabelen met dezelfde naam, wat per ongeluk bugs of verrassend gedrag in onze code kan introduceren.