Por que usamos __init__ nas classes Python?

124

Estou tendo problemas para entender a inicialização de classes.

Qual é o objetivo deles e como sabemos o que incluir neles? A escrita em sala de aula exige um tipo diferente de pensamento em relação à criação de funções (imaginei que poderia criar funções e depois envolvê-las em uma classe para poder reutilizá-las. Isso funcionará?)

Aqui está um exemplo:

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

Ou outro exemplo de código:

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

Existem tantas classes com que __init__me deparo ao tentar ler o código de outras pessoas, mas não entendo a lógica de criá-las.

Alma perdida
fonte
1
a história do init é ... blá, blá, blá ... construtor-destruidor, mas nenhum destruidor porque a coleta de lixo está disponível.
MisterGeeky

Respostas:

289

Pelo que você escreveu, você está perdendo uma parte crítica do entendimento: a diferença entre uma classe e um objeto. __init__não inicializa uma classe, inicializa uma instância de uma classe ou um objeto. Cada cão tem cor, mas os cães como classe não. Cada cão tem quatro ou menos pés, mas a classe de cães não. A classe é um conceito de um objeto. Quando você vê o Fido e o Spot, reconhece a semelhança entre eles e sua dogmática. Essa é a aula.

Quando voce diz

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

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

Você está dizendo, Fido é um cachorro marrom com 4 pernas, enquanto Spot é um pouco aleijado e é principalmente amarelo. A __init__função é chamada de construtor ou inicializador e é chamada automaticamente quando você cria uma nova instância de uma classe. Dentro dessa função, o objeto recém-criado é atribuído ao parâmetro self. A notação self.legsé um atributo chamada legsdo objecto na variável self. Os atributos são variáveis ​​semelhantes, mas descrevem o estado de um objeto ou ações (funções) específicas disponíveis para o objeto.

No entanto, observe que você não colourescolhe a doghood - é um conceito abstrato. Existem atributos que fazem sentido nas classes. Por exemplo, population_sizeé um desses - não faz sentido contar o Fido porque o Fido é sempre um. Faz sentido contar cães. Digamos que existem 200 milhões de cães no mundo. É propriedade da classe Dog. Fido não tem nada a ver com o número 200 milhões, nem o Spot. É chamado de "atributo de classe", em oposição a "atributos de instância" que são colourou legsacima.

Agora, para algo menos canino e mais relacionado à programação. Enquanto escrevo abaixo, a classe para adicionar coisas não é sensata - do que é uma classe? As classes em Python compõem coleções de dados diferentes, que se comportam de maneira semelhante. A classe de cães consiste em Fido e Spot e 199999999998 outros animais semelhantes a eles, todos mijando em postes de luz. Em que consiste a classe para adicionar coisas? Por quais dados inerentes a eles eles diferem? E que ações eles compartilham?

No entanto, números ... esses são assuntos mais interessantes. Diga, Inteiros. Há muitos deles, muito mais que cães. Eu sei que o Python já tem números inteiros, mas vamos nos fazer de bobo e "implementá-los" novamente (trapaceando e usando os números inteiros do Python).

Então, Inteiros são uma classe. Eles têm alguns dados (valor) e alguns comportamentos ("adicione-me a esse outro número"). Vamos mostrar isso:

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

Isso é um pouco frágil (estamos assumindo otherque será um MyInteger), mas ignoraremos agora. No código real, nós não; nós o testávamos para ter certeza, e talvez até o coagia ("você não é um número inteiro? por golly, você tem 10 nanossegundos para se tornar um! 9 ... 8 ....")

Poderíamos até definir frações. As frações também sabem como se adicionar.

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)

Há ainda mais frações do que números inteiros (na verdade não, mas os computadores não sabem disso). Vamos fazer dois:

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

Você não está realmente declarando nada aqui. Os atributos são como um novo tipo de variável. Variáveis ​​normais possuem apenas um valor. Digamos que você escreva colour = "grey". Você não pode ter outra variável com o nome colourque está "fuchsia"- não está no mesmo lugar no código.

Matrizes resolvem isso até certo ponto. Se você disser colour = ["grey", "fuchsia"], você empilhou duas cores na variável, mas as distingue pela posição (0 ou 1, neste caso).

Atributos são variáveis ​​vinculadas a um objeto. Assim como as matrizes, podemos ter muitas colourvariáveis em cães diferentes . Então, fido.colouré uma variável, mas spot.colouré outra. O primeiro está vinculado ao objeto dentro da variável fido; o segundo spot. Agora, quando você chama Dog(4, "brown"), ou three.add(five), sempre haverá um parâmetro invisível, que será atribuído ao extra pendente na frente da lista de parâmetros. É chamado convencionalmente selfe obterá o valor do objeto na frente do ponto. Assim, dentro do cão __init__(construtor), selfserá o que o novo cão se tornará; dentro MyInteger's add, selfirá ser ligado ao objecto na variável three. Portanto,three.valueserá a mesma variável fora do add, como self.valuedentro do add.

Se eu disser the_mangy_one = fido, começarei a me referir ao objeto conhecido como fidocom outro nome. A partir de agora, fido.colouré exatamente a mesma variável que the_mangy_one.colour.

Então, as coisas dentro do __init__. Você pode pensar nelas como anotando coisas na certidão de nascimento do cão. colourpor si só é uma variável aleatória, pode conter qualquer coisa. fido.colourou self.colouré como um campo de formulário na folha de identidade do cão; e __init__é o funcionário preenchendo-o pela primeira vez.

Mais claro?

EDIT : Expandindo no comentário abaixo:

Você quer dizer uma lista de objetos , não é?

Primeiro de tudo, fidona verdade não é um objeto. É uma variável que atualmente contém um objeto, assim como quando você diz x = 5, xé uma variável que atualmente contém o número cinco. Se você mudar de idéia mais tarde, poderá fazê-lo fido = Cat(4, "pleasing")(desde que tenha criado uma classe Cat) e fido, a partir de então, "conterá" um objeto gato. Se o fizer fido = x, ele conterá o número cinco, e não um objeto animal.

Uma classe por si só não conhece suas instâncias, a menos que você escreva um código especificamente para acompanhá-las. Por exemplo:

class Cat:
    census = [] #define census array

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

Aqui censusestá um atributo de Catclasse no nível de classe.

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

Note que você não receberá [fluffy, sparky]. Esses são apenas nomes de variáveis. Se você deseja que os próprios gatos tenham nomes, é necessário criar um atributo separado para o nome e substituir o __str__método para retornar esse nome. O objetivo deste método (isto é, função vinculada à classe, exatamente como addou __init__) é descrever como converter o objeto em uma string, como quando você o imprime.

Amadan
fonte
7
uau obrigado .. isso realmente fez muito sentido para mim, então qualquer coisa que faça algo como é, eu preciso pré-declarar na função init. Nesse caso, cachorro, tem pernas e cores. Por exemplo, se eu fizesse uma classe que adicionasse dois números, eu declararia self.firstnumber e self.secondnumber, em seguida, basta fazer firstnumber + secondnumber mais tarde na classe para obter a resposta?
Lostsoul
1
Mais ou menos. Você poderia fazer isso. Mas dificilmente faz sentido fazer uma aula apenas para adicionar coisas. As classes normalmente implementam dados com comportamentos - comportamentos puros são apenas funções. Expandirei a resposta com algo relevante; espere um pouco.
Amadan
3
Obrigado pela resposta incrível. Eu vejo e entendo o poder das aulas agora. Desculpe, se parece idiota. Você acabou de perceber que posso classificar dados e manter o estado de muitas coisas diferentes de uma só vez (enquanto eu controlaria apenas quantas variáveis ​​puder criar ou mais por meio de loops). Digamos, eu preciso descobrir o número médio de pernas por cão? Existe uma maneira de recuperar uma lista de todos os objetos que criei com uma classe para que eu possa iniciar um cálculo como este? ou devo também manter uma lista das classes que crio (ex. [fido, spot]) #
Lostsoul
23

Contribuir com meus 5 centavos para a explicação completa de Amadan .

Onde classes são uma descrição "de um tipo" de maneira abstrata. Objetos são suas realizações: a coisa viva da respiração. No mundo orientado a objetos, existem idéias principais que você quase pode chamar de essência de tudo. Eles são:

  1. encapsulamento (não será elaborado sobre isso)
  2. herança
  3. polimorfismo

Os objetos têm uma ou mais características (= atributos) e comportamentos (= métodos). O comportamento depende principalmente das características. As classes definem o que o comportamento deve realizar de uma maneira geral, mas enquanto a classe não for realizada (instanciada) como um objeto, ela permanecerá um conceito abstrato de uma possibilidade. Deixe-me ilustrar com a ajuda de "herança" e "polimorfismo".

    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

Algumas características definem seres humanos. Mas toda nacionalidade difere um pouco. Então "tipos nacionais" são como seres humanos com extras. "Americanos" são um tipo de "Humanos" e herdam algumas características e comportamentos abstratos do tipo humano (classe base): isso é herança. Assim, todos os humanos podem rir e beber, portanto, todas as classes infantis também podem! Herança (2).

Mas como todos são do mesmo tipo (Tipo / classe base: Humanos), você pode trocá-los algumas vezes: veja o loop for no final. Mas eles vão expor uma característica individual, e isso é polimorfismo (3).

Portanto, cada ser humano tem uma bebida favorita, mas toda nacionalidade tende a um tipo especial de bebida. Se você subclassificar uma nacionalidade do tipo de Humanos, poderá sobrescrever o comportamento herdado, como demonstrei acima, com o drink()Método. Mas isso ainda é no nível de classe e, por isso, ainda é uma generalização.

hans = German(favorite_drink = "Cola")

instancia a classe alemão e eu "mudei" uma característica padrão no início. (Mas se você chamar hans.drink ('Milk'), ele ainda imprimiria "Preciso de mais cerveja" - um bug óbvio ... ou talvez seja isso que eu chamaria de recurso, se eu fosse um funcionário de uma empresa maior. ;-)! )

A característica de um tipo, por exemplo, alemães (hans) é geralmente definida através do construtor (em python :) __init__no momento da instanciação. Este é o ponto em que você define uma classe para se tornar um objeto. Você pode dizer que respira a vida em um conceito abstrato (classe) preenchendo-a com características individuais e se tornando um objeto.

Porém, como todo objeto é uma instância de uma classe, ele compartilha todos os tipos de características e comportamentos básicos. Essa é uma grande vantagem do conceito orientado a objetos.

Para proteger as características de cada objeto, você as encapsula - significa tentar combinar comportamento e característica e dificultar sua manipulação de fora do objeto. Isso é encapsulamento (1)

Don Question
fonte
5

É apenas para inicializar as variáveis ​​da instância.

Por exemplo, crie uma crawlerinstância com um nome de banco de dados específico (do seu exemplo acima).

jldupont
fonte
Sinto muito, eu realmente não entendo o que isso means..in o exemplo above..couldn't o desenvolvedor apenas adicionou em seu código principal 'esquerda = foo', etc ..
Lostsoul
Você quer dizer os valores padrão da função? left=Noneleft será inicializado para Nonese, na criação, o leftparâmetro não for especificado.
jldupont
Eu acho que está começando a fazer sentido .. é como você tem que pré-declarar suas variáveis ​​em java "String left" ou algo assim? então, uma vez inicializado para a classe, você pode manipular os valores? É um pouco confuso quando comparado a funções, porque eu posso enviar valores para funções e não preciso inicializar nada com antecedência.
Lostsoul
1
@ Lostsoul: left = foofuncionaria - uma vez. O objetivo das aulas é fazer algo sensato para todos os diferentes crawler. Classes não são funções, nem algo que possa ser comparado a funções (bem, não até você estar muito mais avançado e entrar em programação funcional, mas isso só vai confundir você agora). Leia minha resposta para saber o que realmente são as aulas - você ainda não está entendendo.
Amadan
4

Parece que você precisa usar __init__no Python se deseja inicializar corretamente atributos mutáveis ​​de suas instâncias.

Veja o seguinte exemplo:

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

Isso é bem diferente em Java, onde cada atributo é inicializado automaticamente com um novo valor:

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

produz uma saída que esperamos intuitivamente:

[strange] []

Mas se você declarar attrcomo static, ele agirá como Python:

[strange] [strange]
alexpirina
fonte
3

Seguindo o seu exemplo de carro : quando você compra um carro, você simplesmente não compra um carro aleatório, ou seja, você escolhe a cor, a marca, o número de assentos, etc. E algumas coisas também são "inicializadas" sem você escolher para isso, como número de rodas ou número de registro.

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

Portanto, no __init__método, você define os atributos da instância que está criando. Então, se quisermos um carro Renault azul, para 2 pessoas, inicializaríamos ou ocorreria algo Carcomo:

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

Dessa forma, estamos criando uma instância do Car classe. A __init__é a que está a lidar com os atributos específicos (como colorou brand) e o seu gerando os outros atributos, como registration_number.

juliomalegria
fonte
3

Classes são objetos com atributos (estado, característica) e métodos (funções, capacidades) específicos para esse objeto (como a cor branca e os poderes de voar, respectivamente, para um pato).

Ao criar uma instância de uma classe, você pode dar a ela alguma personalidade inicial (estado ou personagem, como o nome e a cor do vestido dela para um recém-nascido). Você faz isso com__init__ .

__init__Define basicamente as características da instância automaticamente quando você chama instance = MyClass(some_individual_traits).

joaquin
fonte
2

A __init__função está configurando todas as variáveis ​​de membro na classe. Assim que o seu bicluster for criado, você poderá acessar o membro e obter um valor de volta:

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

Confira o Python Docs para obter mais informações. Você vai querer pegar um livro sobre os conceitos de OO para continuar aprendendo.

AlG
fonte
1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

No exemplo acima, usamos as espécies como globais, uma vez que será sempre a mesma (tipo de constante que você pode dizer). quando você chamar o __init__método, todas as variáveis ​​dentro __init__dela serão iniciadas (por exemplo: raça, nome).

class Dog(object):
    a = '12'

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

se você imprimir o exemplo acima, chamando abaixo assim

Dog.a
12

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

Isso significa que ele será inicializado apenas durante a criação do objeto. qualquer coisa que você queira declarar constante a torne global e qualquer coisa que mude use __init__

vedavyasa k
fonte