summaryrefslogtreecommitdiff
path: root/es/05_oop.md
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2020-07-22 20:01:38 +0200
committerEkaitz Zarraga <ekaitz@elenq.tech>2020-07-22 20:01:38 +0200
commitf2df77bce2c03910aa3c031405e43b14333bac8e (patch)
tree233e10c2073d2d1ca5ef747b14ffe6766bb80a6a /es/05_oop.md
parentc3a0a74c059ac7790008e7325567317435d0c7f8 (diff)
Corrections everywhere
Diffstat (limited to 'es/05_oop.md')
-rw-r--r--es/05_oop.md293
1 files changed, 146 insertions, 147 deletions
diff --git a/es/05_oop.md b/es/05_oop.md
index c5396b3..e45fdfd 100644
--- a/es/05_oop.md
+++ b/es/05_oop.md
@@ -1,22 +1,19 @@
-# Programación Orientada a Objetos
+# Orientación a Objetos
La *programación orientada a objetos* u *object oriented programming* (OOP) es
un paradigma de programación que envuelve python de pies a cabeza. A pesar de
que python se define como un lenguaje de programación multiparadigma, la
-programación orientada a objetos es el paradigma principal de éste. A pesar de
-que varias de las características que tratamos en el apartado anterior se
-corresponden más con un lenguaje de programación funcional, en python **todo**
+programación orientada a objetos es el paradigma principal de éste. Aunque
+varias de las características que tratamos en el apartado anterior se
+corresponden más con un lenguaje de programación funcional, en python todo
(o casi todo) es una clase.
-Python usa una programación orientada a objetos basada en clases[^class], a
-diferencia de otros lenguajes como JavaScript, donde la orientación a objetos
-está basada en prototipos[^proto]. No es el objetivo de este documento el de
-contarte cuales son las diferencias entre ambas, pero es interesante que sepas
-de su existencia, ya que es una de las pocas diferencias que existen entre
-estos dos lenguajes de amplio uso en la actualidad.
-
-[^class]: <https://en.wikipedia.org/wiki/Class-based_programming>
-[^proto]: <https://en.wikipedia.org/wiki/Prototype-based_programming>
+Python usa una programación orientada a objetos basada en clases, a diferencia
+de otros lenguajes como JavaScript, donde la orientación a objetos está basada
+en prototipos. No es el objetivo de este documento el de contarte cuales son
+las diferencias entre ambas, pero es interesante que sepas de su existencia, ya
+que es una de las pocas diferencias que existen entre estos dos lenguajes de
+amplio uso en la actualidad.
## Programación basada en clases
@@ -26,7 +23,7 @@ programación basada en clases.
Los objetos, *objects*, son entidades que encapsulan un estado, un
comportamiento y una identidad capaz de separarlos de otras entidades. Una
-clase, *class*, es la definición de estos objetos.
+clase, *class*, es la descripción de estos objetos.
Saliendo de la definición filosófica y trayéndola a un nivel de andar por casa,
puedes aclararte sabiendo que las clases son la definición enciclopédica de
@@ -68,45 +65,43 @@ La programación basada en clases se basa en tres conceptos fundamentales que
repasaremos aquí de forma rápida para razonar el interés de la programación
orientada a objetos sobre otros paradigmas.
-La **encapsulación**[^encapsulation] trata de crear datos con sus métodos
-propios para alterarlos de modo que restrinjan el acceso directo al contenido
-de estos datos con el fin de asegurar una coherencia o robustez interna. Puedes
+La **encapsulación** trata de crear datos que restrinjan el acceso directo su
+contenido con el fin de asegurar una coherencia o robustez interna. Puedes
entender esto como una forma de esconder información o como mi profesor de
-programación II en la universidad solía decir: «Las patatas se pelan en la
+Programación II en la universidad solía decir: «Las patatas se pelan en la
cocina del restaurante, no en el comedor». La utilidad de la encapsulación es
-la de aislar secciones del programa para tener total control sobre su
-contenido gracias a tener total control de la vía de acceso a estos datos. A
-nivel práctico este concepto puede usarse para, por ejemplo, obligar a que un
-objeto sólo pueda ser alterado en incrementos controlados en lugar de poder
-pisarse con un valor arbitrario.
-
-La **herencia**[^inheritance] es un truco para reutilizar código de forma
-agresiva que, casualmente, sirve como una buena forma de razonar. Aporta la
-posibilidad de crear nuevas *clases* a partir de clases ya existentes.
-Volviendo a la simplificación anterior, si una clase es una definición
-enciclopédica de un concepto, como un perro, puede estar basada en otra
-descripción para evitar contar todo lo relacionado con ella. En el caso del
-perro, el perro es un animal. Animal podría ser otra clase definida previamente
-de la que el perro heredara y recibiera gran parte de su descripción genérica
-para sólo cubrir puntos que necesite especificar como el tamaño, la forma, el
-tipo de animal, el comportamiento concreto, etc. Existe la posibilidad de hacer
-herencias múltiples también ya que algunos conceptos pueden describirse en dos
+la de aislar secciones del programa para tener total control sobre su contenido
+gracias a tener total control de la vía de acceso a estos datos. A nivel
+práctico este concepto puede usarse para, por ejemplo, obligar a que un objeto
+sólo pueda ser alterado en incrementos controlados en lugar de poder pisarse
+con un valor arbitrario.
+
+La **herencia** es un truco para reutilizar código de forma agresiva que,
+casualmente, sirve como una buena forma de razonar. Aporta la posibilidad de
+crear nuevas *clases* a partir de clases ya existentes. Volviendo a la
+simplificación anterior, si una clase es una definición enciclopédica de un
+concepto, como un perro, puede estar basada en otra descripción para evitar
+contar todo lo relacionado con ella. En el caso del perro, el perro es un
+animal. Animal podría ser otra clase definida previamente de la que el perro
+heredara y recibiera gran parte de su descripción genérica para sólo cubrir
+puntos que necesite especificar como el tamaño, la forma, el tipo de animal, el
+comportamiento concreto, etc. Existe la posibilidad de hacer herencias
+múltiples también ya que algunos conceptos pueden describirse en dos
superclases distintas: un perro es un animal (vive, muere, se alimenta, se
reproduce) y también es terrestre (camina sobre una superficie, etc). Ambos
conceptos son independientes: los coches también son terrestres pero no son
animales y los peces también son animales pero no terrestres.
-Y, finalmente, el **polimorfismo**[^polymorphism]. La propia etimología de la
-palabra define con bastante precisión el concepto, pero aplicarlo a la
-programación orientada a objetos no es tan evidente. Existen varios tipos de
-polimorfismo pero el más sencillo es entender el *subtyping*[^subtyping]. Una
-vez lo comprendas el resto será evidente. Si volvemos al ejemplo del perro,
-para ciertos comportamientos, nos da igual que tratemos de perros, de peces o
-de pájaros, todos son animales y todos los animales se comportan de la misma
-forma. Es decir, todas las subclases señaladas comparten el comportamiento de
-la superclase animal. Si esto es cierto, puede suponerse que en cualquier caso
-en el que se espere un objeto de la clase animal es seguro usar una subclase de
-ésta.
+Y, finalmente, el **polimorfismo**. La propia etimología de la palabra define
+con bastante precisión el concepto, pero aplicarlo a la programación orientada
+a objetos no es tan evidente. Existen varios tipos de polimorfismo pero el más
+sencillo es entender el *subtyping*. Una vez lo comprendas el resto será
+evidente. Si volvemos al ejemplo del perro, para ciertos comportamientos, nos
+da igual que tratemos de perros, de peces o de pájaros, todos son animales y
+todos los animales se comportan de la misma forma. Es decir, todas las
+subclases señaladas comparten el comportamiento de la superclase animal. Si
+esto es cierto, puede suponerse que en cualquier caso en el que se espere un
+objeto de la clase animal es seguro usar una subclase de ésta.
Visto desde otra perspectiva, las subclases comparten comportamiento porque
reutilizan las funciones de la clase principal o las redefinen (*herencia*),
@@ -123,15 +118,10 @@ cualquier tipo de número (integer, float, complex, etc.) de la misma manera, ya
que todos son números, al fin y al cabo.
Entender estos conceptos a nivel intuitivo, sin necesidad de entrar en los
-detalles específicos de cada uno, es interesante para cualquier programador y
-facilita de forma radical la comprensión de muchas de las decisiones de diseño
-tomadas en python y en proyectos relacionados aunque también, por supuesto, de
-otros lenguajes y herramientas.
-
-[^encapsulation]: <https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)>
-[^inheritance]: <https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)>
-[^polymorphism]: <https://en.wikipedia.org/wiki/Polymorphism_(computer_science)>
-[^subtyping]: <https://en.wikipedia.org/wiki/Subtyping>
+detalles específicos de cada uno, es interesante para a la hora de diseñar
+programas y facilita de forma radical la comprensión de muchas de las
+decisiones de diseño tomadas en python y en proyectos relacionados aunque
+también, por supuesto, de otros lenguajes y herramientas.
## Sintaxis
@@ -144,7 +134,7 @@ perro (`Dog`) `bobby` ladre (`bark`) que cuando lo haga el perro `beltza`.
Los métodos describen la *funcionalidad* asociada a los perros en general, pero
además, la función `bark` los describe en particular, haciendo que cada perro
-tome su nombre (`name`), una propiedad o dicho de otro modo, su *estado*.
+tome su nombre (`name`), una propiedad, es decir: su *estado*.
``` python
class Dog:
@@ -154,8 +144,8 @@ class Dog:
def bark(self):
print("Woof! My name is " + self.name)
-bobby = Dog("Bobby") # New Dog called Bobby
-beltza = Dog("Beltza") # New Dog called Beltza
+bobby = Dog("Bobby") # Nuevo Dog llamado "Bobby"
+beltza = Dog("Beltza") # Nuevo Dog llamado "Beltza"
bobby.name # Bobby
beltza.name # Beltza
@@ -163,8 +153,8 @@ beltza.name # Beltza
bobby.type # canine
beltza.type # canine
-bobby.bark() # Prints "Woof! My name is Bobby"
-beltza.bark() # Prints "Woof! My name is Beltza"
+bobby.bark() # "Woof! My name is Bobby"
+beltza.bark() # "Woof! My name is Beltza"
```
### Creación de objetos
@@ -232,13 +222,13 @@ métodos necesarios para hacerlo. Lo que ocurre es que cualquier subclase de
`Terrestrial` tiene la ocasión moverse (`move`) a su manera: en el caso del
perro, caminando.
-> NOTA: La herencia es interesante, pero tampoco debe caerse en la psicosis de
+> La herencia es interesante, pero tampoco debe caerse en la psicosis de
> añadir demasiadas superclases. En ocasiones las superclases son necesarias,
> sobre todo cuando aprovechar el polimorfismo facilita el trabajo, pero
> usarlas de forma agresiva genera código extremadamente complejo sin razón.
-### Métodos de objeto o funciones de clase: `self`
+### Métodos de objeto o funciones de clase
Los métodos reciben un parámetro de entrada llamado `self` que no se utiliza a
la hora de llamarlos: al hacer `bobby.bark()` no se introduce ningún argumento
@@ -267,9 +257,8 @@ Efectivamente, python introduce un argumento de entrada en los métodos, el
argumento de entrada que por convención se suele llamar `self`. Este parámetro
es el propio `bobby` en este caso.
-> NOTA: Por convención se le denomina `self`. Tú le puedes llamar como te
-> apetezca pero, si pretendes que otros programadores te entiendan, mejor
-> `self`.
+> Por convención se le denomina `self`. Tú le puedes llamar como te apetezca
+> pero, si pretendes que los demás te entiendan, mejor `self`.
Para explicar por qué ocurre esto es necesario diferenciar bien entre clase y
objeto. Tal y como hemos hecho antes con las definiciones enciclopédicas
@@ -290,7 +279,7 @@ Ahora bien, para python las funciones de clase y los métodos (de los objetos,
si no no se llamarían métodos), se implementan de la misma manera. Para la
clase ambas cosas son lo mismo. Sin embargo, el comportamiento del operador
punto (`.`), que dice a quién pertenece la función o método, es diferente si el
-valor de la izquierda es una clase o un objeto. Introduciendo en el segundo
+valor de la izquierda es una clase o un objeto, introduciendo en el segundo
caso el propio objeto como primer parámetro de entrada, el `self` del que
hablamos, para que la clase sepa qué objeto tiene que alterar. Este es el
mecanismo de la *identidad* del que antes hablamos y no llegamos a definir en
@@ -319,7 +308,7 @@ forma distinta. A veces como función (*function*) y otras veces como método
<class 'method'>
```
-> NOTA: También te habrás fijado, y si no lo has hecho es momento de hacerlo,
+> También te habrás fijado, y si no lo has hecho es momento de hacerlo,
> que los nombres de las clases empiezan por mayúscula en los ejemplos (`Dog`)
> mientras que los objetos comienzan en minúscula (`bobby`). Se trata de otra
> convención ampliamente utilizada para saber diferenciar entre uno y otro de
@@ -348,23 +337,22 @@ class Dog:
`type` es lo que se conoce como una *variable de clase* (*class variable*).
-> NOTA: En este documento se ha evitado de forma premeditada usar la palabra
-> *variable* para referirse a los valores y sus referencias con la intención de
-> marcar la diferencia entre ambos conceptos. En este apartado, sin embargo, a
-> pesar de que se siga tratando de una referencia, se usa el nombre *class
-> variable* porque es como se le llama en la documentación[^class_var] y así
-> será más fácil que lo encuentres si en algún momento necesitas buscar
-> información al respecto. De esto ya hemos discutido en el capítulo sobre
-> datos, donde decimos que *todo es una referencia*.
+> En este documento se ha evitado de forma premeditada usar la palabra
+> *variable* para referirse a los valores y sus referencias con la intención
+> de marcar la diferencia entre ambos conceptos. En este apartado, sin
+> embargo, a pesar de que se siga tratando de una referencia, se usa el nombre
+> *class variable* porque es como se le llama en la documentación y así será
+> más fácil que lo encuentres si en algún momento necesitas buscar información
+> al respecto. De esto ya hemos discutido en el capítulo sobre datos, donde
+> decimos que *todo es una referencia*.
-[^class_var]: <https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables>
Previamente hemos hablado de que los objetos pueden tener propiedades
asociadas, y cada objeto tendrá las suyas. Es decir, que cada instancia de la
clase puede tener sus propias propiedades independientes. El caso que tratamos
en este momento es el contrario, el `type` es un valor que comparten **todas**
las instancias de `Dog`. Cualquier cambio en esos valores los verán todos los
-objetos de la clase, así que hay que ser cuidadoso.
+objetos de la clase, así que hay que tener cuidado.
El acceso es idéntico al que ocurriría en un valor asociado al objeto, como en
el caso `name` del ejemplo, pero en este caso observas que en su declaración en
@@ -375,7 +363,7 @@ A parte de poder acceder a través de los objetos de la clase, es posible
acceder directamente desde la clase a través de su nombre, como a la hora de
acceder a las funciones de clase: `Dog.type` resultaría en `"canine"`.
-> NOTA: Si en algún caso python viera que un objeto tiene propiedades y
+> Si en algún caso python viera que un objeto tiene propiedades y
> variables de clase definidas con el mismo nombre, cosa que no debería ocurrir
> a menudo, tendrán preferencia las propiedades.
@@ -401,7 +389,7 @@ entra por accidente a las secciones que empiezan por `__`. Añade
`_nombredeclase` al inicio de los campos, transformando su nombre final y
dificultando el acceso por accidente.
-Ese acceso accidental no sólo es para que el programador no acceda, ya que, si
+Ese acceso accidental no sólo es para que quien programa no acceda, ya que, si
se esfuerza la suficiente, va a poder hacerlo de igual modo, si no para que el
propio python no acceda al campo que no corresponde. El hecho de añadir el
nombre de la clase al campo crea una brecha en la herencia, haciendo que los
@@ -426,11 +414,11 @@ método que python tiene para aportar esta funcionalidad.
Es interesante añadir, por otro lado, que python es un lenguaje de programación
muy dinámico por lo que la propia definición de las clases, y muchas cosas más,
puede alterarse una vez creadas. Esto significa que el hecho de ocultar campos
-no es más que un acuerdo tácito entre programadores porque, si quisieran,
-podrían definir todo de nuevo. Trucos como este sirven para que el programador
-sea consciente de que está haciendo cosas que se supone que no debería hacer.
-Cuando programes en python, tómate esto como pistas que te indican cómo se
-supone que deberías estar usando las clases.
+no es más que un acuerdo tácito entre quienes programan porque, si quisieran,
+podrían definir todo de nuevo. Trucos como este sirven para que seamos
+conscientes de que estamos haciendo cosas que se supone que no deberíamos
+hacer. Cuando programes en python, tómate esto como pistas que te indican cómo
+se supone que deberías estar usando las clases.
### Acceso a la superclase
@@ -449,11 +437,11 @@ class Clase( SuperClase ):
# `metodo` de `SuperClase`
```
-> NOTA: `super` busca la clase previa por preferencia, si usas herencias
+> `super` busca la clase previa por preferencia, si usas herencias
> múltiples y pisas los campos puede complicarse.
-## Interfaces estándar: Duck Typing
+## Duck Typing
Una de las razones principales para usar programación orientada a objetos es
que, si se eligen los métodos con precisión, pueden crearse estructuras de
@@ -475,28 +463,30 @@ de crear nuevas definiciones que soporten la misma interfaz.
En otros lenguajes de programación, Java, por ejemplo, existe el concepto
*interfaz* que serían una especie pequeñas clases que definen qué funciones
-debe cumplir una clase para que cumpla la interfaz. A la hora de crear las
-clases se les puede indicar qué interfaces implementan y el lenguaje se encarga
-de asegurarse de que el programador ha hecho todo como debe.
+debe cumplir una clase para ser compatible con la interfaz. A la hora de crear
+las clases se les puede indicar qué interfaces implementan y el lenguaje se
+encarga de asegurarse de que quien programa ha hecho todo como debe.
El dinamismo de python hace que esto sea mucho más flexible. Debido a que
python no hace casi comprobaciones antes de ejecutarse, necesita un método para
-mucho más directo. Para python, *si anda como un pato, vuela como un pato y
-nada como un pato: es un pato*.
+mucho más directo. Python aplica lo que se conoce como *duck typing* (el tipado
+del pato), es decir, *si anda como un pato, vuela como un pato y nada como un
+pato: es un pato*.
-Para que los objetos creados por el programador puedan comportarse como los que
-el propio sistema aporta, python define unos nombres de métodos especiales
+Para que los objetos creados manualmente puedan comportarse como los que el
+propio sistema aporta, python define unos nombres de métodos especiales
(*special method names*). Estos métodos tienen infinidad de utilidades: que sea
posible utilizarlos como iterable en un `for`, que el sistema pueda cerrarlos
-de forma automática, buscar en ellos usando el operador `in`, etc. Simplemente,
+de forma automática, buscar en ellos usando el operador `in`, que puedan
+sumarse y restarse con los operadores de suma y resta, etc. Simplemente,
el sistema define qué funciones se deben cumplir en cada uno de esos casos y
cuando se encuentre con ellos intentará llamarlas automáticamente. Si el
elemento no dispone de esas funciones lanzará una excepción como la que lanza
cuando intentamos acceder a un método que no existe (que es básicamente lo que
estamos haciendo en este caso).
-En general, python, con el fin de diferenciar claramente qué nombres elige el
-programador y cuales han sido seleccionados por el lenguaje, suele utilizar una
+En general, python, con el fin de diferenciar claramente qué nombres se eligen
+manualmente y cuales han sido seleccionados por el lenguaje, suele utilizar una
convención para la nomenclatura: comienzan y terminan por: `__`
A continuación se describen algunos de los nombres especiales más comunes.
@@ -515,13 +505,13 @@ crear una clase que defina un objeto en el que se puede buscar necesitas que
sea un *buscable*, es decir, que soporte el nombre de método especial que
define ese comportamiento.
-### *Representable*: `__repr__`
+### *Representable*
-Este método sirve para otorgar a python una forma de representar estos objetos.
-Al ejecutar la función `print` o al exponer valores en la REPL (recuerda que la
-P significa print), python trata de visualizarlos.
+Un objeto representable es aquél que puede representarse automáticamente en
+modo texto. Al ejecutar la función `print` o al exponer valores en la REPL
+(recuerda que la P significa print), python trata de visualizarlos.
-La el método `__repr__` se ejecuta justo antes de imprimirse el objeto, de
+El método `__repr__` se ejecuta justo antes de imprimirse el objeto, de
forma automática. La función requiere que se devuelva un elemento de tipo
string, que será el que después se visualice.
@@ -533,7 +523,7 @@ a `bobby` el resultado cambia.
Como se ve en el ejemplo, es interesante tener una buena función de
representación si lo que se pretende es entender el contenido de los objetos.
-> NOTA: Python ya aporta una forma estándar de representar los objetos, si la
+> Python ya aporta una forma estándar de representar los objetos, si la
> función `__repr__` no se define simplemente se usará la forma estándar.
``` python
@@ -544,14 +534,14 @@ representación si lo que se pretende es entender el contenido de los objetos.
>>> bobby
<__main__.Dog object at 0x7fb7fba1b908>
->>> Dog.__repr__ = lambda self: "Dog called: " + self.name
+>>> Dog.__repr__ = lambda self: "Dog called " + self.name
>>> bobby.name = "Bobby"
>>> bobby
-Dog called: Bobby
+Dog called Bobby
>>>
```
-### *Contable*: `__len__`
+### *Contable*
En python se utiliza la función `len` para comprobar la longitud de cualquier
elemento contable. Por ejemplo:
@@ -577,7 +567,7 @@ diccionario, tupla y lista. Como por ejemplo los ya existentes `NamedTuple`,
`OrderedDict` y otros. Los métodos *buscable* e *iterable* también son muy
interesantes para esta labor.
-### *Buscable*: `__contains__`
+### *Buscable*
El método `__contains__` debe devolver `True` o `False` y recibir un argumento
de entrada. Con esto el objeto será capaz de comprobarse con sentencias que
@@ -592,7 +582,7 @@ True
True
```
-### *Hasheable*: `__hash__` y `__eq__`
+### *Hasheable*
Los objetos *hasheables*, pueden convertirse a un valor numérico mediante una
función *hash*. Estas funciones habilitan la existencia de los diccionarios,
@@ -614,7 +604,7 @@ pueden compararse.
Los objetos básicos de python son *hasheables*.
-### *Iterable*: `__next__` e `__iter__`
+### *Iterable*
Estos métodos permiten crear objetos con los que es posible iterar en bucles
`for` y otras estructuras. Por ejemplo, los archivos de texto en python
@@ -691,7 +681,7 @@ Así que, si necesitas una clase con capacidad para iterarse sobre ella, puedes
crear un pequeño iterable que soporte el método `__next__` y devolver una
instancia nueva de éste en el método `__iter__`.
-### *Creable*: `__init__`
+### *Inicializable*
El método `__init__` es uno de los más usados e interesantes de esta lista, esa
es la razón por la que ha aparecido en más de una ocasión durante este
@@ -709,9 +699,8 @@ Como se ha visto en algún ejemplo previo, el método `__init__` (es un método,
porque el objeto, aunque vacío, ya está creado) puede recibir argumentos de
entrada adicionales, que serán los que la llamada al nombre de la clase reciba,
a la hora de crear los nuevos objetos. Es muy habitual que el inicializador
-reciba argumentos de entrada, sobre todo argumentos con nombre, para que el
-programador que crea las instancias tenga la opción de inicializar los campos
-que le interesen.
+reciba argumentos de entrada, sobre todo argumentos con nombre, para que quien
+cree las instancias tenga la opción de inicializar los campos que le interesen.
Volviendo a un ejemplo previo:
@@ -730,36 +719,50 @@ El nombre del perro, `"Bobby"` será recibido por `__init__` en el argumento
`name` e insertado al `self` mediante `self.name = name`. De este modo, esa
instancia de `Dog`, `bobby`, tomará el nombre `Bobby`.
-> NOTA: En muchas ocasiones, el método `__init__` inicializa a valores vacíos
+> En muchas ocasiones, el método `__init__` inicializa a valores vacíos
> todas las posibles propiedades del objeto con el fin de que quien lea el
> código de la clase sea capaz de ver cuáles son los campos que se utilizan en
> un primer vistazo. Es una buena práctica listar todos los campos posibles en
> `__init__`, a pesar de que no se necesite inicializarlos aún, con el fin de
> facilitar la lectura.
-> NOTA: Quien tenga experiencia con C++ puede equivocarse pensando que
+> Quien tenga experiencia con C++ puede equivocarse pensando que
> `__init__` es un constructor. Tal y como se ha explicado anteriormente, al
> método `__init__` ya llega un objeto construido. El objetivo de `__init__` es
> inicializar. En python el constructor, que se encarga de crear las instancias
> de la clase, es la función `__new__`.
-> NOTA: Si creas una clase a partir de la herencia y sobreescribes su método
+> Si creas una clase a partir de la herencia y sobreescribes su método
> `__init__` es posible que tengas que llamar al método `__init__` de la
> superclase para inicializar los campos asociados a la superclase. Recuerda
> que puedes acceder a la superclase usando `super`.
-### *Abrible* y *cerrable*: `__enter__` y `__exit__`
+Los tipos básicos de python están definidos en clases también, pero su nombre
+puede usarse para hacer conversiones.
+
+```python
+>>> int("1")
+1
+```
+
+Para entender el funcionamiento de esa llamada, no hay más que recordar el que
+el aplicar el nombre de la clase como una llamada a función sirve para crear un
+objeto del tipo indicado, enviando los argumentos a su inicializador. Es decir,
+simplemente soportan el método `__init__` y construyen un nuevo objeto del tipo
+indicado a partir de lo que se les envía como argumento.
-Estos métodos permiten que los objetos puedan ser abiertos y cerrados de forma
-segura y con una sintaxis eficiente. Aunque no se van a listar en profundidad,
-el objetivo de este punto es mostrar la sentencia `with` que se habilita
-gracias a estos métodos y mostrar cómo facilitan la apertura y cierre.
+### *Abrible* y *cerrable*
+
+Los objetos *abribles* y *cerrables* puedan ser abiertos y cerrados de forma
+segura y con una sintaxis eficiente. Aunque no se van a detallar en
+profundidad, el objetivo de este punto es mostrar la sentencia `with` que se
+habilita gracias a estos métodos y mostrar cómo facilitan la apertura y cierre.
El PEP 343[^pep343] muestra en detalle la implementación de la sentencia
`with`. Simplificándolo y resumiéndolo, `with` sirve para abrir elementos y
cerrarlos de forma automática.
-> NOTA: Los PEP (*Python Enhancement Proposals*) son propuestas de mejora para
+> Los PEP (*Python Enhancement Proposals*) son propuestas de mejora para
> el lenguaje. Puedes consultar todos en la web de python. Son una fuente
> interesante de información y conocimiento del lenguaje y de programación en
> general.
@@ -799,7 +802,7 @@ se muestra la equivalencia entre la sentencia `with` y el uso de `__enter__`,
[^pep343]: Puedes leer el contenido completo del PEP en:
<https://www.python.org/dev/peps/pep-0343/>
-### *Callable*: `__call__`
+### *Llamable*
Queda pendiente desde el capítulo sobre funciones, responder a lo que es un
*callable* o *llamable*. Una vez llegados a este punto, tiene una respuesta
@@ -808,13 +811,8 @@ fácil: un *llamable* es un objeto que soporta el método `__call__`.
Aunque pueda parecer sorprendente, las funciones en python también se llaman de
este modo, así que realmente son objetos que se llaman porque soportan este
método. Es lógico, porque las funciones, recuerda el capítulo previo, pueden
-guardar valores, como el contexto en el que se crean (*closure*). Las funciones
-son meros *llamables* y como tales se comportan.
-
-Llevado más allá, los tipos básicos de python están definidos en clases
-también, lógicamente, pero pueden ser llamados para hacer conversiones tal y
-como vimos en el capítulo sobre datos. Simplemente, soportan el método
-`__init__`.
+guardar valores, como el contexto en el que se crean (la *closure*). Las
+funciones son meros *llamables* y como tales se comportan.
``` python
>>> class Dog:
@@ -833,7 +831,7 @@ propio objeto que está siendo llamado, el `self` que ya conocemos.
Resumiendo, el método `__call__` describe cómo se comporta el objeto cuando se
le aplican las paréntesis.
-### *Subscriptable*: `__getitem__`, `__setitem__` y `__delitem__`
+### *Subscriptable*
Tal y como el método anterior describía cómo se aplican las paréntesis a un
objeto, los métodos que se muestran en este apartado describen el
@@ -901,7 +899,7 @@ siguiente manera:
- Fin: último elemento inclusive
- Salto: un único elemento en orden de cabeza a cola
-> NOTA: El índice para representar el último elemento es -1, pero si se quiere
+> El índice para representar el último elemento es -1, pero si se quiere
> indicar como final, usar -1 descartará el último elemento porque el final no
> es inclusivo. Para que sea inclusivo es necesario dejar el campo fin vacío.
@@ -922,9 +920,10 @@ los campos de un objeto, si se trata de escribir suelta lanza un error de
sintaxis. Para crear *slice*s de forma separada se construyen mediante la
clase `slice` de la siguiente manera: `slice(inicio, fin, salto)`.
-En los métodos del protocolo *subscriptable* (`__getitem__`, `__setitem__` y
-`__delitem__`) a la hora de elegir un *slice* se recibe una instancia del tipo
-*slice* en lugar de una selección única como en el ejemplo previo:
+En los métodos que permiten a un objeto ser *subscriptable* (`__getitem__`,
+`__setitem__` y `__delitem__`) a la hora de elegir un *slice* se recibe una
+instancia del tipo *slice* en lugar de una selección única como en el ejemplo
+previo:
``` python
>>> class Dog:
@@ -943,8 +942,8 @@ slice(1, 100, -9)
Por complicarlo todavía más, los campos del *slice* creado desde la clase
`slice` pueden ser del tipo que se quiera. El formato de los `:` es únicamente
*sintactic sugar* para crear *slices* de tipo integer o string. Aunque después
-es responsabilidad del quien implemente el protocolo soportar el tipo de
-*slice* definido, es posible crear *slices* de lo que sea, incluso anidarlos.
+es responsabilidad del quien implemente soportar el tipo de *slice* definido,
+es posible crear *slices* de lo que sea, incluso anidarlos.
Como ejemplo de un caso que utiliza *slices* no integer, los tipos de datos
como los que te puedes encontrar en la librería `pandas` soportan *slicing*
@@ -952,9 +951,9 @@ basado en claves, como si de un diccionario se tratara.
### Ejemplo de uso
-Para ejemplificar varios de estos protocolos, tomamos como ejemplo una pieza de
-código fuente que quien escribe este documento ha usado en alguna ocasión en su
-trabajo como desarrollador.
+Para ejemplificar varios de estos métodos especiales, tomamos como ejemplo una
+pieza de código fuente que quien escribe este documento ha usado en alguna
+ocasión en su trabajo como desarrollador.
Se trata de un iterable que es capaz de iterar en un sistema de ficheros
estructurado en carpetas *año-mes-día* con la estructura `AAAA/MM/DD`. Este
@@ -973,7 +972,7 @@ funciones que no conozcas.
Te animo a que analices el comportamiento del ejemplo, viendo en detalle cómo
se comporta. Como referencia, fuera de la estructura de la clase, en las
últimas líneas, tienes disponible un bucle que puedes probar a ejecutar para
-ver cómo se comporta.
+ver su comportamiento.
``` {.python .numberLines}
from datetime import timedelta
@@ -1027,7 +1026,7 @@ for i in it:
print(i)
```
-#### Ejercicio libre: `yield` y los generadores
+#### Ejercicio libre: generadores
La parte de la iteración del ejemplo previo puede realizarse forma más breve
mediante el uso de la sentencia `yield`. Aunque no la trataremos, `yield`
@@ -1085,12 +1084,12 @@ ha dado una pincelada sobre ella para que tú investigues cuando lo veas
necesario, pero que sepas por dónde empezar.
Para describir más en detalle lo calado que está python de programación
-orientada a objetos necesitabas un ejemplo mucho más agresivo: los protocolos.
-A través de ellos has visto cómo python recoge las funcionalidades estándar y
-te permite crear objetos que las cumplan. Además, te ha servido para ver que
-**todo** en python es un objeto (hasta las clases lo son[^objects]) y para ver
-formas elegantes de resolver problemas comunes, como los iteradores, `with` y
-otros.
+orientada a objetos necesitabas un ejemplo mucho más agresivo: los métodos con
+nombres especiales. A través de ellos has visto cómo python recoge las
+funcionalidades estándar y te permite crear objetos que las cumplan. Además, te
+ha servido para ver que **todo** en python es un objeto (hasta las clases lo
+son[^objects]) y para ver formas elegantes de resolver problemas comunes, como
+los iteradores, `with` y otros.
También, te recuerdo que, aunque sea de forma colateral y sin prestarle
demasiada atención, se te ha sugerido que cuando programamos no lo hacemos