7

Estoy empezando en esto de programar y me estoy metiendo en la POO. En python, ¿Porqué en las clases se pone __init__?. Y esto, a lo mejor puede sonar estúpido, pero ¿Por qué usar clases cuando se pueden usar funciones?.

FJSevilla
  • 55,603
  • 7
  • 35
  • 58
Diego
  • 369
  • 1
  • 5
  • 16
  • Relacionada: https://es.stackoverflow.com/questions/63407/qué-es-un-constructor/63451#63451 – toledano Jun 17 '17 at 22:13
  • Ambas preguntas parecen buenas, aunque personalmente las separaría: "_¿Por qué usar clases cuando se pueden usar funciones?_" no es específica de Python sino una pregunta sobre programación orientada a objetos vs funcional sin necesidad de entrar en ningún lenguaje en particular. – Alvaro Montoro Jun 18 '17 at 04:56
  • pero a la hora de llamar a la clase, tengo que poner infinidad de prints o hay una manera de hacerlos más rápido? – noob programer Feb 11 '18 at 15:28

3 Answers3

12

El método __init__ es un método especial y que equivale más o menos a lo que se conoce como constructor en otros lenguajes. Su principal función es establecer un estado inicial en el objeto nada más instanciarlo, es decir, inicializar los atributos.

Hace algún tiempo hize una respuesta bastante extensa sobre __init__ y su función en Python, te dejo el enlace para no repetir lo mismo:

¿Qué es un constructor?

En cuanto a tu segunda duda, ¿Por qué no usar programación estructurada siempre y no complicarnos la vida con esto de las clases?

Pues hay que partir de la premisa de no complicarse la vida sin necesidad, no siempre la POO es la mejor opción, no tiene mucho sentido si quiero crear una función que me retorne el cubo de un número (simple y llanamente) ponerme a crear una clase, con su método, instanciarla, etc.

Ahora bién, la POO no se creó para hacernos sufrir, hay casos donde es muy útil y en otros caso casi imprescindible como en la creación de interfaces gráficas o videojuegos. Hay muchas bondades y diferencias de la POO con respecto a la programación estructurada, pero entrar en esto es no terminar nunca.

En la POO todo gira en torno al objeto. Un objeto es una entidad que tiene un determinado estado, comportamiento (método) e identidad. Un objeto en POO no difiere mucho de un "objeto" del mundo real, por ejemplo, una persona tiene un determindao estado (yo estoy escribiendo esto), un comportamiento (como persona, puedo andar, beber, dormir, aburrirme, escribir esto para evitar el aburrimiento XD, etc) y tengo una identidad (yo soy yo y nadie más....).

El estado de un objeto (instancia) esta definido por sus atributos. No son más que datos (variables) que describen el estado actual del objeto.

El comportamiento está especificado por los métodos. Estos métodos permiten al objeto hacer algo (andar, escribir, encenderse, etc).

La identidad es una propiedad de la instancia que permite diferenciarla de cualquier otra. Un objeto siempre se puede diferenciar, incluso con respecto a objetos de la misma clase.

En la programación estructurada los procedimientos y los datos están separados y no se relacionan, ya que lo único que se busca es el procesamiento de unos datos de entrada para obtener otros de salida. La idea es muy simple, tengo unos datos y quiero obtener otros, para ello creo una función se los paso y esta me retorna mis datos nuevos. En la POO no se separan el estado (datos) de los métodos (procesos), no tiene sentido el concepto de una persona si separamos su estado y los métodos. Si esa persona está en coma hay muchos métodos (cosas que puede hacer un ser humano por ser un ser humano) que no va a poder hacer, no es lo mismo andar (método) si tengo una pierna rota (estado) que si no...

Es decir, en POO primero definimos un objeto y luego le pedimos que haga algo por sí mismo.

Despues de esta parrafada, un ejemplo práctico de porqué existe la POO. Como comenté en la creación de interfaces gráficas de usuario es muy importante este concepto. Una cosa muy común en una GUI es un botón. Vamos a necesitar muchos botones, cada uno de una manera (color, texto, acción al hacer click, etc). Si necesitamos 200 botones nos podemos entretener en crear cada uno de la nada, cada uno con sus funciones propias y terminar muertos en el intento. La POO entonces llega a nosotros cual ángel salvador y nos dice: crea una clase Botón, defines sus atributos (texto, tamaño, color, etc) y las cosas que puede hacer (clickearse, relanzarse, cambiar de color, desaparecer de nuestra vista, etc). Después creas tus 200 botones instanciando la clase y le das a cada uno su personalidad propia (pero sin dejar de ser un botón). Como ejemplo tonto:

class Boton():
    def __init__(self, texto = 'Botón', color = 'Azul', accion = None):
        self.texto = texto
        self.color = color
        self.accion = accion
        self.habilitado = True

    def pulsar(self):
        if self.accion:
            if self.habilitado:
                self.accion()
            else:
                print('Estoy desactivado, déjame en paz....')


    def deshabilitar(self):
        self.habilitado = False

    def habilitar(self):
        self.habilitado = True

El ejemplo es muy simple pero bueno, a ver si con esto se capta la idea. Ahora yo puedo crear los botones que quiera con el mínimo esfuerzo, solo los instancio:

def holaMundo():
    print('Hola mundo')

def opinion():
    print('Odio la POO... ¿Que m****a es esto?')

boton1 = Boton(accion = opinion)
boton2 = Boton(texto = 'Hola', accion = holaMundo)

Ahora tengo dos botones, ambos diferentes y ambos hacen lo que se supone que hace un botón:

>>> boton1.pulsar()
Odio la POO... ¿Que m****a es esto?

>>> boton2.pulsar()
Hola mundo

>>> boton2.deshabilitar()

>>> boton2.pulsar()
Estoy desactivado, déjame en paz....

>>> boton2.habilitar()

>>> boton2.pulsar()
Hola mundo

El ejemplo es simple pero se aproxima a la realidad. Observa que el botón no se limita a ejecutar funciones, sino que actua de formas diferentes dependiendo de su estado (en este caso estar o no deshabilitado). En inplementaciones reales esto es mucho mas complejo, imagínate hacer esto sin POO, puedes intentarlo...

Cuando usas Python estas usando esto muchas veces sin darte cuenta. Una lista no es más que una clase, tiene sus atributos (como el contenido) y sus métodos (sort, pop, append, etc). Hacer miLista = [1,2,3] no es mas que instanciar la clase List y crear un estado inicial. Si hacemos myLista.append(4) estamos usando uno de sus métodos, una lista puede crecer y almacenar otro elemento.

Siguendo con mis magníficas analogías para terminar, cuando un ser humano nace (es instanciado) lo hace con una serie de características que lo diferencian del resto (atributos), como son el color de pelo, color de ojos, sexo, etc. El método __init__ hace esto mismo, nada más nacer un objeto, le da una serie de características propias modificando sus atributos. Parece una tontería, pero esta es la forma de pensar en la POO, y es muy similar a como estructuramos el mundo real.

Bueno lo dejo aquí, como objeto de la clase SerHumano ejecuto el método dejar_de_dar_la_brasa() y paso de nuevo mi atributo aburrido a True... XD

FJSevilla
  • 55,603
  • 7
  • 35
  • 58
9

Las clases es la forma de generar objetos, POO es programación orientada a objetos, si quieres usar este paradigma tienes que pensar en objetos y clases.

un clase define un tipo de objeto, cuando lo llamas estas creando una instancia de esa clase, cuando quieres que algo suceda llamas a ese objeto y le indicas que lo haga.

__init__ es el constructor de tu clase en el creas una nueva instancia de la clase, por ejemplo

class perro():

  def __init__(self, nombre, size, raza):
      self.nombre=nombre
      self.size=size
      self.raza=raza

  def ladra(self):
     s=""
     for l in self.nombre:
      s+="wof"
     print s


chucho=perro("chucho","grande","husky")
chucho.ladra()

#wofwofwofwofwofwof

cloe=perro("cloe","mini","chihuahua")
cloe.ladra()

#wofwofwofwof

Para que usar objetos cuando puedes usar funciones, para no confundir varios objetos similares.

imaginemos un bot que maneja 20 clientes, tu no quieres que el bot le conteste al cliente 20 una pregunta del cliente 18, entonces creas 20 objetos bot y le asignas a cada uno un cliente.

en un juego quieres crear 100 enemigos y cada uno actúa de forma similar, pero quieres que cada uno tenga características que los distingan, pues creas la clase enemigo e implementas las características que los distinguen.

se supone que el POO se creo cuando los simuladores de vuelo empezaron a tener problemas al diferenciar un avión de otro dentro de la simulación.

Jorge Arturo Juarez
  • 2,813
  • 1
  • 11
  • 24
3

Yo soy un principiante en la programación en general y por lo tanto en python también.

Los principiantes hacemos preguntas aparentemente "tontas" porque cada signo, letra, combinación que vemos en una línea de código, simplemente no la entendemos porque todo nos es nuevo.

Cuando he leído respuestas tan complejas a una pregunta tan simple me doy cuenta de que, no sé por qué, pero es como si responder a algo simple con algo simple se convierte en algo complicado.

Creo que lo que quería saber este chico es lo que deseaba saber yo y ahora me da que lo he entendido, lo que no entiendo es porque no se explica en ningún sitio de forma directa y sin tanto "rollo" para lo que realmente sirve la funcion __init__ de forma práctica, la verdad que es de locos porque es una tontería pero solo veo respuestas mega retorcidas por diversos foros y artículos de internet de 5 páginas para al final seguir igual que al principio.

Para mi una respuesta directa que explique el uso de "__init__" en una clase sería algo así:

Cuando creas una clase pensando en hacer de ella muchos objetos (instancias de la clase), que tendrán siempre ciertas características en común, lo más fácil para ahorrar trabajo en la creación de cada objeto que se cree usando esa clase es que esos objetos cuando los creas tengan unos características (propiedades) y valores (argumentos) por defecto, para no tener que asignar esas características al objeto cada vez que creas un objeto.

Cuando usas la función __init__ en la clase, estás definiendo las propiedades y argumentos por defecto que tendrá el objeto creado.

Aquí lo explico con un ejemplo:

Si creas la clase vehículo y de ella vas a crear 2000 vehículos (coches objeto) y sabes que por defecto la mayoría de los objetos vehículo que se creen con esa clase tendrán probablemente cuatro ruedas, serán SUVs, tendrán motor diesel y ABS, pues lo defines en la clase con la funcion __init__ y esas serán las características por defecto que tendrá ese objeto al crearlo usando esa clase, en la práctica:

class vehiculo():
      def __init__(self, ruedas = 4, modelo = "SUV", motor = "diesel", ABS = True):
          self.ruedas = ruedas
          self.modelo = modelo
          self.motor = motor
          self.ABS = ABS

(El "self" es básicamente el objeto que se crea de la propia clase, por eso lo llaman así, se entendería mejor creo yo si se usase la palabra object de este modo "object.ruedas = ruedas")

Ahora creo el objeto que lo llamo coche_1 desde la clase vehiculo así:

coche_1 = vehiculo()

Si quiero saber cuantas ruedas tienen ese coche puedo imprimir las ruedas que tiene el (objeto), saldrán 4:

>>>print (coche_1.ruedas)
4

si quiero saber si el coche tiene ABS, (saldrá True):

>>>print (coche.ABS)
True

Porque es lo que he definido en la clase vehiculo usando la funcion __init__ Es decir que los objetos se __init__cializarán (se crearán) con esos parámetros.

Ahora bien, si ahora quiero crear un coche que no tengoa ABS (porque quiero ver como se desliza conduciendo con lluvia frenando en una curva) entonces creo un coche (objeto) y quito el ABS así:

coche_2 = vehiculo(ABS = False)

Con lo que ahora tengo un coche sin ABS (recuerda que si no digo nada, por defecto el nuevo coche objeto tendrá ABS) y lo compruebo así:

>>> print(coche_2.ABS)
False

Si quiero crear un coche con motor eléctrico en vez del defecto que es diesel, lo puedo creo así:

coche_3 = vehiculo(motor = "electrico")

Y al imprimir esa característica de ese coche me saldra que es eléctrico:

>>> print(coche_3.motor)
electrico

Me ha llevado una semana entender esta chorrada porque no la explican en ninguna parte para mortales como yo, debo de ser que soy muy tonto o los demás muy listos, no lo sé.

Llamar "def" y no "fun" cuando creas una función, no facilita las cosas (será que "fun" es divertido y los programadores tienen que parecer serios).

Lo del "self", también es otra coña para parecer que inicializar un objeto en una clase es algo que solo lo entienda la gente que trabaja en la NASA. Si en vez de llamarlo "self" lo hubiesen llamado "object", sería auto-explicativo y lo entendería todo el mundo sin tener que explicarlo...

En fin, cuando acabe de entenderlo todo, escribiré algún libro o algún tutorial al que llamaré que explicará cosas simples de modo simple y no respuestas de 14 páginas que no hay dios que entienda.

AMEN

  • 1
    favor de moderar su lenguaje, lo que en una zona es una expresión común para otra puede implicar una grosería, si quieres dejar el título de tu "libro" que escribirás renombralo de otro modo –  Dec 12 '18 at 19:41
  • `self` dentro de la jerga de python hace referencia al método constructor dentro de la propia clase y como `self` al español es yo o yo mismo pues tiene lógica es un método de mi mismo(la clase en cuestión) –  Dec 12 '18 at 19:43
  • `self` no es una palabra reservada ni especial, nadie te obliga a usarla. Si te gusta más puedes declarar los métodos poniendo `def __init__(objeto, a, b, c)` y luego dentro `objeto.a=2`, etc. Funcionará perfectamente pues lo único que espera python es que el primer parámetro (se llame como se llame) contenga una instancia de ese objeto. Ahora bien, aunque _puedes_ llamarlo como quieras, apartarse de las prácticas comunes que siguen el resto de programadores puede hacer que tu código sea más difícil de entender para los demás (aunque para tí sea más sencillo) – abulafia Dec 12 '18 at 21:46
  • Por otra parte, lo que has explicado sobre los parámetros con valores por defecto, aunque está muy bien traido, no es nada específico de `__init__()`. _Todas_ las funciones python pueden tener parámetros por defecto. `__init__()` simplemente es una función más y lo que la diferencia de otras es que **python llama automáticamente a esa función** después de haber creado el objeto, pasándole como parámetros los que tú hayas puesto al instanciar la clase. Y si no le pasas todos los parámetros, los que faltan recibirán el valor por defecto, como en cualquier otra función. – abulafia Dec 12 '18 at 21:50