Vad är __init__ i Python? [With Examples]

By rik

Är du nyfiken på att börja använda objektorienterad design med Python? Ta dina första steg nu genom att bekanta dig med Pythons __init__-metod.

I denna handledning kommer vi att utforska grunderna i Python-klasser och objekt, och sedan gå vidare till att undersöka metoden __init__.

Efter att ha läst denna guide kommer du kunna besvara följande frågor:

  • Vad är instansvariabler eller instansattribut?
  • Hur underlättar __init__-metoden initialisering av instansattribut?
  • Hur kan vi ange förvalda värden för attribut?
  • Hur kan klassmetoder fungera som konstruktorer för att generera objekt?

Låt oss sätta igång.

Python-klasser och objekt

Klasser utgör kärnan i objektorienterad programmering i Python. Vi har möjlighet att skapa en klass och definiera attribut och metoder – vilket möjliggör en sammankoppling av data och relaterad funktionalitet.

När en klass väl är skapad kan den användas som en modell (eller en ritning) för att skapa objekt (instanser).

👩‍🏫 Dags för ett exempel! Låt oss konstruera en Employee-klass, där varje objekt i klassen kommer att ha följande egenskaper:

  • full_name: den anställdes fullständiga namn i formatet förnamn efternamn
  • emp_id: anställnings-ID
  • avdelning: den avdelning de tillhör
  • erfarenhet: deras antal år av erfarenhet

Vad betyder detta? 🤔

Varje anställd kommer att utgöra en instans eller ett objekt av klassen Employee. Dessutom kommer varje objekt ha sina egna specifika värden för full_name, emp_id, department och experience.

Dessa egenskaper benämns även instansvariabler, och termerna attribut och instansvariabler kommer att användas synonymt.

Vi kommer snart att lägga till attribut. För tillfället skapar vi en Employee-klass på detta vis:

    class Employee:
        pass

Genom att använda pass (som en temporär åtgärd) undviker vi fel när vi exekverar skriptet.

Även om den aktuella versionen av Employee-klassen inte är direkt användbar, är den fortfarande en giltig klass. Följaktligen kan vi skapa objekt av klassen Employee:

    employee_1 = Employee()

    print(employee_1)
    #Output: <__main__.Employee object at 0x00FEE7F0>

Vi kan även lägga till attribut och initiera dem med värden som illustreras här:

    employee_1.full_name="Amy Bell"
    employee_1.department="HR"

Men det här sättet att addera instansattribut är ineffektivt och kan leda till fel. Det möjliggör inte heller att klassen fungerar som en mall för att generera objekt. Här kommer __init__-metoden in i bilden.

Förstå rollen för __init__-metoden i en Python-klass

Vi borde kunna initialisera instansvariablerna när vi instansierar ett objekt, och __init__-metoden hjälper oss att göra just det. __init__-metoden anropas varje gång ett nytt objekt i klassen skapas, för att sätta initialvärdena för instansvariablerna.

Om du har erfarenhet av programmering i språk som C++ kommer du att upptäcka att __init__-metoden fungerar på liknande sätt som konstruktörer.

Definiera __init__-metoden

Låt oss nu integrera metoden __init__ i Employee-klassen:

    class Employee:
        def __init__(self, full_name,emp_id,department,experience):
            self.full_name = full_name
            self.emp_id = emp_id
            self.department = department
            self.experience = experience

self-parametern avser klassens instans, och self.attribute initierar instansattributet med värdet som anges på höger sida.

Nu kan vi skapa objekt på detta sätt:

    employee_2 = Employee('Bella Joy','M007','Marketing',3)
    print(employee_2)
    # Output: <__main__.Employee object at 0x017F88B0>

När vi skriver ut anställdsobjekten får vi ingen användbar information förutom vilken klass de tillhör. Låt oss addera en __repr__-metod som definierar en presentationssträng för klassen:

     def __repr__(self):
            return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

När vi lägger till __repr__ i Employee-klassen ser det ut såhär:

    class Employee:
        def __init__(self, full_name,emp_id,department,experience):
            self.full_name = full_name
            self.emp_id = emp_id
            self.department = department
            self.experience = experience

         def __repr__(self):
            return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

Nu har anställdsobjekten en mer användbar presentationssträng:

    print(employee_2)
    # Output: Bella Joy,M007 from Marketing with 3 years of experience.

Några konventioner

Innan vi fortsätter, här är några punkter att ta fasta på:

  • Vi använde self som den första parametern i __init__-metoden för att hänvisa till själva klassinstansen, och self.attribute_name för att initiera de diverse attributen. Att använda self är den vedertagna konventionen (du kan dock välja vilket annat namn som helst).
  • När vi definierar __init__-metoden ser vi till att parametrarnas namn i __init__-definitionerna matchar attributens namn. Detta bidrar till bättre läsbarhet.

Hur man lägger till standardvärden för attribut

I det exempel vi har arbetat med hittills är alla attribut obligatoriska. Det innebär att objektskapandet enbart lyckas om vi skickar in värden för samtliga fält till konstruktorn.

Testa att instansiera ett objekt av Employee-klassen utan att skicka med värdet för erfarenhetsattributet:

    employee_3 = Employee('Jake Lee','E001','Engineering')

Du kommer att få följande felmeddelande:

    Traceback (most recent call last):
      File "main.py", line 22, in <module>
        employee_3 = Employee('Jake Lee','E001','Engineering')
    TypeError: __init__() missing 1 required positional argument: 'experience'

Men om du vill göra vissa attribut valfria kan du åstadkomma det genom att tillhandahålla standardvärden för dessa attribut i samband med att du definierar __init__-metoden.

Här tillhandahåller vi standardvärdet 0 för erfarenhetsattributet:

    class Employee:
        def __init__(self, full_name,emp_id,department,experience=0):
            self.full_name = full_name
            self.emp_id = emp_id
            self.department = department
            self.experience = experience

         def __repr__(self):
            return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

Employee_3-objektet skapas utan att erfarenhetsattributet får ett värde; standardvärdet 0 används för erfarenhet.

    employee_3 = Employee('Jake Lee','E001','Engineering')
    print(employee_3.experience)
    # Output: 0

Alternativa klasskonstruktörer med klassmetoder

Hittills har vi endast sett hur man definierar __init__-metoden och anger standardvärden för attribut där det är relevant. Vi vet också att vi måste skicka med värdena för de obligatoriska attributen i konstruktorn.

Ibland kan dock värdena för dessa instansvariabler (eller attribut) finnas i en annan datastruktur, som till exempel en tuple, en ordbok eller en JSON-sträng.

Så, vad gör vi då?

Låt oss titta på ett exempel. Antag att vi har värdena för instansvariabeln i en Python-ordbok:

    dict_fanny = {'name':'Fanny Walker','id':'H203','dept':'HR','exp':2}

Vi kan extrahera ordboken och få tag i alla attribut på följande sätt:

    name = dict_fanny['name']
    id = dict_fanny['id']
    dept = dict_fanny['dept']
    exp = dict_fanny['exp']

Efter detta kan du skapa ett objekt genom att skicka dessa värden till klasskonstruktorn:

    employee_4 = Employee(name, id, dept, exp)
    print(employee_4)
    # Output: Fanny Walker,H203 from HR with 2 years of experience.

Kom ihåg att du måste göra detta för varje nytt objekt du skapar. Detta tillvägagångssätt är ineffektivt, och vi kan absolut göra det bättre. Men hur?

I Python kan vi utnyttja klassmetoder som konstruktörer för att generera objekt i klassen. För att skapa en klassmetod använder vi @classmethod-dekoratören.

Låt oss definiera en metod som analyserar ordboken, hämtar värdena för instansvariablerna och använder dem för att konstruera Employee-objekt.

        @classmethod
        def from_dict(cls,data_dict):
            full_name = data_dict['name']
            emp_id = data_dict['id']
            department = data_dict['dept']
            experience = data_dict['exp']
            return cls(full_name, emp_id, department, experience)

När vi behöver skapa objekt med hjälp av data från ordboken kan vi använda klassmetoden from_dict().

💡 Notera användningen av cls i klassmetoden i stället för self. Precis som self används för att referera till instansen, används cls för att referera till klassen. Klassmetoder är också bundna till klassen och inte till objekt.

När vi anropar klassmetoden from_dict() för att skapa objekt, anropar vi den i klassen Employee:

    emp_dict = {'name':'Tia Bell','id':'S270','dept':'Sales','exp':3}
    employee_5 = Employee.from_dict(emp_dict)
    print(employee_5)
    # Output: Tia Bell,S270 from Sales with 3 years of experience.

Om vi nu har en ordbok för varje enskild anställd, kan vi använda klassmetoden from_dict() som en konstruktor för att instansiera objekt – utan att behöva manuellt hämta de enskilda variabelvärdena från ordboken.

📝 En notering om klassvariabler

Här definierade vi klassmetoden som är knuten till klassen, inte enskilda instanser. I likhet med klassmetoder kan vi även ha klassvariabler.

Precis som klassmetoder är klassvariabler bundna till klassen och inte till en instans. När ett attribut har ett fast värde för alla instanser av klassen kan vi överväga att definiera dem som klassvariabler.

Vanliga frågor

1. Varför behövs __init__-metoden i Python?

__init__-metoden i en klassdefinition ger oss möjligheten att initialisera attributen eller instansvariablerna för alla instanser av klassen. __init__-metoden anropas varje gång en ny instans av klassen skapas.

2. Är det möjligt att ha flera __init__-metoder i en Python-klass?

Syftet med att ha flera __init__-metoder i en Python-klass är att erbjuda flera konstruktörer som instansierar objekt. Men det går inte att definiera flera __init__-metoder. Om du definierar flera __init__-metoder kommer den andra och den sista implementeringen att skriva över den första. Men du kan använda @classmethod-dekoratören för att definiera klassmetoder som kan användas som konstruktörer för att instansiera objekt.

3. Vad händer om du inte definierar __init__-metoden i en klass?

Om du inte definierar __init__-metoden kan du fortfarande instansiera objekt. Du behöver dock lägga till instansvariabler manuellt och tilldela värden till var och en av dem. Du kommer inte kunna skicka in värdena för instansvariablerna i konstruktorn. Detta är inte bara riskabelt, utan det motverkar också syftet med att ha klassen som en ritning från vilken vi kan instansiera objekt.

4. Kan du ha standardvärden för argument i __init__-metoden?

Ja, det är fullt möjligt att tillhandahålla standardvärden för ett eller flera attribut i samband med att du definierar __init__-metoden. Att tillhandahålla standardvärden gör att dessa attribut blir valfria i konstruktorn. Attributen antar standardvärdena när vi inte skickar in värdena för dessa attribut i konstruktorn.

5. Är det möjligt att ändra attribut utanför __init__-metoden?

Ja, du kan alltid uppdatera värdet på ett attribut utanför __init__-metoden. Du kan också lägga till nya attribut dynamiskt till en instans efter att du har skapat den specifika instansen.

Sammanfattning

I denna handledning har vi utforskat hur man använder __init__-metoden för att initiera värden för instansvariabler. Även om detta är relativt okomplicerat kan det kännas repetitivt, särskilt när man har många attribut.

Om intresset finns kan du utforska modulen dataklasser. I Python 3.7 och senare kan du använda den inbyggda dataklassmodulen för att skapa dataklasser som lagrar data. Förutom standardimplementeringar av __init__ och andra vanliga metoder, kommer de med många praktiska funktioner för typtips, komplexa standardvärden och optimering.

Lär dig också mer om if __name__==’__main__’ i Python.