Dlaczego używamy __init__ w klasach Pythona?


124

Mam problem ze zrozumieniem inicjalizacji klas.

Jaki jest ich sens i skąd wiemy, co w nich zawrzeć? Czy pisanie na zajęciach wymaga innego sposobu myślenia niż tworzenie funkcji (pomyślałem, że mogę po prostu tworzyć funkcje, a następnie po prostu opakować je w klasę, aby móc ich ponownie użyć. Czy to zadziała?)

Oto przykład:

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

Lub inny przykład kodu:

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

Jest tak wiele zajęć, z __init__którymi się spotykam, próbując czytać kod innych osób, ale nie rozumiem logiki ich tworzenia.


1
historia init jest ... bla, bla, bla ... konstruktor-destruktor, ale nie ma destruktora, ponieważ jest dostępne usuwanie śmieci.
MisterGeeky,

Odpowiedzi:


289

W tym, co napisałeś, brakuje ci krytycznego zrozumienia: różnicy między klasą a przedmiotem. __init__nie inicjalizuje klasy, inicjuje wystąpienie klasy lub obiektu. Każdy pies ma kolor, ale psy jako klasa nie. Każdy pies ma cztery lub mniej łap, ale klasa psów nie. Klasa to pojęcie obiektu. Kiedy widzisz Fido i Spot, rozpoznajesz ich podobieństwo, ich psią budę. To jest klasa.

Kiedy powiesz

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

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

Mówisz, że Fido to brązowy pies z 4 nogami, podczas gdy Spot jest trochę kaleką i jest przeważnie żółty. __init__Wywoływana jest funkcja konstruktora lub inicjator i jest automatycznie wywoływana podczas tworzenia nowej instancji klasy. W ramach tej funkcji nowo utworzony obiekt jest przypisywany do parametru self. Notacja self.legsto wywoływany atrybut legsobiektu w zmiennej self. Atrybuty są czymś w rodzaju zmiennych, ale opisują stan obiektu lub określone akcje (funkcje) dostępne dla obiektu.

Zauważ jednak, że nie jesteś nastawiony colourna samą psiarnię - to abstrakcyjna koncepcja. Istnieją atrybuty, które mają sens w klasach. Na przykład population_sizejest jednym z nich - nie ma sensu liczyć Fido, ponieważ Fido zawsze jest jednym. Liczenie psów ma sens. Powiedzmy, że na świecie jest 200 milionów psów. To własność klasy Dog. Fido nie ma nic wspólnego z liczbą 200 milionów, podobnie jak Spot. Nazywa się to „atrybutem klasy”, w przeciwieństwie do „atrybutów instancji”, które są równe colourlub legswyższe.

A teraz coś mniej psiego, a bardziej związanego z programowaniem. Jak piszę poniżej, klasa dodawania rzeczy nie ma sensu - co to za klasa? Klasy w Pythonie składają się ze zbiorów różnych danych, które zachowują się podobnie. Klasa psów składa się z Fido i Spot oraz 199999999998 innych podobnych do nich zwierząt, wszystkie sikają na latarniach. Z czego składa się klasa dodawania rzeczy? Jakimi danymi się różnią? A jakie akcje mają wspólne?

Jednak liczby… to ciekawsze tematy. Powiedz, liczby całkowite. Jest ich dużo, dużo więcej niż psy. Wiem, że Python ma już liczby całkowite, ale zagrajmy w głupków i „zaimplementujmy” je ponownie (oszukując i używając liczb całkowitych Pythona).

Tak więc liczby całkowite są klasą. Mają pewne dane (wartość) i pewne zachowania („dodaj mnie do tego innego numeru”). Pokażmy to:

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

Jest to trochę kruche (zakładamy, że otherbędzie to MyInteger), ale teraz zignorujemy. W prawdziwym kodzie nie; przetestowalibyśmy to, aby się upewnić, a może nawet wymusilibyśmy to („nie jesteś liczbą całkowitą? przez golly, masz 10 nanosekund, aby stać się jednością! 9 ... 8 ....”)

Moglibyśmy nawet zdefiniować ułamki. Frakcje również wiedzą, jak się dodawać.

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)

Jest jeszcze więcej ułamków niż liczb całkowitych (niezupełnie, ale komputery o tym nie wiedzą). Zróbmy dwa:

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

W rzeczywistości niczego tu nie deklarujesz. Atrybuty są jak nowy rodzaj zmiennych. Zmienne normalne mają tylko jedną wartość. Powiedzmy, że piszesz colour = "grey". Nie możesz mieć innej zmiennej o colourtakiej nazwie"fuchsia" - nie w tym samym miejscu w kodzie.

Tablice do pewnego stopnia to rozwiązują. Jeśli powiesz colour = ["grey", "fuchsia"], że umieściłeś dwa kolory w zmiennej, ale rozróżniasz je na podstawie ich pozycji (w tym przypadku 0 lub 1).

Atrybuty to zmienne powiązane z obiektem. Podobnie jak w przypadku tablic, możemy mieć wiele colourzmiennych dla różnych psów . Tak więc fido.colourjest jedną zmienną, ale spot.colourjest inną. Pierwsza jest związana z obiektem w zmiennej fido; drugi, spot. Teraz, gdy wywołasz Dog(4, "brown")lub three.add(five)zawsze będzie niewidoczny parametr, który zostanie przypisany do wiszącego dodatkowego parametru na początku listy parametrów. Jest konwencjonalnie nazywany selfi pobiera wartość obiektu przed kropką. Tak więc w Dog __init__(konstruktor) selfbędzie to, czym okaże się nowy Dog; wewnątrz MyInteger's add, selfzostanie powiązany z obiektem w zmiennej three. A zatem,three.valuebędzie ta sama zmienna poza add, jak self.valuew add.

Jeśli powiem the_mangy_one = fido, zacznę odwoływać się do przedmiotu znanego pod fidojeszcze inną nazwą. Od teraz fido.colourjest dokładnie tą samą zmienną, co the_mangy_one.colour.

Więc rzeczy wewnątrz __init__. Możesz myśleć o nich jako o zapisywaniu rzeczy w akcie urodzenia Psa. coloursama w sobie jest zmienną losową, może zawierać wszystko. fido.colourlub self.colourjest jak pole formularza na karcie identyfikacyjnej Psa; i __init__czy urzędnik wypełnia go po raz pierwszy.

Jakieś jaśniejsze?

EDYCJA : Rozwinięcie komentarza poniżej:

Masz na myśli listę obiektów , prawda?

Przede wszystkim fidowłaściwie nie jest przedmiotem. Jest to zmienna, która aktualnie zawiera obiekt, tak jak mówisz x = 5, xjest zmienną zawierającą obecnie liczbę pięć. Jeśli później zmienisz zdanie, możesz to zrobić fido = Cat(4, "pleasing")(o ile utworzyłeś klasę Cat) i fidoodtąd „zawiera” obiekt kota. Jeśli to zrobisz fido = x, będzie zawierał liczbę pięć, a nie obiekt zwierzęcy.

Klasa sama w sobie nie zna swoich instancji, chyba że specjalnie napiszesz kod, aby je śledzić. Na przykład:

class Cat:
    census = [] #define census array

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

Tutaj censusjest atrybut Catklasy na poziomie klasy.

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

Pamiętaj, że nie dostaniesz [fluffy, sparky]. To tylko nazwy zmiennych. Jeśli chcesz, aby koty same miały imiona, musisz utworzyć osobny atrybut dla nazwy, a następnie zastąpić __str__metodę zwracania tej nazwy. Celem tej metody (tj. Funkcji związanej z klasą, tak jak addlub __init__) jest opisanie, jak przekształcić obiekt w łańcuch, na przykład podczas drukowania.


7
wow, dzięki… to faktycznie miało dla mnie dużo sensu, więc wszystko, co tworzy coś takiego, czym jest, muszę wstępnie zadeklarować w funkcji init. W tym przypadku Pies ma nogi i maść. Na przykład, jeśli utworzyłbym klasę, która dodałaby dwie liczby, zadeklarowałbym self.firstnumber i self.secondnumber, a następnie po prostu wykonałbym pierwszą liczbę + drugą liczbę później w klasie, aby uzyskać odpowiedź?
Lostsoul,

1
Rodzaj. Mógłbyś to zrobić. Ale tworzenie klasy tylko po to, by coś dodać, nie ma sensu. Klasy zwykle implementują dane za pomocą zachowań - czyste zachowania to tylko funkcje. Rozszerzę odpowiedź o coś istotnego; Poczekaj chwilę.
Amadan

3
Dziękuję za niesamowitą odpowiedź. Teraz widzę i rozumiem siłę zajęć. Przepraszam, jeśli to brzmi głupio. Po prostu zdajesz sobie sprawę, że mogę sortować dane i utrzymywać stan wielu różnych rzeczy naraz (podczas gdy śledziłbym tylko tyle zmiennych, ile mogę utworzyć, lub więcej za pomocą pętli). Powiedzmy, że muszę obliczyć średnią liczbę nóg na psa? Czy istnieje sposób na pobranie listy wszystkich obiektów, które utworzyłem za pomocą klasy, aby móc rozpocząć takie obliczenia? czy też powinienem prowadzić listę klas, które tworzę (np. [fido, spot])
Lostsoul

23

Aby przekazać moje 5 centów na dokładne wyjaśnienie od Amadana .

Gdzie klasy są opisem „typu” w sposób abstrakcyjny. Obiekty są ich urzeczywistnieniem: żyjąca, oddychająca rzecz. W świecie zorientowanym na przedmioty istnieją podstawowe idee, które można niemal nazwać esencją wszystkiego. Oni są:

  1. hermetyzacja (nie będę się nad tym rozwodzić)
  2. dziedzictwo
  3. wielopostaciowość

Obiekty mają jedną lub więcej cech (= Atrybuty) i zachowań (= Metody). Zachowanie zależy głównie od cech. Klasy w sposób ogólny definiują, co powinno osiągnąć zachowanie, ale dopóki klasa nie jest realizowana (tworzona) jako obiekt, pozostaje abstrakcyjną koncepcją możliwości. Pozwólcie, że zilustruję za pomocą „dziedziczenia” i „polimorfizmu”.

    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

Niektóre cechy definiują ludzi. Ale każda narodowość jest nieco inna. Więc „typy narodowe” to ludzie z dodatkami. „Amerykanie” są typem „ludzi” i dziedziczą pewne abstrakcyjne cechy i zachowania po typie ludzkim (klasa podstawowa): to jest dziedziczenie. Tak więc wszyscy ludzie mogą się śmiać i pić, dlatego wszystkie klasy dziecięce też mogą! Dziedziczenie (2).

Ale ponieważ wszystkie są tego samego rodzaju (Type / base-class: Humans), możesz je czasami wymienić: zobacz pętlę for na końcu. Ale ujawnią indywidualną charakterystykę i to jest polimorfizm (3).

Zatem każdy człowiek ma swój ulubiony napój, ale każda narodowość skłania się ku specjalnemu rodzajowi napoju. Jeśli podklasujesz narodowość z typu Ludzi, możesz nadpisać odziedziczone zachowanie, jak pokazałem powyżej za pomocą drink()Metody. Ale to wciąż na poziomie klasowym i dlatego jest to uogólnienie.

hans = German(favorite_drink = "Cola")

tworzy instancję klasy German i „zmieniłem” domyślną charakterystykę na początku. (Ale jeśli zadzwonisz do hans.drink ('Milk'), to i tak wypisze „Potrzebuję więcej piwa” - oczywisty błąd ... a może tak nazwałbym funkcję, gdybym był pracownikiem większej firmy. ;-)! )

Charakterystyka typu np. Germans (hans) jest zwykle definiowana przez konstruktora (w pythonie:) __init__w momencie instancji. To jest punkt, w którym definiujesz klasę, która ma stać się obiektem. Można powiedzieć, że tchnij życie w abstrakcyjną koncepcję (klasę), wypełniając ją indywidualnymi cechami i stając się przedmiotem.

Ale ponieważ każdy obiekt jest instancją klasy, mają one wspólne wszystkie podstawowe typy cech i pewne zachowanie. Jest to główna zaleta koncepcji zorientowanej obiektowo.

Aby chronić cechy każdego obiektu, hermetyzujesz je - oznacza to, że próbujesz połączyć zachowanie i charakterystykę i utrudnić manipulowanie nimi z zewnątrz obiektu. To jest kapsułkowanie (1)


5

Służy tylko do zainicjowania zmiennych instancji.

Np. Utwórz crawlerinstancję o określonej nazwie bazy danych (z powyższego przykładu).


Przepraszam, nie bardzo rozumiem, co to znaczy… w powyższym przykładzie… czy deweloper nie mógł po prostu dodać w swoim głównym kodzie „left = foo” itd.
Lostsoul

Masz na myśli domyślne wartości funkcji? left=Noneleft zostanie zainicjowane, Nonejeśli podczas tworzenia leftparametr nie jest określony.
jldupont

Myślę, że to zaczyna mieć sens… czy to tak, jak musisz wstępnie zadeklarować swoje zmienne w java „Pozostały ciąg znaków” czy coś? następnie po zainicjowaniu jej w klasie możesz manipulować wartościami? Jest to trochę mylące w porównaniu z funkcjami, ponieważ mogę po prostu wysyłać wartości do funkcji i nie muszę niczego inicjować z wyprzedzeniem.
Lostsoul,

1
@Lostsoul: left = foozadziała - raz. Celem zajęć jest zrobienie czegoś sensownego dla każdego innego crawler. Klasy nie są funkcjami ani czymś, co można porównać do funkcji (cóż, nie dopóki nie jesteś dużo bardziej zaawansowany i nie zajmiesz się programowaniem funkcjonalnym, ale to cię teraz zmyli). Przeczytaj moją odpowiedź, czym właściwie są zajęcia - nadal nie rozumiesz.
Amadan,

4

Wygląda na to, że musisz użyć __init__w Pythonie, jeśli chcesz poprawnie zainicjować zmienne atrybuty swoich instancji.

Zobacz poniższy przykład:

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

Zupełnie inaczej jest w Javie, gdzie każdy atrybut jest automatycznie inicjowany z nową wartością:

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

tworzy wynik, którego intuicyjnie oczekujemy:

[strange] []

Ale jeśli zadeklarujesz attrjako static, będzie działać jak Python:

[strange] [strange]

3

Idąc za przykładem twojego samochodu : kiedy kupujesz samochód, po prostu nie dostajesz losowego samochodu, to znaczy wybierasz kolor, markę, liczbę miejsc itp. A niektóre rzeczy są również "inicjalizowane" bez twojego wyboru na przykład liczba kół lub numer rejestracyjny.

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

Tak więc w __init__metodzie definiujesz atrybuty tworzonej instancji. Tak więc, jeśli chcemy mieć niebieski samochód Renault, dla 2 osób, zainicjowalibyśmy lub wystąpił taki przykład Car:

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

W ten sposób tworzymy instancję Carklasy. To __init__ten, który obsługuje nasze określone atrybuty (takie jak colorlub brand) i generuje inne atrybuty, takie jak registration_number.


3

Klasy to obiekty, których atrybuty (stan, charakterystyka) i metody (funkcje, pojemności) są specyficzne dla tego obiektu (jak odpowiednio biały kolor i moc muchy dla kaczki).

Tworząc instancję klasy, możesz nadać jej początkową osobowość (stan lub postać, np. Nazwę i kolor jej sukienki dla noworodka). Robisz to z __init__.

Zasadniczo __init__ustawia właściwości instancji automatycznie podczas wywołania instance = MyClass(some_individual_traits).


2

__init__Funkcja konfigurowania wszystkie zmienne w klasie. Po utworzeniu biclusterka możesz uzyskać dostęp do członka i odzyskać wartość:

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

Sprawdź dokumentację Pythona, aby uzyskać więcej informacji. Będziesz chciał przeczytać książkę o koncepcjach OO, aby kontynuować naukę.


1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

W powyższym przykładzie używamy gatunku jako globalnego, ponieważ będzie on zawsze taki sam (można powiedzieć, że jest to rodzaj stałej). kiedy wywołasz __init__metodę, wszystkie zmienne wewnątrz __init__zostaną zainicjowane (np .: rasa, imię).

class Dog(object):
    a = '12'

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

jeśli wydrukujesz powyższy przykład, dzwoniąc poniżej w ten sposób

Dog.a
12

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

Oznacza to, że zostanie zainicjowany tylko podczas tworzenia obiektu. więc wszystko, co chcesz zadeklarować jako stałe, staje się globalne i wszystko, czego używają zmiany __init__

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.