Diskrimineringen mellan klass- och instansvariabler förklaras.
Variabler är i huvudsak symboler som står för ett värde som vi använder i ett program. Objektorienterad programmering gör det möjligt att använda variabler på klassnivå eller instansnivå. Syftet med den här artikeln är att ha en tydlig distinktion mellan de typer av variabler som erbjuds av Pythons objektmodell och går vidare för att ytterligare diskutera ett märkligt beteende som kan leda till några överraskande resultat, om det försummas. Så låt oss börja!
Enligt Pythons objektmodell finns det två typer av dataattribut på Pythonobjekt: klassvariabler och instansvariabler.
Klassvariabler – Deklareras i klassdefinitionen (men utanför alla instansmetoder). De är inte knutna till något särskilt objekt i klassen och delas därför mellan alla objekt i klassen. Om en klassvariabel ändras påverkar detta alla instansobjekt samtidigt.
Instansvariabel – Deklareras inne i klassens konstruktionsmetod (__init__
-metoden). De är knutna till den specifika objektinstansen av klassen, därför är innehållet i en instansvariabel helt oberoende från en objektinstans till en annan.
Låt oss börja med ett kort och lättförståeligt exempel:
class Car:
wheels = 4 # <- Class variable def __init__(self, name):
self.name = name # <- Instance variable
Ovan är den grundläggande, utan krusiduller definierade klassen Car definierad. Varje instans av den kommer att ha klassvariabeln hjul tillsammans med instansvariabelns namn. Låt oss instantiera klassen för att få tillgång till variablerna.
>>> jag = Car('jaguar')
>>> fer = Car('ferrari')>>> jag.name, fer.name
('jaguar', 'ferrari')>>> jag.wheels, fer.wheels
(4, 4)>>> Car.wheels
4
Accessing the instance variable name is pretty straight forward. Det finns dock lite mer flexibilitet när det gäller åtkomst till klassvariabeln. Som ovan kan vi komma åt hjul via objektinstansen eller själva klassen.
Också att försöka komma åt namnet via klassen kommer att resultera i ett AttributeError eftersom instansvariabler är objektspecifika och skapas när __init__
konstruktören åberopas. Detta är den centrala skillnaden mellan klass- och instansvariabler.
>>> Car.name
AttributeError: type object 'Car' has no attribute 'name'
Nu antar vi för tillfället att vår Jaguar-bil har tre hjul. (Ja, det är dumt, men ha tålamod med det! 😅). För att representera det i vår kod kan vi ändra variabeln wheels.
>>> Car.wheels = 3
Vad vi gjorde ovan kommer att göra att alla bilar har 3 hjul eftersom vi har ändrat en klassvariabel, vilket kommer att gälla alla instanser av klassen Car.
>>> jag.wheels, fer.wheels
(3, 3)
Det är alltså så att om vi ändrar en klassvariabel i klassnamnsutrymmet så påverkar det alla instanser av klassen. Låt oss återkalla ändringen och ändra hjulvariabeln med hjälp av jag-objektet.
>>> Car.wheels = 4
>>> jag.wheels = 3
Detta kommer att ge oss det resultat vi önskar.
>>> jag.wheels, fer.wheels, Car.wheels
(3, 4, 4)
Och vi fick visserligen det resultat vi önskade, men det som hände bakom kulisserna är att en ny hjulvariabel har lagts till i jag-objektet och att den nya variabeln skuggar klassvariabeln med samma namn, överordnar och döljer den. Vi kan komma åt båda hjulvariablerna enligt nedan:
>>> jag.wheels, jag.__class__.wheels
(3, 4)
Därmed kan vi dra slutsatsen att jag.wheels = 3
har skapat en ny instansvariabel med samma namn som klassvariabeln (hjul). Detta är ett viktigt begrepp att vara medveten om.
Att använda sig av klass- och instansspecifika variabler kan säkerställa att vår kod följer DRY-principen för att minska upprepningar i koden.
Att veta detta känner jag kan vara riktigt praktiskt och spara många timmars felsökning en dag. 😇
Se mitt inlägg för att läsa mer om olika typer av metoder (instance, class & static) i Python.
Slutsatser
- Klassvariabler delas av alla objekt medan instansvariabler är till för data som är unika för varje instans.
- Instansvariabler åsidosätter klassvariabler som har samma namn, vilket oavsiktligt kan introducera buggar eller överraskande beteenden i vår kod.