¿Por qué usamos __init__ en las clases de Python?

124

Tengo problemas para comprender la inicialización de clases.

¿Qué sentido tienen y cómo sabemos qué incluir en ellos? ¿Escribir en clases requiere un tipo diferente de pensamiento en comparación con la creación de funciones (pensé que podría simplemente crear funciones y luego envolverlas en una clase para poder reutilizarlas?

He aquí un ejemplo:

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

U otra muestra 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

Hay tantas clases con las que __init__me encuentro cuando trato de leer el código de otras personas, pero no entiendo la lógica para crearlas.

Alma perdida
fuente
1
la historia de init es ... bla, bla, bla .... constructor-destructor pero no destructor porque la recolección de basura está disponible.
MisterGeeky

Respuestas:

289

Por lo que escribió, se está perdiendo una pieza fundamental de comprensión: la diferencia entre una clase y un objeto. __init__no inicializa una clase, inicializa una instancia de una clase o un objeto. Cada perro tiene color, pero los perros como clase no. Cada perro tiene cuatro patas o menos, pero la clase de perros no. La clase es un concepto de objeto. Cuando ves a Fido y Spot, reconoces su similitud, su condición de perro. Esa es la clase.

Cuando tu dices

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

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

Estás diciendo, Fido es un perro marrón con 4 patas, mientras que Spot es un poco lisiado y es mayormente amarillo. La __init__función se denomina constructor o inicializador y se llama automáticamente cuando crea una nueva instancia de una clase. Dentro de esa función, el objeto recién creado se asigna al parámetro self. La notación self.legses un atributo llamado legsdel objeto en la variable self. Los atributos son una especie de variables similares, pero describen el estado de un objeto o acciones particulares (funciones) disponibles para el objeto.

Sin embargo, tenga en cuenta que no se establece colourpara el doghood en sí, es un concepto abstracto. Hay atributos que tienen sentido en las clases. Por ejemplo, population_sizees uno de esos: no tiene sentido contar el Fido porque Fido siempre es uno. Tiene sentido contar perros. Digamos que hay 200 millones de perros en el mundo. Es propiedad de la clase Dog. Fido no tiene nada que ver con el número 200 millones, ni Spot. Se llama "atributo de clase", a diferencia de "atributos de instancia" que son colouro legssuperiores.

Ahora, a algo menos canino y más relacionado con la programación. Como escribo a continuación, la clase para agregar cosas no es sensata, ¿de qué es una clase? Las clases en Python se componen de colecciones de diferentes datos que se comportan de manera similar. La clase de perros consiste en Fido y Spot y 199999999998 otros animales similares a ellos, todos ellos haciendo pis en postes de luz. ¿En qué consiste la clase para agregar cosas? ¿En qué datos inherentes a ellos se diferencian? ¿Y qué acciones comparten?

Sin embargo, los números ... esos son temas más interesantes. Dime, enteros. Hay muchos, muchos más que perros. Sé que Python ya tiene números enteros, pero hagamos el tonto y los "implementemos" nuevamente (haciendo trampa y usando los números enteros de Python).

Entonces, los enteros son una clase. Tienen algunos datos (valor) y algunos comportamientos ("agrégame a este otro número"). Demostremos esto:

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

Esto es un poco frágil (asumimos otherque será un MyInteger), pero lo ignoraremos ahora. En código real, no lo haríamos; lo probaríamos para asegurarnos, y tal vez incluso coaccionarlo ("¿no eres un número entero? ¡Caramba, tienes 10 nanosegundos para convertirte en uno! 9 ... 8 ...")

Incluso podríamos definir fracciones. Las fracciones también saben cómo sumarse.

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)

Hay incluso más fracciones que números enteros (en realidad no, pero las computadoras no lo saben). Hagamos dos:

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

En realidad, no declaras nada aquí. Los atributos son como un nuevo tipo de variable. Las variables normales solo tienen un valor. Digamos que escribe colour = "grey". No puede tener otra variable nombrada colourque "fuchsia"no esté en el mismo lugar en el código.

Las matrices resuelven eso hasta cierto punto. Si dice colour = ["grey", "fuchsia"], ha apilado dos colores en la variable, pero los distingue por su posición (0 o 1, en este caso).

Los atributos son variables que están vinculadas a un objeto. Al igual que con las matrices, podemos tener muchas colourvariables, en diferentes perros . Entonces, fido.coloures una variable, pero spot.coloures otra. El primero está vinculado al objeto dentro de la variable fido; el segundo spot,. Ahora, cuando llame Dog(4, "brown"), o three.add(five), siempre habrá un parámetro invisible, que se asignará al extra que cuelga al principio de la lista de parámetros. Se llama convencionalmente selfy obtendrá el valor del objeto delante del punto. Así, dentro del __init__(constructor) del Perro , selfestará lo que resulte ser el nuevo Perro; dentro MyIntegerde add, selfse vinculará al objeto en la variable three. Así,three.valueserá la misma variable fuera de add, como self.valuedentro de add.

Si digo the_mangy_one = fido, comenzaré a referirme al objeto conocido como fidocon otro nombre. De ahora en adelante, fido.coloures exactamente la misma variable que the_mangy_one.colour.

Entonces, las cosas dentro del __init__. Puede pensar en ellos como anotando cosas en el certificado de nacimiento del Perro. colourpor sí misma es una variable aleatoria, podría contener cualquier cosa. fido.colouro self.coloures como un campo de formulario en la hoja de identidad del Perro; y __init__el secretario lo está llenando por primera vez.

¿Más claro?

EDITAR : Ampliando el comentario a continuación:

Te refieres a una lista de objetos , ¿no?

En primer lugar, en fidorealidad no es un objeto. Es una variable, que actualmente contiene un objeto, al igual que cuando dice x = 5, xes una variable que actualmente contiene el número cinco. Si luego cambia de opinión, puede hacerlo fido = Cat(4, "pleasing")(siempre que haya creado una clase Cat) y fido, a partir de ese momento, "contendrá" un objeto gato. Si lo hace fido = x, contendrá el número cinco, y no un objeto animal en absoluto.

Una clase por sí misma no conoce sus instancias a menos que usted escriba específicamente código para realizar un seguimiento de ellas. Por ejemplo:

class Cat:
    census = [] #define census array

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

Aquí, censuses un atributo de Catclase a nivel de clase.

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

Tenga en cuenta que no obtendrá [fluffy, sparky]. Esos son solo nombres de variables. Si desea que los gatos mismos tengan nombres, debe crear un atributo separado para el nombre y luego anular el __str__método para devolver este nombre. El propósito de este método (es decir, función ligada a la clase, como addo __init__) es describir cómo convertir el objeto en una cadena, como cuando lo imprime.

Amadan
fuente
7
wow gracias ... esto realmente tiene mucho sentido para mí, así que cualquier cosa que haga que algo sea lo que es, necesito declararlo previamente en la función init. En este caso, Perro, tiene patas y color. Por ejemplo, si hiciera una clase que agregara dos números, declararía self.firstnumber y self.secondnumber y luego solo haría firstnumber + secondnumber más adelante en la clase para obtener la respuesta.
Lostsoul
1
Mas o menos. Podrías hacer eso. Pero no tiene sentido hacer una clase solo para agregar cosas. Las clases normalmente implementan datos con comportamientos; los comportamientos puros son solo funciones. Ampliaré la respuesta con algo relevante; espera un poco.
Amadan
3
Gracias por la asombrosa respuesta. Ahora veo y entiendo el poder de las clases. Lo siento, si suena tonto. Solo me doy cuenta de que puedo ordenar datos y mantener el estado de muchas cosas diferentes a la vez (mientras que solo rastrearía tantas variables como pueda crear o más a través de bucles). Entonces, ¿necesito averiguar el número promedio de patas por perro? ¿Hay alguna manera de recuperar una lista de todos los objetos que he creado con una clase para poder comenzar una calucación como esta? o también debería mantener una lista de las clases que creo (es decir, [fido, spot])
Lostsoul
23

Contribuir con mis 5 centavos a la explicación detallada de Amadan .

Donde las clases son una descripción "de un tipo" de forma abstracta. Los objetos son sus realizaciones: la cosa viva que respira. En el mundo orientado a objetos hay ideas principales que casi se pueden llamar la esencia de todo. Son:

  1. encapsulación (no profundizaré en esto)
  2. herencia
  3. polimorfismo

Los objetos tienen una o más características (= Atributos) y comportamientos (= Métodos). El comportamiento depende principalmente de las características. Las clases definen lo que la conducta debería lograr de manera general, pero mientras la clase no se realice (instancia) como un objeto, sigue siendo un concepto abstracto de una posibilidad. Permítanme ilustrar con la ayuda de "herencia" y "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

Algunas características definen al ser humano. Pero cada nacionalidad difiere un poco. Así que los "tipos nacionales" son algo así como humanos con extras. Los "estadounidenses" son un tipo de "humanos" y heredan algunas características y comportamientos abstractos del tipo humano (clase base): eso es herencia. ¡Así que todos los humanos pueden reír y beber, por lo tanto, todas las clases para niños también pueden hacerlo! Herencia (2).

Pero debido a que todos son del mismo tipo (Tipo / clase base: Humanos), puede intercambiarlos a veces: vea el bucle for al final. Pero expondrán una característica individual, y ese es el polimorfismo (3).

Así que cada ser humano tiene una bebida favorita, pero todas las nacionalidades tienden a tomar un tipo especial de bebida. Si subclasifica una nacionalidad del tipo de Humanos, puede sobrescribir el comportamiento heredado como he demostrado anteriormente con el drink()Método. Pero eso todavía está en el nivel de clase y, debido a esto, sigue siendo una generalización.

hans = German(favorite_drink = "Cola")

instancia la clase German y "cambié" una característica predeterminada al principio. (Pero si llamas a hans.drink ('Leche'), él todavía imprimiría "Necesito más cerveza", un error obvio ... o tal vez eso es lo que llamaría una función si fuera un Empleado de una Compañía más grande. ;-)! )

La característica de un tipo, por ejemplo, los alemanes (hans) generalmente se definen a través del constructor (en python :) __init__en el momento de la instanciación. Este es el punto en el que se define una clase para que se convierta en un objeto. Se podría decir que da vida a un concepto abstracto (clase) llenándolo de características individuales y convirtiéndolo en un objeto.

Pero debido a que cada objeto es una instancia de una clase, comparten todos algunos tipos de características básicas y algún comportamiento. Esta es una gran ventaja del concepto orientado a objetos.

Para proteger las características de cada objeto, las encapsula, significa que intenta combinar el comportamiento y la característica y dificultar la manipulación desde fuera del objeto. Eso es encapsulación (1)

Don Pregunta
fuente
5

Es solo para inicializar las variables de la instancia.

Por ejemplo, cree una crawlerinstancia con un nombre de base de datos específico (de su ejemplo anterior).

jldupont
fuente
Lo siento, realmente no entiendo lo que eso significa ... en el ejemplo anterior ... no podría el desarrollador simplemente haber agregado en su código principal 'left = foo', etc.
Lostsoul
¿Te refieres a los valores predeterminados de la función? left=Noneleft se inicializará en Nonesi al crearse el leftparámetro no se especifica.
jldupont
Creo que está empezando a tener sentido ... ¿es como si tuviera que declarar previamente sus variables en java "String left" o algo así? luego, una vez que se inicializa en la clase, ¿puede manipular los valores? Es un poco confuso en comparación con las funciones porque puedo enviar valores a funciones y no necesito inicializar nada por adelantado.
Lostsoul
1
@Lostsoul: left = foofuncionaría, una vez. El objetivo de las clases es hacer algo sensato para todos los diferentes crawler. Las clases no son funciones, ni algo que se pueda comparar con funciones (bueno, no hasta que estés mucho más avanzado y entres en programación funcional, pero eso te confundirá ahora). Lea mi respuesta para saber qué son realmente las clases: todavía no lo está obteniendo.
Amadan
4

Parece que necesita usar __init__en Python si desea inicializar correctamente los atributos mutables de sus instancias.

Vea el siguiente ejemplo:

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

Esto es bastante diferente en Java, donde cada atributo se inicializa automáticamente con un nuevo 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);
    }
}

produce una salida que intuitivamente esperamos:

[strange] []

Pero si declaras attrcomo static, actuará como Python:

[strange] [strange]
alexpirina
fuente
3

Siguiendo con el ejemplo de su automóvil : cuando obtiene un automóvil, simplemente no obtiene un automóvil al azar, es decir, elige el color, la marca, el número de asientos, etc. Y algunas cosas también se "inicializan" sin que usted elija para ello, como número de ruedas o número de matrícula.

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

Entonces, en el __init__método, define los atributos de la instancia que está creando. Entonces, si queremos un automóvil Renault azul, para 2 personas, inicializaríamos o una instancia de Carcomo:

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

De esta forma, estamos creando una instancia de la Carclase. El __init__es el que está manejando nuestros atributos específicos (como coloro brand) y está generando los otros atributos, como registration_number.

juliomalegria
fuente
3

Las clases son objetos con atributos (estado, característica) y métodos (funciones, capacidades) que son específicos para ese objeto (como el color blanco y los poderes de mosca, respectivamente, para un pato).

Cuando crea una instancia de una clase, puede darle algo de personalidad inicial (estado o carácter como el nombre y el color de su vestido para un recién nacido). Haces esto con __init__.

Básicamente, __init__establece las características de la instancia automáticamente cuando llama instance = MyClass(some_individual_traits).

Joaquín
fuente
2

La __init__función está configurando todas las variables miembro de la clase. Entonces, una vez que se crea su bicluster, puede acceder al miembro y recuperar un valor:

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

Consulte los documentos de Python para obtener más información. Querrá tomar un libro sobre conceptos de OO para continuar aprendiendo.

AlG
fuente
1
class Dog(object):

    # Class Object Attribute
    species = 'mammal'

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

En el ejemplo anterior, usamos la especie como global, ya que siempre será la misma (se puede decir que es una especie de constante). cuando llame al __init__método __init__, se iniciarán todas las variables internas (por ejemplo: raza, nombre).

class Dog(object):
    a = '12'

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

si imprime el ejemplo anterior llamando a continuación así

Dog.a
12

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

Eso significa que solo se inicializará durante la creación del objeto. por lo que cualquier cosa que desee declarar como constante lo convierte en global y cualquier cosa que cambie use __init__

vedavyasa k
fuente