Почему мы используем __init__ в классах Python? - PullRequest
110 голосов
/ 23 декабря 2011

У меня проблемы с пониманием инициализации классов.

В чем их смысл и как мы знаем, что в них включать?Требуется ли для написания в классах другой тип мышления по сравнению с созданием функций (я подумал, что могу просто создать функции, а затем просто обернуть их в классе, чтобы я мог их снова использовать. Будет ли это работать?)

Вотпример:

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__, с которыми я сталкиваюсь, когда пытаюсь прочитать код других людей, но я непонять логику в их создании.

Ответы [ 8 ]

268 голосов
/ 23 декабря 2011

По тому, что вы написали, вам не хватает критического понимания: разницы между классом и объектом. __init__ не инициализирует класс, он инициализирует экземпляр класса или объекта. У каждой собаки есть окрас, но у собак как у класса нет. У каждой собаки четыре или меньше фута, но у класса собак нет. Класс - это понятие объекта. Когда вы видите Fido и Spot, вы узнаете их сходство, их собачье. Это класс.

Когда вы говорите

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 миллионов собак. Это собственность класса Dog. Фидо не имеет ничего общего с числом 200 миллионов, как и Спот. Он называется «атрибут класса», а не «атрибуты экземпляра», которые colour или legs выше.

Теперь о чем-то менее собачьем и более связанном с программированием. Как я пишу ниже, класс для добавления вещей не имеет смысла - что это за класс? Классы в Python состоят из наборов различных данных, которые ведут себя одинаково. Класс собак состоит из Fido и Spot и 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") или three.add(five), всегда будет невидимый параметр, который будет назначен дополнительному висячему в начале списка параметров.Он обычно называется self и получит значение объекта перед точкой.Таким образом, в __init__ (конструкторе) Пса self будет тем, чем окажется новый Пёс;в пределах 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 class.

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

23 голосов
/ 23 декабря 2011

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

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

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

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

5 голосов
/ 23 декабря 2011

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

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

4 голосов
/ 31 августа 2013

Похоже, вам нужно использовать __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 голосов
/ 23 декабря 2011

Классы - это объекты с атрибутами (состоянием, характеристикой) и методами (функциями, возможностями), которые являются специфическими для этого объекта (например, белого цвета и силы мух, соответственно, для утки).

Когда высоздать экземпляр класса, вы можете дать ему некоторую начальную личность (состояние или характер, как имя и цвет ее платья для новорожденного).Вы делаете это с помощью __init__.

В основном __init__ автоматически устанавливает характеристики экземпляра при вызове instance = MyClass(some_individual_traits).

3 голосов
/ 23 декабря 2011

Следуя примеру car : когда вы получаете автомобиль, вы просто не получаете случайную машину, я имею в виду, вы выбираете цвет, марку, количество мест и т. Д. материал также «инициализируется» без вашего выбора, например, количество колес или регистрационный номер.

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.

2 голосов
/ 23 декабря 2011

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

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

Проверьте Python Docs для получения дополнительной информации.Для продолжения обучения вам понадобится книга о понятиях ОО.

1 голос
/ 11 октября 2017
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__

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...