Perché usiamo __init__ nelle classi Python?


124

Ho problemi a comprendere l'inizializzazione delle classi.

Qual è il punto di loro e come sappiamo cosa includere in loro? Scrivere nelle classi richiede un diverso tipo di pensiero rispetto alla creazione di funzioni (ho pensato che potevo semplicemente creare funzioni e poi inserirle in una classe in modo da poterle riutilizzare. Funzionerà?)

Ecco un esempio:

class crawler:
  # Initialize the crawler with the name of database
  def __init__(self,dbname):
    self.con=sqlite.connect(dbname)

  def __del__(self):
    self.con.close()

  def dbcommit(self):
    self.con.commit()

O un altro esempio di codice:

class bicluster:
  def __init__(self,vec,left=None,right=None,distance=0.0,id=None):
    self.left=left
    self.right=right
    self.vec=vec
    self.id=id
    self.distance=distance

Ci sono così tante classi con cui __init__mi imbatto quando provo a leggere il codice di altre persone, ma non capisco la logica nella loro creazione.


1
la storia di init è ... blah, blah, blah .... costruttore-distruttore ma nessun distruttore perché è disponibile la garbage collection.
MisterGeeky

Risposte:


289

Da quello che hai scritto, ti manca una parte fondamentale della comprensione: la differenza tra una classe e un oggetto. __init__non inizializza una classe, inizializza un'istanza di una classe o di un oggetto. Ogni cane ha un colore, ma i cani come classe no. Ogni cane ha quattro o meno piedi, ma la classe dei cani no. La classe è un concetto di un oggetto. Quando vedi Fido e Spot, riconosci la loro somiglianza, la loro dogma. Questa è la classe.

Quando dici

class Dog:
    def __init__(self, legs, colour):
        self.legs = legs
        self.colour = colour

fido = Dog(4, "brown")
spot = Dog(3, "mostly yellow")

Stai dicendo, Fido è un cane marrone con 4 zampe mentre Spot è un po 'storpio ed è per lo più giallo. La __init__funzione è chiamata costruttore, o inizializzatore, e viene chiamata automaticamente quando si crea una nuova istanza di una classe. All'interno di quella funzione, l'oggetto appena creato viene assegnato al parametro self. La notazione self.legsè un attributo chiamato legsdell'oggetto nella variabile self. Gli attributi sono un po 'come le variabili, ma descrivono lo stato di un oggetto o particolari azioni (funzioni) disponibili per l'oggetto.

Tuttavia, nota che non sei pronto colourper la dogità stessa: è un concetto astratto. Ci sono attributi che hanno senso nelle classi. Ad esempio, population_sizeè uno di questi - non ha senso contare il Fido perché Fido è sempre uno. Ha senso contare i cani. Diciamo che ci sono 200 milioni di cani nel mondo. È di proprietà della classe Dog. Fido non ha nulla a che fare con il numero 200 milioni, né Spot. Si chiama "attributo di classe", al contrario di "attributi di istanza" che sono colouro legssuperiori.

Ora, a qualcosa di meno canino e più legato alla programmazione. Mentre scrivo di seguito, la classe per aggiungere cose non è sensata: di cosa è una classe? Le classi in Python sono costituite da raccolte di dati diversi, che si comportano in modo simile. La classe di cani è composta da Fido e Spot e 199999999998 altri animali simili a loro, tutti che fanno pipì sui lampioni. In cosa consiste la classe per aggiungere cose? In base a quali dati inerenti a loro differiscono? E quali azioni condividono?

Tuttavia, i numeri ... quelli sono argomenti più interessanti. Dì, numeri interi. Ce ne sono molti, molto di più dei cani. So che Python ha già degli interi, ma facciamo il finto tonto e "implementiamoli" di nuovo (barando e usando gli interi di Python).

Quindi, gli interi sono una classe. Hanno alcuni dati (valore) e alcuni comportamenti ("aggiungimi a questo altro numero"). Mostriamo questo:

class MyInteger:
    def __init__(self, newvalue)
        # imagine self as an index card.
        # under the heading of "value", we will write
        # the contents of the variable newvalue.
        self.value = newvalue
    def add(self, other):
        # when an integer wants to add itself to another integer,
        # we'll take their values and add them together,
        # then make a new integer with the result value.
        return MyInteger(self.value + other.value)

three = MyInteger(3)
# three now contains an object of class MyInteger
# three.value is now 3
five = MyInteger(5)
# five now contains an object of class MyInteger
# five.value is now 5
eight = three.add(five)
# here, we invoked the three's behaviour of adding another integer
# now, eight.value is three.value + five.value = 3 + 5 = 8
print eight.value
# ==> 8

Questo è un po 'fragile (supponiamo otherche sarà un MyInteger), ma ora lo ignoreremo. Nel codice reale, non lo faremmo; lo testeremmo per esserne sicuri, e magari lo forzeremmo ("non sei un intero? perbacco, hai 10 nanosecondi per diventarlo! 9 ... 8 ....")

Potremmo anche definire frazioni. Anche le frazioni sanno come sommarsi.

class MyFraction:
    def __init__(self, newnumerator, newdenominator)
        self.numerator = newnumerator
        self.denominator = newdenominator
        # because every fraction is described by these two things
    def add(self, other):
        newdenominator = self.denominator * other.denominator
        newnumerator = self.numerator * other.denominator + self.denominator * other.numerator
        return MyFraction(newnumerator, newdenominator)

Ci sono anche più frazioni che numeri interi (non proprio, ma i computer non lo sanno). Facciamo due:

half = MyFraction(1, 2)
third = MyFraction(1, 3)
five_sixths = half.add(third)
print five_sixths.numerator
# ==> 5
print five_sixths.denominator
# ==> 6

In realtà non stai dichiarando nulla qui. Gli attributi sono come un nuovo tipo di variabile. Le variabili normali hanno solo un valore. Diciamo che scrivi colour = "grey". Non puoi avere un'altra variabile denominata colourche sia "fuchsia"- non nella stessa posizione nel codice.

Gli array lo risolvono fino a un certo punto. Se dici colour = ["grey", "fuchsia"]di aver impilato due colori nella variabile, ma li distingui in base alla loro posizione (0 o 1, in questo caso).

Gli attributi sono variabili legate a un oggetto. Come con gli array, possiamo avere molte colourvariabili, su cani diversi . Quindi, fido.colourè una variabile, ma spot.colourè un'altra. Il primo è legato all'oggetto all'interno della variabile fido; il secondo spot,. Ora, quando chiami Dog(4, "brown"), o three.add(five), ci sarà sempre un parametro invisibile, che verrà assegnato a quello extra penzolante all'inizio dell'elenco dei parametri. Viene chiamato convenzionalmente selfe otterrà il valore dell'oggetto davanti al punto. Quindi, all'interno del Dog's __init__(costruttore), selfci sarà qualunque cosa risulterà essere il nuovo Dog; all'interno di MyInteger's add, selfsarà associato all'oggetto nella variabile three. Così,three.valuesarà la stessa variabile all'esterno di add, come self.valueall'interno di add.

Se lo dico the_mangy_one = fido, inizierò a fare riferimento all'oggetto noto come fidocon un altro nome. D'ora in poi, fido.colourè esattamente la stessa variabile di the_mangy_one.colour.

Quindi, le cose all'interno del file __init__. Puoi pensare a loro come a annotazioni nel certificato di nascita del cane. colourdi per sé è una variabile casuale, potrebbe contenere qualsiasi cosa. fido.colouro self.colourè come un campo modulo sul foglio di identità del cane; ed __init__è l'impiegato che lo compila per la prima volta.

Qualcosa di più chiaro?

EDIT : Espandendo il commento qui sotto:

Intendi una lista di oggetti , vero?

Prima di tutto, in fidorealtà non è un oggetto. È una variabile, che attualmente contiene un oggetto, proprio come quando dici x = 5, xè una variabile che attualmente contiene il numero cinque. Se in seguito cambi idea, puoi farlo fido = Cat(4, "pleasing")(a patto di aver creato una classe Cat) e fidoda quel momento in poi "conterrai" un oggetto gatto. Se lo fai fido = x, conterrà il numero cinque e non sarà affatto un oggetto animale.

Una classe di per sé non conosce le sue istanze a meno che tu non scriva specificamente codice per tenerne traccia. Per esempio:

class Cat:
    census = [] #define census array

    def __init__(self, legs, colour):
        self.colour = colour
        self.legs = legs
        Cat.census.append(self)

Ecco censusun attributo di Catclasse a livello di classe.

fluffy = Cat(4, "white")
spark = Cat(4, "fiery")
Cat.census
# ==> [<__main__.Cat instance at 0x108982cb0>, <__main__.Cat instance at 0x108982e18>]
# or something like that

Nota che non otterrai [fluffy, sparky]. Quelli sono solo nomi di variabili. Se vuoi che i gatti stessi abbiano un nome, devi creare un attributo separato per il nome e quindi sovrascrivere il __str__metodo per restituire questo nome. Lo scopo di questo metodo (cioè la funzione associata alla classe, proprio come addo __init__) è descrivere come convertire l'oggetto in una stringa, come quando lo stampi.


7
wow grazie..questo in realtà ha molto senso per me, quindi tutto ciò che rende qualcosa quello che è, devo pre-dichiararlo nella funzione init. In questo caso, il cane, ha le gambe e il colore. Ad esempio, se ho creato una classe che ha aggiunto due numeri, dichiarerei self.firstnumber e self.secondnumber quindi basta fare firstnumber + secondnumber più avanti nella classe per ottenere la risposta?
Lostsoul

1
Tipo. Potresti farlo. Ma difficilmente ha senso fare una lezione solo per aggiungere cose. Le classi normalmente implementano dati con comportamenti: i comportamenti puri sono solo funzioni. Espanderò la risposta con qualcosa di rilevante; attendere un po.
Amadan

3
Grazie per la fantastica risposta. Adesso vedo e capisco il potere delle classi. Scusa, se suona stupido. Mi rendo conto che posso ordinare i dati e mantenere lo stato di molte cose diverse contemporaneamente (mentre traccerei solo il numero di variabili che posso creare o più tramite loop). Quindi dimmi, devo calcolare il numero medio di zampe per cane? C'è un modo per recuperare un elenco di tutti gli oggetti che ho creato con una classe in modo da poter iniziare un calucation come questo? o dovrei anche mantenere un elenco delle classi che creo (ad esempio [fido, spot])
Lostsoul

23

Per contribuire con i miei 5 centesimi alla completa spiegazione di Amadan .

Dove le classi sono una descrizione "di un tipo" in modo astratto. Gli oggetti sono le loro realizzazioni: la cosa vivente che respira. Nel mondo orientato agli oggetti ci sono idee principali che puoi quasi chiamare l'essenza di ogni cosa. Loro sono:

  1. incapsulamento (non approfondirò questo)
  2. eredità
  3. polimorfismo

Gli oggetti hanno una o più caratteristiche (= Attributi) e comportamenti (= Metodi). Il comportamento dipende principalmente dalle caratteristiche. Le classi definiscono ciò che il comportamento dovrebbe realizzare in modo generale, ma finché la classe non è realizzata (istanziata) come un oggetto, rimane un concetto astratto di una possibilità. Permettetemi di illustrare con l'aiuto di "eredità" e "polimorfismo".

    class Human:
        gender
        nationality
        favorite_drink
        core_characteristic
        favorite_beverage
        name
        age

        def love    
        def drink
        def laugh
        def do_your_special_thing                

    class Americans(Humans)
        def drink(beverage):
            if beverage != favorite_drink: print "You call that a drink?"
            else: print "Great!" 

    class French(Humans)
        def drink(beverage, cheese):
            if beverage == favourite_drink and cheese == None: print "No cheese?" 
            elif beverage != favourite_drink and cheese == None: print "Révolution!"

    class Brazilian(Humans)
        def do_your_special_thing
            win_every_football_world_cup()

    class Germans(Humans)
        def drink(beverage):
            if favorite_drink != beverage: print "I need more beer"
            else: print "Lecker!" 

    class HighSchoolStudent(Americans):
        def __init__(self, name, age):
             self.name = name
             self.age = age

jeff = HighSchoolStudent(name, age):
hans = Germans()
ronaldo = Brazilian()
amelie = French()

for friends in [jeff, hans, ronaldo]:
    friends.laugh()
    friends.drink("cola")
    friends.do_your_special_thing()

print amelie.love(jeff)
>>> True
print ronaldo.love(hans)
>>> False

Alcune caratteristiche definiscono gli esseri umani. Ma ogni nazionalità è leggermente diversa. Quindi i "tipi nazionali" sono una specie di Umani con extra. Gli "americani" sono un tipo di "umani" ed ereditano alcune caratteristiche e comportamenti astratti dal tipo umano (classe base): questa è l'eredità. Quindi tutti gli Umani possono ridere e bere, quindi anche tutte le classi di bambini possono! Eredità (2).

Ma poiché sono tutti dello stesso tipo (Tipo / classe base: Umani) a volte puoi scambiarli: vedi il ciclo for alla fine. Ma esporranno una caratteristica individuale, e questo è il polimorfismo (3).

Quindi ogni essere umano ha una bevanda preferita, ma ogni nazionalità tende verso un tipo speciale di bevanda. Se sottoclassi una nazionalità dal tipo di Umani puoi sovrascrivere il comportamento ereditato come ho dimostrato sopra con il drink()Metodo. Ma questo è ancora a livello di classe e per questo motivo è ancora una generalizzazione.

hans = German(favorite_drink = "Cola")

istanzia la classe tedesco e all'inizio ho "cambiato" una caratteristica predefinita. (Ma se chiami hans.drink ('Milk') stamperebbe comunque "Ho bisogno di più birra" - un bug ovvio ... o forse è quello che chiamerei una funzionalità se fossi un dipendente di un'azienda più grande. ;-)! )

Le caratteristiche di un tipo, ad esempio tedeschi (hans), sono solitamente definite tramite il costruttore (in python :) __init__al momento dell'istanziazione. Questo è il punto in cui definisci una classe perché diventi un oggetto. Si potrebbe dire dare vita a un concetto astratto (classe) riempiendolo di caratteristiche individuali e diventando un oggetto.

Ma poiché ogni oggetto è un'istanza di una classe, condividono tutti alcuni tipi di caratteristiche di base e alcuni comportamenti. Questo è un grande vantaggio del concetto orientato agli oggetti.

Per proteggere le caratteristiche di ogni oggetto, le incapsulate significa che provate ad accoppiare comportamento e caratteristica e rendere difficile manipolarlo dall'esterno dell'oggetto. Questo è l'incapsulamento (1)


5

Serve solo per inizializzare le variabili dell'istanza.

Ad esempio, crea crawlerun'istanza con un nome di database specifico (dal tuo esempio sopra).


Mi dispiace, non capisco davvero cosa significhi ... nell'esempio sopra ... lo sviluppatore non avrebbe potuto semplicemente aggiungere nel suo codice principale 'left = foo', ecc.
Lostsoul

Intendi i valori di default della funzione? left=Noneleft verrà inizializzato a Nonese al momento della creazione il leftparametro non è specificato.
jldupont

Penso che stia iniziando ad avere un senso..è come come devi dichiarare le tue variabili in java "String left" o qualcosa del genere? quindi una volta inizializzato nella classe, puoi manipolare i valori? È solo un po 'confuso rispetto alle funzioni perché posso semplicemente inviare valori alle funzioni e non è necessario inizializzare nulla in anticipo.
Lostsoul

1
@Lostsoul: left = foofunzionerebbe - una volta. Lo scopo delle lezioni è fare qualcosa di sensato per ogni diverso crawler. Le classi non sono funzioni, né qualcosa che può essere paragonato a funzioni (beh, non finché non sarai molto più avanzato e non entrerai nella programmazione funzionale, ma questo ti confonderà ora). Leggi la mia risposta per sapere quali sono in realtà le classi: non l'hai ancora capito.
Amadan

4

Sembra che tu debba usare __init__in Python se vuoi inizializzare correttamente gli attributi modificabili delle tue istanze.

Vedi il seguente esempio:

>>> class EvilTest(object):
...     attr = []
... 
>>> evil_test1 = EvilTest()
>>> evil_test2 = EvilTest()
>>> evil_test1.attr.append('strange')
>>> 
>>> print "This is evil:", evil_test1.attr, evil_test2.attr
This is evil: ['strange'] ['strange']
>>> 
>>> 
>>> class GoodTest(object):
...     def __init__(self):
...         self.attr = []
... 
>>> good_test1 = GoodTest()
>>> good_test2 = GoodTest()
>>> good_test1.attr.append('strange')
>>> 
>>> print "This is good:", good_test1.attr, good_test2.attr
This is good: ['strange'] []

Questo è abbastanza diverso in Java dove ogni attributo viene inizializzato automaticamente con un nuovo valore:

import java.util.ArrayList;
import java.lang.String;

class SimpleTest
{
    public ArrayList<String> attr = new ArrayList<String>();
}

class Main
{
    public static void main(String [] args)
    {
        SimpleTest t1 = new SimpleTest();
        SimpleTest t2 = new SimpleTest();

        t1.attr.add("strange");

        System.out.println(t1.attr + " " + t2.attr);
    }
}

produce un output che ci aspettiamo intuitivamente:

[strange] []

Ma se dichiari attrcome static, agirà come Python:

[strange] [strange]

3

Seguendo l' esempio della tua auto : quando prendi un'auto, semplicemente non ottieni un'auto a caso, voglio dire, scegli il colore, la marca, il numero di posti, ecc. E alcune cose vengono anche "inizializzate" senza che tu scelga per questo, come il numero di ruote o il numero di registrazione.

class Car:
    def __init__(self, color, brand, number_of_seats):
        self.color = color
        self.brand = brand
        self.number_of_seats = number_of_seats
        self.number_of_wheels = 4
        self.registration_number = GenerateRegistrationNumber()

Quindi, nel __init__metodo definisci gli attributi dell'istanza che stai creando. Quindi, se vogliamo un'auto Renault blu, per 2 persone, dovremmo inizializzare o istanziare Carcome:

my_car = Car('blue', 'Renault', 2)

In questo modo, creiamo un'istanza della Carclasse. È __init__quello che gestisce i nostri attributi specifici (come coloro brand) e genera gli altri attributi, come registration_number.


3

Le classi sono oggetti con attributi (stato, caratteristica) e metodi (funzioni, capacità) specifici per quell'oggetto (come il colore bianco e le potenze di volo, rispettivamente, per un'anatra).

Quando crei un'istanza di una classe, puoi darle una personalità iniziale (stato o carattere come il nome e il colore del suo vestito per un neonato). Lo fai con __init__.

Fondamentalmente __init__imposta automaticamente le caratteristiche dell'istanza quando chiami instance = MyClass(some_individual_traits).


2

La __init__funzione sta impostando tutte le variabili membro nella classe. Quindi una volta creato il tuo bicluster puoi accedere al membro e ottenere un valore indietro:

mycluster = bicluster(...actual values go here...)
mycluster.left # returns the value passed in as 'left'

Controlla i documenti di Python per alcune informazioni. Ti consigliamo di prendere un libro sui concetti OO per continuare a imparare.


1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

    def __init__(self,breed,name):
        self.breed = breed
        self.name = name

Nell'esempio sopra usiamo la specie come globale poiché sarà sempre la stessa (tipo di costante che puoi dire). quando chiami il __init__metodo, __init__verranno avviate tutte le variabili all'interno (ad esempio: razza, nome).

class Dog(object):
    a = '12'

    def __init__(self,breed,name,a):
        self.breed = breed
        self.name = name
        self.a= a

se si stampa l'esempio sopra chiamando sotto in questo modo

Dog.a
12

Dog('Lab','Sam','10')
Dog.a
10

Ciò significa che verrà inizializzato solo durante la creazione dell'oggetto. quindi tutto ciò che vuoi dichiarare come costante rendilo globale e tutto ciò che cambia lo usa __init__

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.