Classes combinan datos con comportamiento.
Las clases se usan para crear copias o instances de datos y comportamiento agrupados, a los que comúnmente se llama objects.
Los objetos pueden representar entidades del mundo real (como autos o gatos) o conceptos más abstractos (como números enteros, vehículos o mamíferos).
Las clases son fundamentales para un enfoque de programación orientada a objetos (OOP), que le pide a quien programa pensar en modelar un problema como uno o más objetos que interactúan entre sí, mantienen estado y actúan sobre los datos.
Las clases son las definiciones de nuevos tipos de objeto, y a partir de ellas se crean nuevas instances de objetos.
A menudo agrupan datos (conocidos como fields, attributes, properties, data members o variables) con código o funciones (conocidas como methods) que operan sobre esos datos.
En este sentido, una clase es un plano o un conjunto de instrucciones a partir del cual se pueden construir y usar muchos objetos de un tipo similar.
Un programa complejo puede tener muchas clases, y cada una construye muchas variedades diferentes de objetos.
El proceso de construir y personalizar un objeto a partir de una clase se conoce como instantiation (o crear una instancia de la clase).
La definición de una clase en Python es sencilla:
class MyClass:
# Class body goes here
Los campos de clase (también conocidos como propiedades, atributos, miembros de datos o variables) se pueden agregar al cuerpo de la clase.
class MyClass:
number = 5
string = "Hello!"
Se puede crear una instancia (objeto) de MyClass y vincularla a un nombre:
>>> new_object = MyClass()
# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>
Los Class attributes se comparten entre todos los objetos (o instancias) creados a partir de una clase, y se puede acceder a ellos mediante la dot notation: un . que se coloca después del nombre del objeto y antes del nombre del atributo:
>>> new_object = MyClass()
# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5
# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'
# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()
>>> second_new_object
<__main__.MyClass at 0x15ad99970>
# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True
Los atributos de clase se definen en el cuerpo de la clase misma, antes que cualquier otro método. Son propiedad de la clase, lo que permite compartirlos entre las instancias. Como estos atributos son compartidos por todas las instancias de la clase, se puede acceder a su valor y manipularlo directamente desde la clase. Cambiar el valor de los atributos de clase altera el valor para todos los objetos instanciados a partir de la clase:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
# Accessing a class attribute from an object.
>>> obj_two.number
5
# Accessing the class attribute from the class itself.
>>> MyClass.number
5
# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27
# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27
>>> obj_two.number
27
Tener un montón de objetos con datos sincronizados en todo momento no es especialmente útil.
Por suerte, los objetos creados a partir de una clase se pueden personalizar con sus propios instance attributes (o propiedades, variables o campos de instancia).
Como su nombre lo indica, los atributos de instancia son únicos para cada objeto y se pueden modificar de forma independiente.
__init__()
El especial «método dunder» (abreviatura de «método de doble guion bajo») __init__() se usa para personalizar las instancias de clase y puede usarse para inicializar atributos o propiedades de instancia de los objetos.
Por su papel en la inicialización de atributos de instancia, también se le llama a __init__() un class constructor o initializer.
__init__() toma un parámetro obligatorio llamado self, que hace referencia al objeto recién iniciado o creado.
Los datos para los atributos o propiedades de instancia se pueden pasar como argumentos de __init__(), después del parámetro self.
A continuación, MyClass ahora tiene atributos de instancia llamados location_x y location_y.
Como puedes ver, los dos atributos se han asignado al primer y segundo índice del argumento location (una tupla) que se pasó a __init__().
Los atributos location_x y location_y de una instancia de clase ahora se inicializarán cuando instancies la clase y se cree un objeto:
class MyClass:
# These are class attributes, variables, or fields.
number = 5
string = "Hello!"
# This is the class "constructor", with a "location" parameter that is a tuple.
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
# Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))
# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))
# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1
>>> new_object_two.location_x
-8
Fíjate que solo necesitas pasar un argumento al inicializar MyClass arriba: Python se encarga de pasar self cuando se llama a la clase.
Otra forma de crear una variable de instancia es simplemente acceder a una variable de clase a través de un objeto y cambiarla por otra cosa:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'
>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'
>>> obj_one.string
'Hello!'
Ahora, observa qué pasa cuando cambia la variable de clase:
>>> MyClass.string = "World!"
>>> obj_two.string
# obj_two.string has not changed
'Hi!'
>>> obj_one.string
# obj_one.string changed
'World!'
El atributo string ahora es una variable de instancia en el caso de obj_two, pero sigue siendo una variable de clase en obj_one.
Esto también se puede hacer desde dentro de la clase:
class Demo:
new_var = 3
def add_two(self):
self.new_var += 2
En el momento en que se llama a <object>.add_two() y se lee self.new_var += 2, new_var pasa de ser una variable de clase a una variable de instancia del mismo nombre.
Esto puede ser útil durante la inicialización, cuando todas las instancias de una clase necesiten que uno o más atributos empiecen con el mismo valor.
Sin embargo, la variable de instancia entonces oculta la variable de clase, lo que hace que esta sea inaccesible desde la instancia donde queda oculta.
Dada esta situación, puede ser más seguro y claro establecer los atributos de instancia desde el método __init__() como self.<attribute>.
Un method es una function que está vinculada a la clase misma (conocido como método de clase, que se tratará en profundidad en un ejercicio posterior) o a una instancia de la clase (objeto).
Los métodos que operan sobre un objeto o instancia se deben definir con self como primer parámetro.
Luego puedes definir el resto de los parámetros como lo harías para una función «normal» o no vinculada.
Los métodos que operan sobre una clase se deben definir con el decorador @classmethod y (por convención) con cls como primer parámetro.
Los métodos de clase se llaman directamente sobre una clase sin crear primero un objeto a partir de ella.
class MyClass:
number = 5
string = "Hello!"
# Class constructor.
def __init__(self, location):
# Instance properties
self.location_x = location[0]
self.location_y = location[1]
# Class method. Uses the @classmethod decorator, and cls as the first parameter.
# Can be called without first making an instance of the class.
@classmethod
def change_string(cls, new_string):
#Class attributes are referred to with cls.
cls.string = new_string
return cls.string
# Instance method. Note "self" as first parameter.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
Al igual que con el acceso a atributos, llamar a un método es tan simple como poner un . después del nombre del objeto y antes del nombre del método.
El método llamado no necesita una copia del objeto como primer parámetro: Python completa self automáticamente:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)
# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)
Se puede acceder a los atributos de clase desde dentro de los métodos de instancia de la misma manera en que se accede a ellos fuera de la clase:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5
>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6
pass
En ejercicios de conceptos anteriores y en los stubs de ejercicios de práctica, habrás visto la palabra clave pass usada dentro del cuerpo de funciones en lugar de código real.
La palabra clave pass es un marcador de posición sintácticamente válido: evita que Python arroje un error de sintaxis por una función o definición de clase vacía.
Básicamente, es una forma de decirle al intérprete de Python: «¡No te preocupes! Sí voy a poner código aquí en algún momento, solo que todavía no lo he hecho.»
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
# Stubbing or place-holding the body of this method.
pass