Mengapa kami menggunakan __init__ di kelas Python?

124

Saya mengalami kesulitan memahami Inisialisasi kelas.

Apa gunanya dan bagaimana kita tahu apa yang harus disertakan di dalamnya? Apakah menulis di kelas memerlukan jenis pemikiran yang berbeda versus membuat fungsi (saya pikir saya bisa membuat fungsi dan kemudian membungkusnya di kelas sehingga saya bisa menggunakannya kembali. Akankah itu berhasil?)

Berikut contohnya:

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

Atau contoh kode lain:

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

Ada begitu banyak kelas yang __init__saya temui ketika mencoba membaca kode orang lain, tetapi saya tidak memahami logika dalam membuatnya.

Kehilangan jiwa
sumber
1
cerita init adalah ... bla, bla, bla .... konstruktor-destruktor tetapi tidak ada destruktor karena pengumpulan sampah tersedia.
MisterGeeky

Jawaban:

289

Dengan apa yang Anda tulis, Anda kehilangan bagian penting dari pemahaman: perbedaan antara kelas dan objek. __init__tidak menginisialisasi kelas, itu menginisialisasi sebuah instance dari kelas atau objek. Setiap anjing memiliki warna, tetapi anjing sebagai kelas tidak. Setiap anjing memiliki empat kaki atau lebih sedikit, tetapi kelas anjing tidak. Kelas adalah konsep dari suatu objek. Saat Anda melihat Fido dan Spot, Anda mengenali kemiripan mereka, sifat anjingnya. Itu kelasnya.

Saat Anda berkata

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

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

Maksudmu, Fido adalah anjing coklat dengan 4 kaki sedangkan Spot agak cacat dan sebagian besar berwarna kuning. The __init__fungsi disebut konstruktor, atau initializer, dan secara otomatis dipanggil ketika Anda membuat contoh baru dari sebuah kelas. Di dalam fungsi itu, objek yang baru dibuat ditugaskan ke parameter self. Notasi self.legsadalah atribut yang dipanggil legsdari objek dalam variabel self. Atribut adalah sejenis variabel, tetapi mereka menggambarkan status suatu objek, atau tindakan (fungsi) tertentu yang tersedia untuk objek tersebut.

Namun, perhatikan bahwa Anda tidak menetapkan colourdoghood itu sendiri - ini adalah konsep abstrak. Ada atribut yang masuk akal di kelas. Misalnya, population_sizeadalah salah satunya - tidak masuk akal untuk menghitung Fido karena Fido selalu satu. Masuk akal untuk menghitung anjing. Katakanlah ada 200 juta anjing di dunia. Itu milik kelas Anjing. Fido tidak ada hubungannya dengan angka 200 juta, begitu juga dengan Spot. Ini disebut "atribut kelas", sebagai lawan dari "atribut contoh" yang ada colouratau di legsatasnya.

Sekarang, untuk sesuatu yang lebih sedikit anjing dan lebih terkait dengan pemrograman. Seperti yang saya tulis di bawah ini, kelas untuk menambahkan sesuatu tidak masuk akal - kelas apa itu? Kelas dalam Python terdiri dari kumpulan data berbeda, yang berperilaku serupa. Kelas anjing terdiri dari Fido dan Spot serta 199999999998 hewan sejenis lainnya, semuanya kencing di tiang lampu. Terdiri dari apakah kelas untuk menambahkan sesuatu? Berdasarkan data apa yang melekat pada mereka, mereka berbeda? Dan tindakan apa yang mereka bagi?

Namun, angka ... itu adalah topik yang lebih menarik. Katakanlah, Integers. Ada banyak dari mereka, lebih banyak dari pada anjing. Saya tahu bahwa Python sudah memiliki bilangan bulat, tapi mari kita bermain bodoh dan "menerapkan" mereka lagi (dengan menipu dan menggunakan bilangan bulat Python).

Jadi, Integer adalah sebuah kelas. Mereka memiliki beberapa data (nilai), dan beberapa perilaku ("tambahkan saya ke nomor lain ini"). Mari tunjukkan ini:

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

Ini agak rapuh (kami asumsikan otherakan menjadi MyInteger), tetapi kami akan mengabaikannya sekarang. Dalam kode nyata, kami tidak akan; kami akan mengujinya untuk memastikan, dan bahkan mungkin memaksanya ("Anda bukan bilangan bulat? Astaga, Anda memiliki 10 nanodetik untuk menjadi satu! 9 ... 8 ....")

Kita bahkan bisa mendefinisikan pecahan. Pecahan juga tahu cara menjumlahkan dirinya sendiri.

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)

Bahkan ada lebih banyak pecahan daripada bilangan bulat (tidak juga, tetapi komputer tidak tahu itu). Mari buat dua:

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

Anda sebenarnya tidak mendeklarasikan apa pun di sini. Atribut seperti variabel jenis baru. Variabel normal hanya memiliki satu nilai. Katakanlah Anda menulis colour = "grey". Anda tidak dapat memiliki variabel lain bernama colouryang "fuchsia"- tidak di tempat yang sama dalam kode.

Array memecahkannya sampai tingkat tertentu. Jika Anda mengatakan colour = ["grey", "fuchsia"], Anda telah menumpuk dua warna ke dalam variabel, tetapi Anda membedakannya dengan posisinya (0, atau 1, dalam kasus ini).

Atribut adalah variabel yang terikat pada suatu objek. Seperti halnya array, kita dapat memiliki banyak colourvariabel, pada anjing yang berbeda . Jadi, fido.colouradalah satu variabel, tetapi variabel spot.colourlainnya. Yang pertama terikat ke objek dalam variabel fido; yang kedua spot,. Sekarang, saat Anda memanggil Dog(4, "brown"), atau three.add(five), akan selalu ada parameter tak terlihat, yang akan ditetapkan ke ekstra yang menggantung di depan daftar parameter. Ini disebut secara konvensional self, dan akan mendapatkan nilai benda di depan titik. Jadi, di dalam Dog __init__(konstruktor), selfakan menjadi apa pun yang Anjing baru itu kelak; inside MyInteger's add, selfakan terikat ke objek dalam variabel three. Jadi,three.valueakan menjadi variabel yang sama di luar add, seperti self.valuedi dalam add.

Jika saya katakan the_mangy_one = fido, saya akan mulai mengacu pada objek yang dikenal fidodengan nama lain. Mulai sekarang, fido.colourvariabel yang sama persis dengan the_mangy_one.colour.

Jadi, hal-hal di dalam file __init__. Anda dapat menganggapnya sebagai catatan di dalam akta kelahiran Anjing. colourdengan sendirinya adalah variabel acak, bisa berisi apa saja. fido.colouratau self.colourseperti kolom formulir pada lembar identitas Anjing; dan __init__petugas mengisinya untuk pertama kali.

Ada yang lebih jelas?

EDIT : Memperluas komentar di bawah ini:

Maksud Anda daftar objek , bukan?

Pertama-tama, fidosebenarnya bukanlah sebuah objek. Ini adalah variabel, yang saat ini berisi objek, seperti yang Anda katakan x = 5, xadalah variabel yang saat ini berisi angka lima. Jika Anda kemudian berubah pikiran, Anda dapat melakukannya fido = Cat(4, "pleasing")(selama Anda telah membuat kelas Cat), dan fidosejak saat itu akan "berisi" objek kucing. Jika Anda melakukannya fido = x, maka itu akan berisi angka lima, dan bukan objek hewan sama sekali.

Sebuah kelas dengan sendirinya tidak mengetahui kejadiannya kecuali Anda secara khusus menulis kode untuk melacaknya. Misalnya:

class Cat:
    census = [] #define census array

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

Di sini, censusadalah atribut tingkat Catkelas dari kelas.

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

Perhatikan bahwa Anda tidak akan mendapatkan [fluffy, sparky]. Itu hanya nama variabel. Jika Anda ingin kucing itu sendiri memiliki nama, Anda harus membuat atribut terpisah untuk namanya, lalu mengganti __str__metode untuk mengembalikan nama ini. Tujuan metode ini (mis. Fungsi terikat kelas, seperti addatau __init__) adalah untuk menjelaskan cara mengonversi objek menjadi string, seperti saat Anda mencetaknya.

Amadan
sumber
7
wow terima kasih..ini sebenarnya sangat masuk akal bagi saya jadi apa pun yang membuat sesuatu seperti itu, saya perlu mendeklarasikan sebelumnya di fungsi init. Dalam hal ini, Anjing, memiliki kaki dan warna. Misalnya, jika saya membuat kelas yang menambahkan dua angka, saya akan mendeklarasikan self.firstnumber dan self.secondnumber lalu lakukan firstnumber + secondnumber nanti di kelas untuk mendapatkan jawabannya?
Lostsoul
1
Agak. Kamu bisa melakukan itu. Tetapi tidak masuk akal untuk membuat kelas hanya untuk menambahkan sesuatu. Kelas biasanya mengimplementasikan data dengan perilaku - perilaku murni hanyalah fungsi. Saya akan memperluas jawabannya dengan sesuatu yang relevan; tunggu sebentar.
Amadan
3
Terima kasih atas jawaban yang luar biasa. Saya melihat dan memahami kekuatan kelas sekarang. Maaf, jika kedengarannya bodoh. Anda baru saja saya menyadari bahwa saya dapat mengurutkan data dan mempertahankan status banyak hal yang berbeda sekaligus (sedangkan saya hanya akan melacak sebanyak mungkin variabel yang dapat saya buat atau lebih melalui loop). Jadi katakanlah, saya perlu mencari tahu jumlah rata-rata kaki per anjing? Apakah ada cara untuk mengambil daftar semua objek yang telah saya buat dengan kelas sehingga saya dapat memulai pendidikan seperti ini? atau haruskah saya juga mempertahankan daftar kelas yang saya buat (yaitu [fido, spot])
Lostsoul
23

Untuk menyumbangkan 5 sen saya untuk penjelasan menyeluruh dari Amadan .

Dimana kelas adalah deskripsi "dari suatu tipe" secara abstrak. Objek adalah realisasinya: makhluk bernapas yang hidup. Dalam dunia yang berorientasi objek, terdapat ide-ide utama yang hampir dapat Anda sebut sebagai esensi dari segalanya. Mereka:

  1. enkapsulasi (tidak akan menjelaskan lebih lanjut tentang ini)
  2. warisan
  3. polimorfisme

Objek memiliki satu, atau lebih karakteristik (= Atribut) dan perilaku (= Metode). Perilaku tersebut sebagian besar tergantung pada karakteristiknya. Kelas menentukan apa yang harus dicapai perilaku secara umum, tetapi selama kelas tidak direalisasikan (dipakai) sebagai objek, ia tetap merupakan konsep abstrak dari suatu kemungkinan. Izinkan saya mengilustrasikan dengan bantuan "warisan" dan "polimorfisme".

    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

Beberapa karakteristik mendefinisikan manusia. Tetapi setiap kebangsaan agak berbeda. Jadi "tipe-nasional" adalah semacam Manusia dengan ekstra. "Orang Amerika" adalah sejenis "Manusia" dan mewarisi beberapa karakteristik abstrak dan perilaku dari tipe manusia (kelas dasar): itu warisan. Jadi semua Manusia bisa tertawa dan minum, oleh karena itu semua kelas anak juga bisa! Warisan (2).

Tetapi karena mereka semua dari jenis yang sama (Jenis / kelas-dasar: Manusia) Anda terkadang dapat menukarnya: lihat loop-for di bagian akhir. Tetapi mereka akan mengekspos karakteristik individu, itulah Polimorfisme (3).

Jadi, setiap manusia memiliki minuman_ favorit, tetapi setiap negara cenderung memilih minuman khusus. Jika Anda membuat subkelas kebangsaan dari tipe Manusia, Anda dapat menimpa perilaku yang diwariskan seperti yang telah saya tunjukkan di atas dengan drink()Metode. Tapi itu masih di level kelas dan karena itu masih generalisasi.

hans = German(favorite_drink = "Cola")

instantiates kelas bahasa Jerman dan saya "mengubah" karakteristik default di awal. (Tetapi jika Anda memanggil hans.drink ('Milk') dia masih akan mencetak "Saya butuh lebih banyak bir" - bug yang jelas ... atau mungkin itulah yang akan saya sebut fitur jika saya akan menjadi Karyawan di Perusahaan yang lebih besar. ;-)! )

Ciri-ciri suatu tipe misalnya Jerman (hans) biasanya ditentukan melalui konstruktor (dalam python :) __init__pada saat pembuatan contoh. Ini adalah titik di mana Anda mendefinisikan kelas untuk menjadi objek. Bisa dibilang menghembuskan nafas ke dalam konsep abstrak (kelas) dengan mengisinya dengan karakteristik individu dan menjadi sebuah obyek.

Tapi karena setiap objek adalah turunan dari kelas mereka berbagi semua tipe-karakteristik dasar dan beberapa perilaku. Ini adalah keuntungan utama dari konsep berorientasi objek.

Untuk melindungi karakteristik masing-masing objek, Anda merangkumnya - berarti Anda mencoba menggabungkan perilaku dan karakteristik dan membuatnya sulit untuk memanipulasinya dari luar objek. Itu Enkapsulasi (1)

Jangan Tanya
sumber
5

Ini hanya untuk menginisialisasi variabel instance.

Misalnya, buat crawlerinstance dengan nama database tertentu (dari contoh Anda di atas).

jldupont.dll
sumber
Maaf, saya tidak begitu mengerti apa artinya..dalam contoh di atas..bukankah pengembang baru saja menambahkan kode utamanya 'left = foo', dll ..
Lostsoul
Maksud Anda nilai default dari fungsinya? left=Noneleft akan diinisialisasi Nonejika setelah pembuatan leftparameter tidak ditentukan.
jldupont
Saya pikir itu mulai masuk akal .. apakah itu seperti bagaimana Anda harus mendeklarasikan variabel Anda di java "String kiri" atau sesuatu? lalu setelah diinisialisasi ke kelas, Anda dapat memanipulasi nilai? Ini hanya sedikit membingungkan jika dibandingkan dengan fungsi karena saya hanya dapat mengirim nilai ke fungsi dan tidak perlu menginisialisasi apa pun sebelumnya.
Lostsoul
1
@Lostsoul: left = fooakan berhasil - sekali. Inti dari kelas adalah melakukan sesuatu yang masuk akal untuk setiap perbedaan crawler. Kelas bukanlah fungsi, atau sesuatu yang dapat dibandingkan dengan fungsi (yah, tidak sampai Anda jauh lebih mahir dan masuk ke dalam pemrograman fungsional, tapi itu hanya akan membingungkan Anda sekarang). Baca jawaban saya untuk kelas yang sebenarnya - Anda masih belum mengerti.
Amadan
4

Sepertinya Anda perlu menggunakan __init__Python jika Anda ingin menginisialisasi atribut yang bisa berubah dari instance Anda dengan benar.

Lihat contoh berikut:

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

Ini sangat berbeda di Java di mana setiap atribut secara otomatis diinisialisasi dengan nilai baru:

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

menghasilkan keluaran yang kita harapkan secara intuitif:

[strange] []

Tetapi jika Anda mendeklarasikan attrsebagai static, itu akan bertindak seperti Python:

[strange] [strange]
alexpirine.dll
sumber
3

Berikut dengan contoh mobil Anda : ketika Anda mendapatkan mobil, Anda tidak mendapatkan mobil sembarangan, maksud saya, Anda memilih warna, merek, jumlah kursi, dll. Dan beberapa barang juga "diinisialisasi" tanpa Anda pilih untuk itu, seperti nomor roda atau nomor registrasi.

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

Jadi, dalam __init__metode Anda menentukan atribut dari instance yang Anda buat. Jadi kalau kita mau mobil Renault biru, untuk 2 orang kita inisialisasi atau contoh Carseperti:

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

Dengan cara ini, kami membuat instance Carkelas. The __init__adalah salah satu yang menangani atribut spesifik kita (seperti coloratau brand) dan menghasilkan atribut lain, seperti registration_number.

juliomalegria.dll
sumber
3

Kelas adalah objek dengan atribut (status, karakteristik) dan metode (fungsi, kapasitas) yang spesifik untuk objek tersebut (seperti warna putih dan kekuatan terbang, masing-masing, untuk bebek).

Saat Anda membuat instance kelas, Anda dapat memberinya beberapa kepribadian awal (status atau karakter seperti nama dan warna gaunnya untuk bayi yang baru lahir). Anda melakukan ini dengan __init__.

Pada dasarnya __init__menyetel karakteristik instance secara otomatis saat Anda memanggil instance = MyClass(some_individual_traits).

joaquin
sumber
2

The __init__Fungsi adalah menyiapkan semua variabel anggota di kelas. Jadi, setelah bicluster Anda dibuat, Anda dapat mengakses anggota tersebut dan mendapatkan nilainya kembali:

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

Lihat Dokumen Python untuk beberapa info. Anda akan ingin mengambil buku tentang konsep OO untuk terus belajar.

AlG
sumber
1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

Dalam contoh di atas kami menggunakan spesies sebagai global karena akan selalu sama (Jenis konstanta dapat Anda katakan). ketika Anda memanggil __init__metode maka semua variabel di dalamnya __init__akan dimulai (misalnya: breed, name).

class Dog(object):
    a = '12'

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

jika Anda mencetak contoh di atas dengan menelepon di bawah seperti ini

Dog.a
12

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

Itu berarti itu hanya akan diinisialisasi selama pembuatan objek. jadi apa pun yang ingin Anda deklarasikan sebagai konstan jadikan sebagai global dan apa pun yang berubah digunakan __init__

vedavyasa k
sumber