Чому ми використовуємо __init__ в класах Python?


124

У мене виникають проблеми з розумінням ініціалізації занять.

Який сенс у них і як ми знаємо, що до них включити? Чи вимагає написання в класах іншого типу мислення, а не створення функцій (я вважав, що я можу просто створити функції, а потім просто обернути їх у класі, щоб я міг їх повторно використовувати. Чи буде це спрацьовувати?)

Ось приклад:

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

Або інший зразок коду:

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

Стільки класів __init__я зустрічаю, коли я намагаюся читати код інших людей, але я не розумію логіки їх створення.


1
історія про Ініта - це ... бла, бла, бла .... конструктор-деструктор, але не руйнівець, тому що збирання сміття доступне.
MisterGeeky

Відповіді:


289

По тому, що ви написали, вам не вистачає критичного розуміння: різниця між класом і об'єктом. __init__не ініціалізує клас, він ініціалізує екземпляр класу або об'єкта. Кожна собака має колір, але собаки як клас не мають. У кожної собаки чотири або менше футів, але клас собак немає. Клас - це поняття предмета. Побачивши Фідо і Пляму, ти впізнаєш їхню схожість, їх собачість. Ось клас.

Коли ти кажеш

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

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

Ви кажете, Фідо - це коричнева собака з чотирма ногами, а пляма - трохи каліка і переважно жовта. __init__Функція викликається конструктор, або ініціалізатор, і автоматично викликається при створенні нового екземпляра класу. У межах цієї функції новоствореному об'єкту присвоюється параметр self. Позначення self.legs- це атрибут, який називається legsоб'єктом у змінній self. Атрибути на зразок змінних, але вони описують стан об'єкта або конкретні дії (функції), доступні об'єкту.

Однак зауважте, що ви не налаштовані colourна саму собачість - це абстрактне поняття. На заняттях є атрибути, які мають сенс. Наприклад, population_sizeє одна така - немає сенсу рахувати Фідо, бо Фідо завжди є одним. Має сенс рахувати собак. Скажімо, у світі є 200 мільйонів собак. Це властивість класу Собака. Фідо не має нічого спільного з чисельністю 200 мільйонів, а також з Spot. Це називається "атрибут класу", на відміну від "атрибутів екземпляра", які є colourабо legsвище.

Тепер, до чогось менш собачого і більше пов'язаного з програмуванням. Як я пишу нижче, клас додавати речі не є розумним - що це за клас? Заняття в Python складають колекції різних даних, які ведуть себе аналогічно. Клас собак складається з Фідо і Пляма та 199999999998 інших подібних до них тварин, всі вони визирають на ліхтариках. З чого складається клас для додавання речей? За якими властивими їм даними вони відрізняються? І якими діями вони поділяються?

Однак цифри ... це цікавіші предмети. Скажіть, цілі. Їх багато, набагато більше, ніж собак. Я знаю, що в Python вже є цілі числа, але давайте пограємо німо і "реалізуємо" їх знову (шляхом обману та використання цілих чисел Python).

Отже, Integers - це клас. Вони мають деякі дані (значення) та деякі поведінки ("додайте мене до цього іншого числа"). Покажемо це:

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

Це трохи крихко (ми припускаємо, що otherце буде MyInteger), але ми зараз ігноруємо. У реальному коді ми б не стали; ми перевіримо це, і, можливо, навіть примусимо його ("ти не ціле число?

Ми могли навіть визначити дроби. Дроби також знають, як додати себе.

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)

Існує навіть більше дробів, ніж цілі числа (не дуже, але комп'ютери цього не знають). Зробимо два:

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

Ви насправді нічого тут не декларуєте. Атрибути - це як новий вид змінної. Звичайні змінні мають лише одне значення. Скажімо, ви пишете colour = "grey". Ви не можете мати іншу змінну colour, яка "fuchsia"- не в тому ж місці в коді.

Масиви вирішують це до певної міри. Якщо ви скажете colour = ["grey", "fuchsia"], ви склали два кольори у змінну, але ви розрізняєте їх за їх положенням (0, або 1, у цьому випадку).

Атрибути - це змінні, які пов'язані з об'єктом. Як і в масивах, у нас може бути безліч colourзмінних для різних собак . Отже, fido.colourодна змінна, але spot.colourінша. Перший пов'язаний з об'єктом в межах змінної fido; другий , spot. Тепер, коли ви зателефонували Dog(4, "brown")або three.add(five), завжди буде невидимий параметр, який буде присвоєний додатковому висячому внизу списку параметрів. Це умовно називається selfі отримає значення об’єкта перед крапкою. Таким чином, усередині Собаки __init__(конструктора) selfбуде те , що виявиться новою Собакою; в межах MyIntegers add, selfбуде прив'язаний до об'єкта в змінній three. Таким чином,three.valueбуде такою ж змінною за межами add, як і self.valueв межах add.

Якщо я скажу the_mangy_one = fido, я почну посилатися на об'єкт, відомий як fidoще з іншою назвою. Відтепер fido.colourточно така ж змінна, як і the_mangy_one.colour.

Отже, речі всередині __init__. Ви можете подумати про них як про зауваження речей у свідоцтві про народження Собаки. colourсама по собі є випадковою змінною, може містити що завгодно. fido.colourабо self.colour- як поле форми на посвідченні особи собаки; і __init__чиновник заповнює його вперше.

Ясніше?

EDIT : Розширення на коментар нижче:

Ви маєте на увазі перелік об’єктів , чи не так?

Перш за все, fidoнасправді це не об’єкт. Це змінна, яка наразі містить об'єкт, як і коли ви говорите x = 5, xце змінна, що містить число п'ять. Якщо згодом ви передумаєте, ви можете fido = Cat(4, "pleasing")(поки ви створили клас Cat) і fidoз цього моменту "містити" об'єкт кішки. Якщо ви це зробите fido = x, то вона буде містити число п’ять, а зовсім не об’єкт тварини.

Клас сам по собі не знає своїх примірників, якщо ви спеціально не пишете код, щоб відстежувати їх. Наприклад:

class Cat:
    census = [] #define census array

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

Ось censusатрибут Catкласу на рівні класу.

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

Зверніть увагу, що ви не отримаєте [fluffy, sparky]. Це просто назви змінних. Якщо ви хочете, щоб самі коти мали імена, вам слід зробити окремий атрибут для імені, а потім замінити __str__метод повернення цього імені. Метод цього методу (тобто функція, пов'язана з класом, точно так само, як addі __init__) полягає в тому, щоб описати, як перетворити об'єкт у рядок, як, наприклад, при його друкуванні.


7
Нічого, спасибі ... це насправді мало сенсу для мене, так що все, що робить щось таке, мені потрібно заздалегідь заявити у функції init. У цьому випадку Собака має ніжки та колір. Наприклад, якщо я створив клас, який додав два числа, я б оголосив self.firstnumber та self.secondnumber, тоді просто роблю firstnumber + secondnumber пізніше в класі, щоб отримати відповідь?
Lostsoul

1
Типу. Ви могли це зробити. Але навряд чи має сенс робити клас просто для додавання речей. Класи зазвичай реалізують дані з поведінкою - чиста поведінка - це лише функції. Я розширю відповідь чимось релевантним; почекай трохи.
Амадан

3
Дякую за чудову відповідь. Я бачу і розумію силу класів зараз. Вибачте, якщо це звучить німо. Ви просто мені зрозуміли, що я можу сортувати дані та підтримувати стан багатьох різних речей одночасно (тоді як я би відслідковував стільки змінних, скільки можу створити чи більше за допомогою циклів). Так скажіть, мені потрібно з'ясувати середню кількість ніг на одну собаку? Чи є спосіб отримати список усіх створених мною об'єктів за допомогою класу, щоб я міг розпочати наступну оцінку? або я також повинен вести список створених мною класів (тобто [fido, spot])
Lostsoul

23

Щоб внести свої 5 копійок у ґрунтовне пояснення з Амадану .

Де класи - це опис "типу" абстрактно. Об'єкти - це їх реалізація: жива дихаюча річ. У об'єктно-орієнтованому світі є основні ідеї, які можна майже назвати сутністю всього. Вони є:

  1. інкапсуляція (на цьому не буде детальніше)
  2. спадкування
  3. поліморфізм

Об'єкти мають одну чи більше характеристик (= Атрибути) та поведінки (= Методи). Поведінка здебільшого залежить від особливостей. Класи визначають, що поведінка повинна досягати загальним чином, але до тих пір, поки клас не усвідомлюється (інстанціюється) як об'єкт, він залишається абстрактним поняттям можливості. Дозвольте проілюструвати за допомогою «спадкування» та «поліморфізму».

    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

Деякі характеристики визначають людину. Але кожна національність дещо відрізняється. Тож "національні типи" - це людні люди з мотивами. "Американці" - це тип "людей" і успадковують деякі абстрактні характеристики та поведінку від людського типу (базовий клас): це спадщина. Тож усі люди можуть сміятися і пити, тому всі дитячі класи також можуть! Спадщина (2).

Але оскільки вони однакові (Тип / базовий клас: Люди), ви можете іноді їх обмінювати: дивіться фор-цикл наприкінці. Але вони розкриють індивідуальну характеристику, і це поліморфізм (3).

Таким чином, у кожної людини є улюблена потреба, але кожна національність схильна до особливого виду напоїв. Якщо ви підкласуєте до національності від типу людей, ви можете перезаписати успадковану поведінку, як я продемонстрував вище drink()методом. Але це все ще на рівні класу, і тому це все ще узагальнення.

hans = German(favorite_drink = "Cola")

примірник класу німецька, і я "змінив" характеристику за замовчуванням на початку. (Але якщо ви зателефонуєте на hans.drink ("Молоко"), він все-таки надрукує "мені потрібно більше пива" - очевидна помилка ... а може, це те, що я назвав би особливістю, якщо я став співробітником більшої компанії. ;-)! )

Характеристика типу, наприклад, німці (хани), як правило, визначаються через конструктор (у пітоні: __init__ в момент інстанції. Це той момент, коли ви визначаєте клас, щоб стати об’єктом. Можна сказати, що вдихне життя в абстрактне поняття (клас), наповнивши його індивідуальними характеристиками і ставши об'єктом.

Але оскільки кожен об'єкт є екземпляром класу, вони поділяють усі основні типи характеристик та певну поведінку. Це головна перевага об'єктно-орієнтованої концепції.

Щоб захистити характеристики кожного об'єкта, ви їх інкапсулюєте - означає, що ви намагаєтеся з’єднати поведінку та характеристику і ускладнюєте маніпулювати ними ззовні об'єкта. Це інкапсуляція (1)


5

Це просто ініціалізувати змінні екземпляра.

Наприклад, створити crawlerекземпляр із конкретним іменем бази даних (з вашого прикладу вище).


Вибачте, я не дуже розумію, що це означає. У наведеному вище прикладі. Чи не міг розробник просто додати до свого основного коду "left = foo" тощо.
Lostsoul

Ви маєте на увазі значення за замовчуванням функції? left=Noneзліва буде ініціалізовано до, Noneякщо при створенні leftпараметр не вказаний.
jldupont

Я думаю, що це починає мати сенс ... це подобається, як ви повинні попередньо визначити свої змінні в java "String left" чи щось таке? то, як тільки його ініціалізується до класу, ви можете маніпулювати значеннями? Це лише трохи заплутано в порівнянні з функціями, тому що я можу просто надсилати значення функції і не потрібно нічого ініціалізувати заздалегідь.
Lostsoul

1
@Lostsoul: left = fooпрацював би - один раз. Сенс занять - робити щось розумне для кожного іншого crawler. Класи - це не функції, ані те, що може порівнюватись із функціями (ну, поки ви не набагато просунутіші та не ввійдете у функціональне програмування, але це зараз просто вас бентежить). Прочитайте мою відповідь про те, якими є насправді класи - ви все ще не отримуєте цього.
Амадан

4

Здається, вам потрібно використовувати __init__в Python, якщо ви хочете правильно ініціалізувати змінні атрибути своїх примірників.

Дивіться наступний приклад:

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

Це зовсім інше в Java, де кожен атрибут автоматично ініціалізується з новим значенням:

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

інтуїтивно ми очікуємо:

[strange] []

Але якщо ви оголосите attrяк static, це буде діяти як Python:

[strange] [strange]

3

Слідуючи прикладу вашого автомобіля : коли ви отримуєте автомобіль, ви просто не отримуєте випадковий автомобіль, я маю на увазі, ви вибираєте колір, марку, кількість місць тощо. І деякі речі також "ініціалізуються", не вибираючи для нього, як кількість коліс або реєстраційний номер.

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

Отже, у __init__методі ви визначаєте атрибути створеного вами екземпляра. Отже, якщо ми хочемо синій автомобіль Renault, на 2 людини, ми б ініціалізували або екземпляр на Carзразок:

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

Таким чином ми створюємо екземпляр Carкласу. Це __init__той, який обробляє наші конкретні атрибути (наприклад, colorабо brand) та генерує інші атрибути, як-от registration_number.


3

Класи - це об'єкти з атрибутами (стан, характеристика) та методами (функціями, ємностями), специфічними для цього об’єкта (як, наприклад, білий колір та сили мухи, відповідно, для качки).

Створюючи екземпляр класу, ви можете надати йому деяку початкову особистість (стан або персонаж, як ім'я та колір її сукні для новонародженого). Ви робите це за допомогою__init__ .

В основному __init__встановлює характеристики примірника автоматично під час дзвінка instance = MyClass(some_individual_traits).


2

__init__Функція установки всіх змінних - членів в класі. Тож як тільки ваш бікстер буде створений, ви можете отримати доступ до члена та отримати значення назад:

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

Ознайомтеся з Документами Python для отримання деякої інформації. Ви хочете взяти книгу про поняття ОО для продовження навчання.


1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

У наведеному вище прикладі ми використовуємо види як глобальні, оскільки вони завжди будуть однаковими (Вигляд постійного, який ви можете сказати). коли ви викликаєте __init__метод, тоді __init__буде започаткована вся змінна всередині (наприклад: порода, ім'я).

class Dog(object):
    a = '12'

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

якщо ви надрукуєте приклад вище, зателефонувавши нижче

Dog.a
12

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

Це означає, що він буде ініціалізований лише під час створення об'єкта. тому все, що ви хочете оголосити постійним, зробить це як глобальне і все, що зміни використовують __init__

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.