33 | Spoilers
34 |
35 | La instanciación es fundamental en la programación orientada a objetos porque permite a los desarrolladores crear múltiples objetos únicos a partir de una sola definición de clase. Esto facilita la reutilización de código y la organización del programa, permitiendo que los objetos interactúen entre sí. Cada objeto puede contener sus propios datos, lo que permite representar entidades del mundo real dentro del código de manera aislada y modular.
36 |
37 |
38 |
39 | ## Clases en Python
40 | Una clase se define con la palabra clave `class` seguida de un nombre y dos puntos. La clase más simple en Python no necesita nada más que la palabra clave pass para representar un bloque de código vacío.
41 |
42 | **Pro tip:** El nombre de las clases debe ir en `CamelCase`, con la primera palabra capitalizada.
43 |
44 | ```python
45 | class MyFirstClass:
46 | pass
47 | ```
48 |
49 | ```python
50 | class Point:
51 | pass
52 | ```
53 |
54 | **Dato:** `pass` permite definir un bloque de código sin funcionalidad, sin tener el error de que se tiene un bloque no definido.
55 |
56 |
57 | **Ejercicio:** Probar esto y ver qué pasa...
58 |
59 | ```python
60 | class Point:
61 | pass
62 |
63 | point = Point()
64 | print(point)
65 | ```
66 |
67 | ### Atributos
68 | En Python, los atributos de una clase son variables asociadas a esta, que definen las **características** de los objetos que se crean a partir de ella.
69 |
70 | #### Atributos de clase
71 | Son variables asociadas a la clase misma, no a las instancias de la clase. Estos atributos son compartidos por todas las instancias de la clase. Se definen directamente en el cuerpo de la clase, fuera de cualquier método.
72 |
73 | ```mermaid
74 | classDiagram
75 | class Point {
76 | +str definition
77 | }
78 | ```
79 |
80 | ```python
81 | class Point:
82 | definition: str = "Entidad geometrica abstracta que representa una ubicación en un espacio."
83 |
84 | point = Point()
85 | print(point.definition)
86 | ```
87 |
88 | #### Atributos de instancia
89 | Son variables específicas de cada instancia de una clase. Cada objeto creado a partir de la clase puede tener valores diferentes para estos atributos. Se definen comúnmente dentro del método `__init__`, utilizando el prefijo `self` para indicar que son atributos de instancia.
90 |
91 | ```python
92 | class Point:
93 | definition: str = "Entidad geometrica abstracta que representa una ubicación en un espacio."
94 | def __init__(self, x=0, y=0):
95 | self.x = x
96 | self.y = y
97 |
98 | point = Point()
99 | print(point.definition, point.x, point.y)
100 | ```
101 |
102 | ```mermaid
103 | classDiagram
104 | class Point {
105 | +str definition
106 | +int x
107 | +int y
108 | +__init__(x, y)
109 | }
110 | ```
111 |
112 | ### Inicializacion (`__init__`) y el si mismo (`self`)
113 |
114 | Instanciar un objeto implica llamar a la clase como si fuera una función, lo que a menudo involucra pasar valores iniciales a su método constructor `__init__`. Este método constructor **inicializa** los nuevos objetos, realizando la configuración inicial, de atributos y métodos.
115 |
116 | - `__init__` es el método de inicialización de un objeto recién creado. Se utiliza para inicializar los atributos del objeto, es decir, para darles un estado inicial.
117 | - No devuelve un valor; su propósito es inicializar la instancia: `def __init__(self)->None:`.
118 | - Se denomina *dunder* init, porque double underscore, so doble __, pretty neat.
119 |
120 | En Python, `self` es un término que se utiliza para referirse a la instancia actual de la clase. En otras palabras, `self` representa al objeto específico que está ejecutando. Este término no es una palabra reservada de Python pero es una convención fuertemente arraigada (como todo en Python).
121 |
122 | Cuando se define un método en una clase, el primer argumento del método se refiere a la instancia en la que el método está siendo llamado. Por convención, este primer argumento es `self`. Esto permite acceder a los atributos y métodos del objeto desde dentro de la clase.
123 |
124 | Algunos se preguntarán....y esto qué...pues es magia pura, porque después del inicializador se puede compartir todo el objeto con el resto de la clase...y si aún no le ven utilidad, si esto no existiera, para cada método dentro de la clase, habría que estar pasando variables (que representan atributos) a través de argumentos a todos los métodos de la clase, lo cual es super largo y engorroso. La magia del `self`, es esa, proveer autoconciencia, saber qué soy, qué tengo, y qué puedo hacer en cada momento....es **M A G I A**.
125 |
126 | **Disclaimer:** En concepto de `self` sería más o menos el equivalente de `this` en Java.
127 |
128 | #### ¿Por qué es necesario `self`?
129 | `self` es necesario para diferenciar entre atributos y métodos de la instancia y variables locales o globales. Permite a un objeto referenciar sus propios atributos y métodos dentro de la clase. Sin `self`, no tendríamos una forma de acceder a estos datos desde dentro de los métodos de la clase.
130 |
131 | #### ¿Cómo funciona `self`?
132 | - Cuando se crea una instancia de una clase, Python automáticamente pasa la instancia recién creada al método `__init__` como el primer argumento. Este argumento se recibe en el parámetro `self`.
133 | - Para cualquier método de instancia, el objeto sobre el cual se llama el método se pasa automáticamente como el primer argumento, y se recibe en `self`.
134 |
135 | ### Métodos
136 | Una clase no solo tiene atributos (características) sino también métodos (comportamientos/acciones), estos métodos se definen como funciones dentro de la estructura de la clase. Ya se vió el primer método importante, el inicializador, existen unos cuantos más, pero de momento cualquier funcionalidad que se le quiera atribuir a la clase, es tan simple como agregar más métodos en su interior.
137 |
138 | ```mermaid
139 | classDiagram
140 | class Point {
141 | +str definition
142 | +int x
143 | +int y
144 | +__init__(x, y)
145 | +reset()
146 | +move()
147 | }
148 | ```
149 |
150 |
151 | ```python
152 | class Point:
153 | definition: str = "Entidad geometrica abstracta que representa una ubicación en un espacio."
154 | def __init__(self, x=0, y=0):
155 | self.x = x
156 | self.y = y
157 | def move(self, new_x, new_y):
158 | self.x = new_x
159 | self.y = new_y
160 | def reset(self):
161 | self.x = 0
162 | self.y = 0
163 |
164 | point = Point(x=1, y=1)
165 | print(point.definition, point.x, point.y)
166 | point.move(new_x=2, new_y=2)
167 | print(point.x, point.y)
168 | point.reset()
169 | print(point.x, point.y)
170 | ```
171 |
172 | #### Objetos como argumentos...
173 | Como se vio todo en Python son objetos, de manera que los métodos de una clase y la clase en sí pueden recibir argumentos tipo objeto.
174 |
175 | ```mermaid
176 | classDiagram
177 | class Point {
178 | +str definition
179 | +int x
180 | +int y
181 | +__init__(x, y)
182 | +reset()
183 | +move(x, y)
184 | +compute_distance(point)
185 | }
186 | ```
187 |
188 | ```python
189 | class Point:
190 | definition: str = "Entidad geometrica abstracta que representa una ubicación en un espacio."
191 | def __init__(self, x: float=0, y: float=0):
192 | self.x = x
193 | self.y = y
194 | def move(self, new_x: float, new_y: float):
195 | self.x = new_x
196 | self.y = new_y
197 | def reset(self):
198 | self.x = 0
199 | self.y = 0
200 | def compute_distance(self, point: "Point")-> float:
201 | distance = ((self.x - point.x)**2+(self.y - point.y)**2)**(0.5)
202 | return distance
203 |
204 | first_point = Point(x=1, y=1)
205 | second_point = Point(x=2, y=2)
206 | # should be root of 2 (?)
207 | print(first_point.compute_distance(second_point))
208 | ```
209 |
210 | **Ejercicio:**
211 | Defina una clase siguiendo la siguiente estructura:
212 | ```mermaid
213 | classDiagram
214 | class Person {
215 | +str specie
216 | +int age
217 | +str name
218 | +greet()
219 | +is_older_than(Person)
220 | }
221 | ```
222 | - *specie*: Class attribute
223 | - *age*, *name*: Instance attributes
224 | - greet(): should print->