Warum verwenden wir __init__ in Python-Klassen?

124

Ich habe Probleme, die Initialisierung von Klassen zu verstehen.

Was ist der Sinn von ihnen und woher wissen wir, was wir in sie aufnehmen sollen? Erfordert das Schreiben in Klassen eine andere Art des Denkens als das Erstellen von Funktionen (ich dachte, ich könnte einfach Funktionen erstellen und sie dann einfach in eine Klasse einschließen, damit ich sie wiederverwenden kann. Funktioniert das?)

Hier ist ein Beispiel:

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()

Oder ein anderes Codebeispiel:

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

Es gibt so viele Klassen, auf die __init__ich stoße, wenn ich versuche, den Code anderer Leute zu lesen, aber ich verstehe die Logik beim Erstellen nicht.

Verlorene Seele
quelle
1
Die Geschichte von Init ist ... bla, bla, bla ... Konstruktor-Destruktor, aber kein Destruktor, weil Müllsammlung verfügbar ist.
MisterGeeky

Antworten:

289

Nach dem, was Sie geschrieben haben, fehlt Ihnen ein kritisches Stück Verständnis: der Unterschied zwischen einer Klasse und einem Objekt. __init__initialisiert keine Klasse, sondern eine Instanz einer Klasse oder eines Objekts. Jeder Hund hat Farbe, Hunde als Klasse jedoch nicht. Jeder Hund hat vier oder weniger Füße, die Klasse der Hunde jedoch nicht. Die Klasse ist ein Konzept eines Objekts. Wenn Sie Fido und Spot sehen, erkennen Sie ihre Ähnlichkeit, ihre Doghood. Das ist die Klasse.

Wenn du sagst

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

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

Du sagst, Fido ist ein brauner Hund mit 4 Beinen, während Spot ein bisschen verkrüppelt und meistens gelb ist. Die __init__Funktion wird als Konstruktor oder Initialisierer bezeichnet und automatisch aufgerufen, wenn Sie eine neue Instanz einer Klasse erstellen. Innerhalb dieser Funktion wird das neu erstellte Objekt dem Parameter zugewiesen self. Die Notation self.legsist ein Attribut, das legsvom Objekt in der Variablen aufgerufen wird self. Attribute ähneln Variablen, beschreiben jedoch den Status eines Objekts oder bestimmte Aktionen (Funktionen), die dem Objekt zur Verfügung stehen.

colourBeachten Sie jedoch, dass Sie sich nicht auf die Doghood selbst einstellen - es ist ein abstraktes Konzept. Es gibt Attribute, die für Klassen sinnvoll sind. Ist zum Beispiel population_sizeeine solche - es macht keinen Sinn, den Fido zu zählen, weil Fido immer einer ist. Es ist sinnvoll, Hunde zu zählen. Nehmen wir an, es gibt 200 Millionen Hunde auf der Welt. Es ist Eigentum der Hundeklasse. Fido hat nichts mit der Zahl von 200 Millionen zu tun, Spot auch nicht. Es wird als "Klassenattribut" bezeichnet, im Gegensatz zu "Instanzattributen", die colouroder legshöher sind.

Nun zu etwas weniger Eckzahnigem und mehr Programmierbezogenem. Wie ich weiter unten schreibe, ist eine Klasse zum Hinzufügen von Dingen nicht sinnvoll - wovon ist eine Klasse? Klassen in Python bestehen aus Sammlungen verschiedener Daten, die sich ähnlich verhalten. Die Klasse der Hunde besteht aus Fido und Spot und 199999999998 ähnlichen Tieren, die alle auf Laternenpfählen pinkeln. Woraus besteht die Klasse zum Hinzufügen von Dingen? Durch welche ihnen innewohnenden Daten unterscheiden sie sich? Und welche Aktionen teilen sie?

Zahlen ... das sind jedoch interessantere Themen. Sagen wir, Ganzzahlen. Es gibt viele von ihnen, viel mehr als Hunde. Ich weiß, dass Python bereits Ganzzahlen hat, aber lasst uns dumm spielen und sie erneut "implementieren" (indem wir betrügen und Pythons Ganzzahlen verwenden).

Ganzzahlen sind also eine Klasse. Sie haben einige Daten (Wert) und einige Verhaltensweisen ("füge mich zu dieser anderen Zahl hinzu"). Lassen Sie uns dies zeigen:

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

Dies ist etwas fragil (wir gehen davon aus, dass otheres sich um eine MyInteger handelt), aber wir werden sie jetzt ignorieren. In echtem Code würden wir nicht; Wir würden es testen, um sicherzugehen und es vielleicht sogar zu erzwingen ("Sie sind keine ganze Zahl? Sie haben 10 Nanosekunden Zeit, um eins zu werden! 9 ... 8 ...")

Wir könnten sogar Brüche definieren. Brüche wissen auch, wie sie sich selbst hinzufügen können.

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)

Es gibt sogar mehr Brüche als ganze Zahlen (nicht wirklich, aber Computer wissen das nicht). Machen wir zwei:

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

Sie erklären hier eigentlich nichts. Attribute sind wie eine neue Art von Variablen. Normale Variablen haben nur einen Wert. Sagen wir, Sie schreiben colour = "grey". Sie können nicht eine andere Variable mit dem Namen haben , colourdas ist "fuchsia"- nicht an der gleichen Stelle im Code.

Arrays lösen das bis zu einem gewissen Grad. Wenn Sie sagen colour = ["grey", "fuchsia"], Sie haben zwei Farben in die Variable gestapelt, aber Sie unterscheiden sie durch ihre Position (in diesem Fall 0 oder 1).

Attribute sind Variablen, die an ein Objekt gebunden sind. Wie bei Arrays können wir bei verschiedenen Hunden viele colourVariablen haben . Ist also eine Variable, aber eine andere. Der erste ist an das Objekt innerhalb der Variablen gebunden ; der zweite , . Wenn Sie jetzt oder aufrufen , wird immer ein unsichtbarer Parameter angezeigt, der dem baumelnden zusätzlichen Parameter am Anfang der Parameterliste zugewiesen wird. Es wird herkömmlicherweise aufgerufen und erhält den Wert des Objekts vor dem Punkt. So wird innerhalb des Hundes (Konstrukteurs) sein, was auch immer der neue Hund sein wird; innerhalb ‚s , wird auf das Objekt in den Variablen gebunden werden . So,fido.colourspot.colourfidospotDog(4, "brown")three.add(five)self__init__selfMyIntegeraddselfthreethree.valuewird die gleiche Variable außerhalb der add, wie self.valueinnerhalb der add.

Wenn ich sage the_mangy_one = fido, beziehe ich mich auf das Objekt fidomit einem anderen Namen. Von nun an fido.colourist genau die gleiche Variable wie the_mangy_one.colour.

Also, die Dinge in der __init__. Sie können sich vorstellen, dass sie Dinge in der Geburtsurkunde des Hundes vermerken. colouran sich ist eine zufällige Variable, könnte alles enthalten. fido.colouroder self.colourist wie ein Formularfeld auf dem Ausweis des Hundes; und __init__füllt der Angestellte es zum ersten Mal aus.

Noch klarer?

BEARBEITEN : Erweitern Sie den Kommentar unten:

Du meinst eine Liste von Objekten , nicht wahr?

Erstens fidoist eigentlich kein Objekt. Es ist eine Variable, die derzeit ein Objekt enthält, genau wie wenn Sie sagen x = 5, xeine Variable, die derzeit die Nummer fünf enthält. Wenn Sie später Ihre Meinung ändern, können Sie dies tun fido = Cat(4, "pleasing")(solange Sie eine Klasse erstellt haben Cat) und fidowürden fortan ein Katzenobjekt "enthalten". Wenn Sie dies tun fido = x, enthält es die Nummer fünf und überhaupt kein Tierobjekt.

Eine Klasse selbst kennt ihre Instanzen nur, wenn Sie speziell Code schreiben, um sie zu verfolgen. Zum Beispiel:

class Cat:
    census = [] #define census array

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

Hier censusist ein Attribut der CatKlasse auf Klassenebene.

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

Beachten Sie, dass Sie nicht bekommen [fluffy, sparky]. Das sind nur Variablennamen. Wenn Sie möchten, dass Katzen selbst Namen haben, müssen Sie ein separates Attribut für den Namen erstellen und dann die __str__Methode überschreiben , um diesen Namen zurückzugeben. Der Zweck dieser Methode (dh klassengebundene Funktion, genau wie addoder __init__) besteht darin, zu beschreiben, wie das Objekt in eine Zeichenfolge konvertiert wird, beispielsweise beim Ausdrucken.

Amadan
quelle
7
wow danke ... das hat mir tatsächlich sehr viel Sinn gemacht, also muss ich alles, was etwas zu dem macht, was es ist, in der Init-Funktion vordeklarieren. In diesem Fall hat Hund Beine und Farbe. Wenn ich zum Beispiel eine Klasse erstellen würde, die zwei Zahlen hinzufügt, würde ich self.firstnumber und self.secondnumber deklarieren und dann später in der Klasse einfach firstnumber + secondnumber ausführen, um die Antwort zu erhalten?
Lostsoul
1
So'ne Art. Das könntest du machen. Aber es macht kaum Sinn, eine Klasse zu bilden, nur um Dinge hinzuzufügen. Klassen implementieren normalerweise Daten mit Verhalten - reines Verhalten ist nur eine Funktion. Ich werde die Antwort mit etwas Relevantem erweitern. warten Sie ein wenig.
Amadan
3
Vielen Dank für die tolle Antwort. Ich sehe und verstehe jetzt die Macht des Unterrichts. Entschuldigung, wenn es dumm klingt. Sie erkennen nur, dass ich Daten sortieren und den Status vieler verschiedener Dinge gleichzeitig beibehalten kann (während ich nur so viele Variablen verfolgen würde, wie ich oder mehr über Schleifen erstellen kann). Sagen wir also, ich muss die durchschnittliche Anzahl der Beine pro Hund herausfinden? Gibt es eine Möglichkeit, eine Liste aller Objekte abzurufen, die ich mit einer Klasse erstellt habe, damit ich eine solche Berechnung starten kann? oder sollte ich auch eine Liste der Klassen führen, die ich erstelle (dh [fido, spot])
Lostsoul
23

Um meine 5 Cent zur gründlichen Erklärung von Amadan beizutragen .

Wobei Klassen eine abstrakte Beschreibung "eines Typs" sind. Objekte sind ihre Erkenntnisse: das lebendige Atmen. In der objektorientierten Welt gibt es Hauptideen, die man fast als die Essenz von allem bezeichnen kann. Sie sind:

  1. Kapselung (wird hier nicht näher erläutert)
  2. Erbe
  3. Polymorphismus

Objekte haben eine oder mehrere Eigenschaften (= Attribute) und Verhaltensweisen (= Methoden). Das Verhalten hängt hauptsächlich von den Eigenschaften ab. Klassen definieren, was das Verhalten allgemein erreichen soll, aber solange die Klasse nicht als Objekt realisiert (instanziiert) wird, bleibt sie ein abstraktes Konzept einer Möglichkeit. Lassen Sie mich mit Hilfe von "Vererbung" und "Polymorphismus" veranschaulichen.

    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

Einige Eigenschaften definieren den Menschen. Aber jede Nationalität ist etwas anders. "National-Typen" sind also Menschen mit Extras. "Amerikaner" sind eine Art von "Menschen" und erben einige abstrakte Merkmale und Verhaltensweisen vom menschlichen Typ (Basisklasse): das ist Vererbung. So können alle Menschen lachen und trinken, also auch alle Kinderklassen! Vererbung (2).

Aber weil sie alle von der gleichen Art sind (Typ / Basisklasse: Menschen), können Sie sie manchmal austauschen: siehe die for-Schleife am Ende. Aber sie werden ein individuelles Merkmal aufdecken, und das ist Polymorphismus (3).

Jeder Mensch hat also ein Lieblingsgetränk, aber jede Nationalität tendiert zu einer besonderen Art von Getränk. Wenn Sie eine Nationalität vom Typ Mensch unterordnen, können Sie das ererbte Verhalten überschreiben, wie ich oben mit der drink()Methode gezeigt habe. Aber das ist immer noch auf Klassenebene und aus diesem Grund ist es immer noch eine Verallgemeinerung.

hans = German(favorite_drink = "Cola")

instanziiert die Klasse Deutsch und ich habe am Anfang ein Standardmerkmal "geändert". (Aber wenn Sie hans.drink ('Milk') nennen, würde er immer noch "Ich brauche mehr Bier" drucken - ein offensichtlicher Fehler ... oder vielleicht würde ich das eine Funktion nennen, wenn ich ein Mitarbeiter eines größeren Unternehmens wäre. ;-)! )

Die Eigenschaften eines Typs, z. B. Deutsche (Hans), werden normalerweise __init__zum Zeitpunkt der Instanziierung durch den Konstruktor (in Python :) definiert. Dies ist der Punkt, an dem Sie eine Klasse definieren, um ein Objekt zu werden. Man könnte sagen, einem abstrakten Konzept (einer Klasse) Leben einzuhauchen, indem man es mit individuellen Merkmalen füllt und ein Objekt wird.

Da jedoch jedes Objekt eine Instanz einer Klasse ist, teilen sie alle grundlegenden Merkmalstypen und Verhaltensweisen. Dies ist ein wesentlicher Vorteil des objektorientierten Konzepts.

Um die Eigenschaften jedes Objekts zu schützen, kapseln Sie sie ein. Dies bedeutet, dass Sie versuchen, Verhalten und Eigenschaften zu koppeln und es schwierig zu machen, sie von außerhalb des Objekts zu manipulieren. Das ist Verkapselung (1)

Don Frage
quelle
5

Es dient lediglich dazu, die Variablen der Instanz zu initialisieren.

Erstellen Sie beispielsweise eine crawlerInstanz mit einem bestimmten Datenbanknamen (aus dem obigen Beispiel).

jldupont
quelle
Es tut mir leid, ich verstehe nicht wirklich, was das bedeutet. Im obigen Beispiel. Könnte der Entwickler nicht einfach in seinem Hauptcode 'left = foo' usw. hinzugefügt haben.
Lostsoul
Du meinst die Standardwerte der Funktion? left=Noneleft wird initialisiert, Nonewenn bei der Erstellung der leftParameter nicht angegeben wird.
Jldupont
Ich denke, es fängt an, Sinn zu machen. Ist es so, wie Sie Ihre Variablen in Java "String left" oder so vordeklarieren müssen? Sobald es für die Klasse initialisiert wurde, können Sie die Werte manipulieren. Es ist nur ein bisschen verwirrend im Vergleich zu Funktionen, weil ich nur Werte an Funktionen senden kann und nichts im Voraus initialisieren muss.
Lostsoul
1
@Lostsoul: left = foowürde funktionieren - einmal. Der Sinn des Unterrichts ist es, für jeden etwas Vernünftiges zu tun crawler. Klassen sind weder Funktionen noch etwas, das mit Funktionen verglichen werden kann (naja, erst wenn Sie viel fortgeschrittener sind und in die funktionale Programmierung einsteigen, aber das wird Sie jetzt nur verwirren). Lesen Sie meine Antwort, was Klassen eigentlich sind - Sie bekommen es immer noch nicht.
Amadan
4

Es scheint, als müssten Sie __init__Python verwenden, wenn Sie veränderbare Attribute Ihrer Instanzen korrekt initialisieren möchten.

Siehe folgendes Beispiel:

>>> 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'] []

Dies ist in Java ganz anders, wo jedes Attribut automatisch mit einem neuen Wert initialisiert wird:

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);
    }
}

erzeugt eine Ausgabe, die wir intuitiv erwarten:

[strange] []

Wenn Sie jedoch attrals deklarieren static, verhält es sich wie Python:

[strange] [strange]
Alexpirin
quelle
3

Folgen Sie Ihrem Auto- Beispiel: Wenn Sie ein Auto bekommen, bekommen Sie einfach kein zufälliges Auto, ich meine, Sie wählen die Farbe, die Marke, die Anzahl der Sitze usw. Und einige Dinge werden auch ohne Ihre Wahl "initialisiert" dafür, wie Anzahl der Räder oder Kennzeichen.

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()

In der __init__Methode definieren Sie also die Attribute der Instanz, die Sie erstellen. Wenn wir also ein blaues Renault-Auto für 2 Personen wollen, würden wir Folgendes initialisieren oder instanziieren Car:

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

Auf diese Weise erstellen wir eine Instanz von Car Klasse. Das __init__ist dasjenige, das unsere spezifischen Attribute (wie coloroder brand) behandelt und die anderen Attribute wie erzeugt registration_number.

juliomalegria
quelle
3

Klassen sind Objekte mit Attributen (Zustand, Charakteristik) und Methoden (Funktionen, Kapazitäten), die für dieses Objekt spezifisch sind (wie die weiße Farbe bzw. die Flugkraft für eine Ente).

Wenn Sie eine Instanz einer Klasse erstellen, können Sie ihr eine anfängliche Persönlichkeit geben (Zustand oder Charakter wie der Name und die Farbe ihres Kleides für ein Neugeborenes). Du machst das mit__init__ .

Grundsätzlich __init__werden die Instanzmerkmale beim Aufruf automatisch festgelegt instance = MyClass(some_individual_traits).

Joaquin
quelle
2

Die __init__Funktion richtet alle Mitgliedsvariablen in der Klasse ein. Sobald Ihr Bicluster erstellt ist, können Sie auf das Mitglied zugreifen und einen Wert zurückerhalten:

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

In den Python-Dokumenten finden Sie einige Informationen. Sie sollten ein Buch über OO-Konzepte lesen, um weiter zu lernen.

AlG
quelle
1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

Im obigen Beispiel verwenden wir Arten als global, da sie immer gleich sind (Art von Konstante kann man sagen). Wenn Sie die __init__Methode aufrufen , wird die gesamte darin enthaltene Variable __init__initiiert (z. B. Rasse, Name).

class Dog(object):
    a = '12'

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

Wenn Sie das obige Beispiel ausdrucken, indem Sie unten wie folgt aufrufen

Dog.a
12

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

Das heißt, es wird nur während der Objekterstellung initialisiert. Alles, was Sie als konstant deklarieren möchten, wird global und alles, was geändert wird, wird verwendet __init__

vedavyasa k
quelle