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.legs
es un atributo llamado legs
del 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 colour
para el doghood en sí, es un concepto abstracto. Hay atributos que tienen sentido en las clases. Por ejemplo, population_size
es 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 colour
o legs
superiores.
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 other
que 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 colour
que "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 colour
variables, en diferentes perros . Entonces, fido.colour
es una variable, pero spot.colour
es 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 self
y obtendrá el valor del objeto delante del punto. Así, dentro del __init__
(constructor) del Perro , self
estará lo que resulte ser el nuevo Perro; dentro MyInteger
de add
, self
se vinculará al objeto en la variable three
. Así,three.value
será la misma variable fuera de add
, como self.value
dentro de add
.
Si digo the_mangy_one = fido
, comenzaré a referirme al objeto conocido como fido
con otro nombre. De ahora en adelante, fido.colour
es 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. colour
por sí misma es una variable aleatoria, podría contener cualquier cosa. fido.colour
o self.colour
es 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 fido
realidad no es un objeto. Es una variable, que actualmente contiene un objeto, al igual que cuando dice x = 5
, x
es 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í, census
es un atributo de Cat
clase 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 add
o __init__
) es describir cómo convertir el objeto en una cadena, como cuando lo imprime.