Python from Scratch Objektorienterad programmering

Välkommen tillbaka till lektion fyra i vår Python från grunden serier. Denna handledning tar på sig några förkunskaper om variabler, datatyper, funktioner och utskrift. Om du inte är uppdaterad, kolla in de föregående tre artiklarna i serien för att komma ikapp.

Idag kommer vi att dyka in i ämnet Objektorienterad Programmering (OOP). OOP är ett mycket kraftfullt sätt att organisera din kod och en god förståelse av begreppen bakom den kan verkligen hjälpa dig att få ut det mesta av din kodning.


Föredrar en skärmdump?


Transkription


Vad är Objektorienterad programmering?

Python är i första hand utformat som ett objektorienterat programmeringsspråk - men vad betyder "objektorienterad" faktiskt?

Det finns en mängd definitioner för termen, och du kan prata för bokstavligen timmar som försöker förklara de komplicerade insatserna, nyanserna och skillnaderna i implementeringar, men jag ska försöka ge en snabb översikt.

I stort sett är objektorienterad programmering konceptet att de objekt som vi manipulerar i programmeringen är viktigare än den logik som behövs för att manipulera dessa objekt. Traditionellt har ett program ses som ett recept - en uppsättning instruktioner som du följer från början till slut för att slutföra en uppgift. Det kan fortfarande vara sant, och för många enkla program är det allt som krävs. Det här tillvägagångssättet kallas ibland procedurprogrammering.

OOP sätter objekt i mitten av processen.

Å andra sidan, eftersom programmen blir mer och mer komplicerade och sammanfyllda, blir logiken som behövs för att skriva dem på ett rent processuellt sätt mer och mer vridet och svårt att förstå. Ofta objektorienterade tillvägagångssätt kan hjälpa till med det.

När vi pratar om objektorienterade tillvägagångssätt, sätter vi objekten i centrum av processen istället för att bara använda dem som nödvändiga behållare för information som en del av våra processuella instruktioner. Först definierar vi de objekt som vi vill manipulera och hur de är relaterade till varandra, och sedan börjar vi kölja det med logik för att programmet ska fungera..

När jag pratar om "föremål", kan jag prata om alla sorters saker. Ett "objekt" kan representera en person (som definieras av egenskaper som namn, ålder, adress etc.) eller ett företag (som definieras av saker som antal anställda och så vidare) eller till och med något mycket mer abstrakt, som en knappen i ett datorgränssnitt.

I denna introduktion kommer vi inte att täcka alla koncept i detta ämne eftersom vi skulle vara här hela natten, men i slutet av handledningen hoppas jag att du kommer att ha en gedigen förståelse för de principer du behöver att börja omedelbart med hjälp av några enkla objektorienterade tekniker i dina Python-program. Ännu bättre är dessa begrepp ganska likartade i många programmiljöer. Kunskapen överför från språk till språk ganska snällt.


Komma igång

Jag nämnde tidigare att det första vi bör göra när vi går för en OOP-strategi är att definiera de objekt vi ska använda. Hur vi gör det här är att först definiera de egenskaper som den har med en klass. Du kan tänka på en klass som en slags mall; en guide för hur ett objekt ska struktureras. Varje objekt tillhör en klass och ärvererar egenskaperna hos den klassen, men handlar individuellt mot de andra objekten i den klassen.

Ett objekt kallas ibland som en "förekomst" av en klass.

Som ett enkelt exempel kan du ha en klass som heter "person" med, säg en ålder och en namnegenskap, och en förekomst av den klassen (ett objekt) skulle vara en enda person. Den personen kan ha ett namn på? Andy? och 23 år, men du kan samtidigt få en annan person som tillhör samma klass med namnet Lucy? och en ålder av 18 år.

Det är svårt att förstå detta utan att se det i praktiken, så låt oss få några riktiga koden att gå.

Definiera en klass

För att definiera en klass, i typiska enkla Python-mode använder vi ordet "klass", följt av namnet på din nya klass. Jag ska göra en ny klass här, kallad "husdjur". Vi använder ett kolon efter namnet, och sedan är innehållet i klassdefinitionen inryckt. Men med en klass finns inga parenteser:

 klass husdjur: 

Så nu har vi en klass, men det är ganska värdelöst utan något i det. För att börja, låt oss ge det ett par egenskaper. För att göra detta definierar du bara några variabler i klassen - jag ska gå med antalet ben till att börja med. Som vanligt bör du alltid namnge dina variabler så att det är lätt att berätta vad de är. Låt oss vara original och kalla det "number_of_legs". Vi måste definiera ett värde eller vi får ett fel. Jag använder 0 här (det spelar ingen roll för mycket i det här fallet eftersom antalet ben kommer att vara specifika för varje förekomst av klassen - en fisk har inte lika många ben som en hund eller en anka, etc. - så måste vi ändra det värdet för varje objekt ändå).

 klass husdjur: number_of_legs = 0 

Instanser och medlemsvariabler

En klass i sig är inte något du kan direkt manipulera; Först måste vi skapa en förekomst av klassen att leka med. Vi kan lagra den förekomsten i en variabel. Utanför klassen (utan indrag), låt oss göra en förekomst av klassen och lagra den i variabeln, "doug". För att skapa en ny instans av en klass skriver du bara klassens namn och sedan ett par parenteser. Vid denna tidpunkt behöver du inte oroa dig för parenteserna, men senare ser du att de är där eftersom, som en funktion, det finns ett sätt att passera i en variabel för klassens användning när du först skapar instansen.

En klass i sig är inte något som du direkt kan manipulera.

 klass husdjur: number_of_legs = 0 doug = husdjur () 

Nu när vi har en förekomst av en klass, hur får vi tillgång till och manipulera dess egenskaper? För att referera till en egenskap hos ett objekt först måste vi berätta för Python vilket objekt (eller vilket exempel av en klass) vi pratar om, så vi ska börja med doug. Då ska vi skriva en period för att indikera att vi hänvisar till något som finns i vår doug-instans. Efter perioden lägger vi till namnet på vår variabel. Om vi ​​åtkomst till number_of_legs variabel, det kommer att se ut så här:

 doug.number_of_legs 

Vi kan behandla det nu exakt som vi skulle behandla någon annan variabel - här kommer jag att anta att doug är en hund och kommer att ge den variabla värdet av 4.

För att komma åt denna variabel kommer vi att använda den igen precis som vi skulle behandla någon annan variabel, men använda det doug.number_of_legs egendom istället för det normala variabelnamnet. Låt oss lägga in en rad för att skriva ut hur många ben doug har så att vi kan visa att det fungerar som det ska:

 klass husdjur: number_of_legs = 0 doug = pet () doug.number_of_legs = 4 print "Doug har% s ben." % doug.number_of_legs 

Om du kör koden ovan ser du att den skrivs ut för oss. Det definierade vår "sällskapsdjur" -klass, skapade en ny instans av den klassen och lagrade den i variablet doug, och i det fallet tilldelades det värdet 4 till number_of_legs variabel som den ärvde från sin klass.

Så du kan se från det mycket förenklade exemplet hur du kan börja bygga fina, modulära datastrukturer som är klara och lätta att använda, och kan börja skala ganska bra.


Introducerar logik

Okej, så det är själva grunderna för klasser och objekt, men för tillfället kan vi bara verkligen använda klasser som datastrukturer - eller, behållare för variabler. Det är allt bra och bra, men om vi vill börja utföra mer komplexa uppgifter med de data som vi manipulerar, behöver vi ett sätt att introducera en viss logik i dessa objekt. Hur vi gör det är med metoder.

Metoder är i huvudsak funktioner som ingår i en klass. Du definierar en på exakt samma sätt som du skulle ha en funktion, men skillnaden är att du placerar den i en klass och den tillhör den klassen. Om du någonsin vill ringa den metoden måste du först referera till ett objekt av den klassen precis som de variabler vi tittade på tidigare.

Metoder är i huvudsak funktioner som ingår i en klass.

Jag ska skriva ett snabbt exempel här i vår husdjursklass för att demonstrera; låt oss skapa en metod som kallas sömn, som kommer att skriva ut ett meddelande när det först kallas. Precis som en funktion kommer jag att sätta "def" för "define", och då ska jag skriva namnet på den metod jag vill skapa. Då ska vi sätta våra parentes och semikolon, och sedan starta en ny rad. Som vanligt kommer allt som ingår i denna metod att vara inryckt en extra nivå.

Nu finns det en annan skillnad mellan en metod och en funktion: en metod måste alltid alltid ha ett argument som kallas "själv" mellan parenteserna. När Python ringer en metod, skickas det nuvarande objektet till den metoden som det första argumentet. Med andra ord, när vi ringer doug.sleep (), Python kommer faktiskt att passera objektet 'doug' som ett argument för sömnmetoden.

Vi förstår varför det är senare, men för tillfället måste du veta att med en metod måste du alltid inkludera ett argument som kallas "själv" först i listan (om du vill lägga till fler argument kan du lägga till dem efteråt, precis som om du passerade flera argument till en funktion). Om du inte inkluderar det här argumentet, när du kör koden kommer du att få ett fel som kastas eftersom Python passerar i ett argument (det här "själv" -objektet) och metoden säger "Hej, man, Jag tar inga argument, vad pratar du om? '. Det är detsamma som om du försökte skicka ett argument till en funktion som inte accepterar några argument.

Så här är vad vi har hittills:

 klass husdjur: number_of_legs = 0 def sömn (egen): doug = pet () 

Inom den här metoden kommer vi att skriva ett utskriftsutdrag som så:

 klass husdjur: number_of_legs = 0 def sömn (egen): skriv ut "zzz" doug = pet () 

Nu, om vi vill använda den här metoden använder vi helt enkelt en förekomst av djurklassen för att referera till den. Precis som number_of_legs variabel, vi skriver namnet på förekomsten (vi har en som kallas doug), då en period, då namnet på metoden inklusive parenteser. Observera att vi kallar sömn utan argumenter, men Python kommer att lägga till i det argumentet av sig själv, så vi kommer att sluta med rätt mängd argument totalt.

 klass husdjur: number_of_legs = 0 def sömn (egen): skriv ut "zzz" doug = pet () doug.sleep () 

Om du kör den här koden ska du se att den skriver ut det meddelande vi skrev.

Data

Bra, så nu då skriver vi en ny metod för att skriva ut hur många ben husdjuret har för att visa hur du kan använda metoder för att börja manipulera data i klassen och för att visa varför vi måste inkludera detta förvirrande "själv" argument. Låt oss göra en ny metod, kallad "count_legs'.

Det är här som "själv" -argumentet kommer in. Kom ihåg när vi åtkomst number_of_legs från utanför klassen och vi var tvungna att använda "doug.number_of_legs" istället för bara "number_of_legs"? Samma princip gäller om vi vill veta vad som finns i den variabeln, måste vi referera den genom att först ange förekomsten som innehåller den variabeln.

Men vi vet inte vad förekomsten kommer att kallas när vi skriver klassen, så vi tar sig runt det med hjälp av variabeln "själv". "själv" är bara en referens till det objekt som för närvarande manipuleras. Så för att komma åt en variabel i nuvarande klass behöver du bara förorda det med "själv" och sedan en period, som så:

 klass djur: number_of_legs = 0 def sömn (egen): tryck "zzz" def count_legs (self): print "Jag har% s ben"% self.number_of_legs doug = pet () doug.number_of_legs = 4 doug.count_legs 

I praktiken betyder det att det där som du skriver "själv" i din metod, när du kör den metod som själv ersätts med objektets namn, så när vi kallar doug.count_legs () är "jaget" ersatt av "doug". För att visa hur det här fungerar med flera instanser, låt oss lägga till en andra instans, som representerar ett annat husdjur, kallat 'nemo':

 klass husdjur: number_of_legs = 0 def sömn (egen): tryck "zzz" def count_legs (self): print "Jag har% s ben"% self.number_of_legs doug = pet () doug.number_of_legs = 4 doug.count_legs () nemo = husdjur () nemo.number_of_legs = 0 nemo.count_legs () 

Detta kommer att skriva ut ett meddelande för 4 och sedan 0 ben, precis som vi ville ha, för när vi kallar 'nemo.count_legs ()' ersätts 'self''en istället för' doug '.

På så sätt kommer vår metod att köras exakt som avsedd för att "självreferensen" kommer att förändras dynamiskt beroende på sammanhanget och tillåter oss att manipulera data endast inom det aktuella objektet.

De viktigaste sakerna du behöver komma ihåg om metoder är att de är precis som funktioner, förutom att det första argumentet måste vara "själv" och att för att referera till en intern variabel måste du förorda variabelnamnet med "själv".

Precis som en anteckning: Du kan faktiskt använda något namn istället för "själv" för dina metoder. -Metoderna här skulle fungera lika bra om vi bytte namn på variabeln "själv" till något ord. Att använda namnet "själv" är helt enkelt en konvention som är användbar för Python-programmerare eftersom det gör koden mycket mer standard och lätt att förstå, även om den är skriven av någon annan. Mitt råd skulle vara att hålla fast vid konventionerna.


Några mer avancerade funktioner

Nu när vi har gått över grunderna, låt oss ta en titt på några mer avancerade funktioner i klasserna, och hur de kan hjälpa till att göra programmeringen lättare att strukturera.

Nästa sak vi ska prata om är arv. Som namnet kan antyda är arv processen att skapa en ny klass baserad kring en föräldraklass och låta den nya klassen få arvet av föräldrarklassens egenskaper. Den nya klassen kan ta alla metoder och variabler från förälderklassen (ofta kallad basklassen).

Arv är processen att skapa en ny klass baserad kring en föräldraklass.

Låt oss utöka vårt husdjursexempel för att se hur det här kan vara användbart. Om vi ​​använder "husdjur" som vår föräldraklass kan vi skapa en barnklass som ärvt från djurklassen. Barnklassen kan vara något som "hund" eller "fisk" - något som fortfarande är ett "husdjur", men är mer specifikt än det. En hund är ett husdjur och gör samma saker som alla husdjur gör - till exempel det äter och sover och har en ålder och ett antal ben - men det gör andra saker som är specifika för att vara en hund, eller åtminstone mer specifika än att vara ett husdjur: Hundar har päls, men inte alla husdjur gör det. En hund kan skälla, eller hämta en pinne, men inte alla husdjur skulle.

Att komma tillbaka till punkten säger att vi ville göra en klass i vårt program för att representera en hund. Vi skulle kunna använda arv för att ärva de metoder och variabler som finns i "husdjur" så att vår hund skulle kunna ha ett "numberOf Legs" och möjligheten att "sova" förutom alla hundspecifika saker vi kan lagra eller göra.

Nu kanske du undrar varför vi inte sätter dessa metoder och variabler i hundklassen och blir helt av med hundklassen? Nåväl, arv ger oss två olika fördelar framför den metoden: En, om vi vill ha ett föremål som är ett husdjur, men det är inte en hund - ett generiskt husdjur, om du vill - det kan vi fortfarande göra. Två, kanske senare vill vi lägga till en andra typ av husdjur - kanske en fisk. Vi kan göra den andra klassen också ärv från husdjur, så att båda klasserna kan dela allt i husdjur, men samtidigt ha sina egna mer specifika metoder och variabler som endast gäller för den typen av objekt.

Vi börjar lite försvagad i teorin här, så låt oss skriva något för att göra det lite tydligare. Först ska vi skriva en ny klass, kallad hund, men den här gången, mellan klassnamnet och kolon, kommer vi att lägga några parentes, och i dem ska vi skriva namnet av den klass som vi vill ärva från, som om vi passerar denna nya klass ett argument, som om vi skulle fungera.

Låt oss sedan ge den här klassen en enkel metod för att visa hur det fungerar. Jag ska lägga till en "bark'metod som kommer att skriva ut' woof ':

 klass djur: number_of_legs = 0 def sömn (egen): tryck "zzz" def count_legs (self): print "Jag har% s legs"% self.number_of_legs klasshund (pet): def bark 

Så, låt oss nu se vad som händer om vi gör en förekomst av den här klassen. Jag ska ringa vår nya hund doug igen. Nu, om vi ringer doug.bark ():

 klass djur: number_of_legs = 0 def sömn (egen): tryck "zzz" def count_legs (self): print "Jag har% s legs"% self.number_of_legs klasshund (pet): def bark doug = hund () doug.bark () 

Som förväntat skäller doug. Det är bra, men vi har inte sett något nytt ännu - bara en klass med en metod. Vad arv har gjort för oss är att göra alla djurfunktioner och variabler tillgängliga för oss genom vårt "doug" -objekt, så om jag gör något så här:

 klass djur: number_of_legs = 0 def sömn (egen): tryck "zzz" def count_legs (self): print "Jag har% s legs"% self.number_of_legs klasshund (pet): def bark doug = hund () doug.sleep () 

Då kommer sömnmetoden också att utföras korrekt. I själva verket tillhör vårt dougobjekt både "PET" OCH "Hund" klassen. För att säkerställa att variablerna gör detsamma som metoderna, låt oss försöka detta:

 klass djur: number_of_legs = 0 def sömn (egen): tryck "zzz" def count_legs (self): print "Jag har% s legs"% self.number_of_legs klasshund (pet): def bark doug = hund () doug.number_of_legs = 4 doug.count_legs () 

Du kan se att doug fungerar precis som tidigare och visar att våra variabler är ärvda. Vår nya barnklass är helt enkelt en specialiserad version av föräldern, med lite extra funktionalitet men behåller all tidigare funktionalitet.


Så där har du det, en snabb introduktion till objektorienterad programmering. Stanna in för nästa avbetalning i den här serien, där vi ska jobba med Python på webben!