Почему мы используем __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
история init такова ... бла, бла, бла .... конструктор-деструктор, но не деструктор, потому что доступна сборка мусора.
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")

Вы говорите, что Фидо - коричневая собака с 4 ногами, а Спот немного калека и в основном желтого цвета. __init__Функция вызывается конструктор, или инициализатор, и автоматически вызывается при создании нового экземпляра класса. В этой функции вновь созданный объект назначается параметру self. Обозначение self.legs- это атрибут, вызываемый legsобъектом в переменной self. Атрибуты похожи на переменные, но они описывают состояние объекта или определенные действия (функции), доступные для объекта.

Однако обратите внимание, что вы не устанавливаете colourсамо собачье достоинство - это абстрактное понятие. Есть атрибуты, которые имеют смысл для классов. Например, population_sizeодна такая - считать Фидо не имеет смысла, потому что Фидо всегда один. Считать собак имеет смысл. Допустим, в мире 200 миллионов собак. Это собственность класса Dog. Ни Фидо, ни Спот не имеют отношения к числу 200 миллионов. Он называется «атрибутом класса», в отличие от «атрибутов экземпляра», которые есть colourили legsвыше.

Теперь о чем-то менее собачьем и более связанном с программированием. Как я пишу ниже, класс для добавления вещей не имеет смысла - что это за класс? Классы в Python состоят из наборов разных данных, которые ведут себя одинаково. Класс собак состоит из Фидо и Спота и 199999999998 других похожих на них животных, все они писают на фонарные столбы. Из чего состоит класс для добавления вещей? Какими присущими им данными они отличаются? И какие действия они разделяют?

Однако числа ... это более интересные предметы. Скажем, целые числа. Их много, намного больше, чем собак. Я знаю, что в Python уже есть целые числа, но давайте поиграемся в глупости и снова «реализуем» их (обманывая и используя целые числа Python).

Итак, целые числа - это класс. У них есть некоторые данные (значение) и поведение («добавьте меня к этому другому числу»). Покажем это:

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), но сейчас мы проигнорируем. В реальном коде мы бы этого не сделали; мы бы проверили его, чтобы убедиться, и, может быть, даже принудили его («вы не целое число? черт возьми, у вас есть 10 наносекунд, чтобы стать одним! 9 ... 8 ....»)

Мы могли даже определять дроби. Дроби тоже умеют складывать себя.

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"), or three.add(five), всегда будет невидимый параметр, который будет назначен дополнительному висящему в начале списка параметров. Он называется условно selfи получает значение объекта перед точкой. Таким образом, внутри объекта Dog __init__(конструктора) selfбудет то, чем окажется новый Dog; в MyInteger«с 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__клерк заполняет его впервые.

Есть более ясный?

РЕДАКТИРОВАТЬ : расширение комментария ниже:

Вы имеете в виду список объектов , не так ли?

Во-первых, fidoна самом деле это не объект. Это переменная, которая в настоящее время содержит объект, точно так же, как вы говорите x = 5, xчто это переменная, в настоящее время содержащая число пять. Если вы позже передумаете, вы можете это сделать fido = Cat(4, "pleasing")(при условии, что вы создали класс Cat), и fidoс этого момента он будет «содержать» объект cat. Если вы это сделаете 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).

Но поскольку все они одного вида (Тип / базовый класс: Люди), вы можете иногда их менять: см. Цикл for в конце. Но они выявят индивидуальную характеристику, а именно полиморфизм (3).

Итак, у каждого человека есть любимый напиток, но представители всех национальностей склонны к особому виду напитка. Если вы подклассифицируете национальность из типа людей, вы можете перезаписать унаследованное поведение, как я продемонстрировал выше с помощью drink()метода. Но это все еще на уровне класса, и поэтому это все еще обобщение.

hans = German(favorite_drink = "Cola")

создает экземпляр класса German, и я "изменил" характеристику по умолчанию в начале. (Но если вы назовете hans.drink ('Milk'), он все равно напечатает «Мне нужно больше пива» - очевидная ошибка ... или, может быть, это то, что я бы назвал функцией, если бы я был сотрудником более крупной компании. ;-)! )

Характеристики типа, например, Germans (hans), обычно определяются через конструктор (в python:) __init__в момент создания экземпляра. Это момент, когда вы определяете класс, который станет объектом. Вы можете сказать «вдохнуть жизнь» в абстрактное понятие (класс), наполнив его индивидуальными характеристиками и став объектом.

Но поскольку каждый объект является экземпляром класса, они разделяют все основные типы характеристик и поведение. Это главное преимущество объектно-ориентированной концепции.

Чтобы защитить характеристики каждого объекта, вы их инкапсулируете - это означает, что вы пытаетесь связать поведение и характеристику и затрудняете манипулирование им извне объекта. Это инкапсуляция (1)


5

Это просто инициализация переменных экземпляра.

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


Извините, я действительно не понимаю, что это значит ... в приведенном выше примере ... не мог ли разработчик просто добавить в свой основной код 'left = foo' и т. Д.
Lostsoul

Вы имеете ввиду значения функции по умолчанию? left=Noneleft будет инициализирован, 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] []

Но если вы объявите attras 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.