summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2019-12-04 19:26:21 +0100
committerEkaitz Zarraga <ekaitz@elenq.tech>2019-12-04 19:26:21 +0100
commitdd51ede6460b2392afb28676ecc7a42cb8748f1c (patch)
tree6549781e431c3ce2d8c64589c28579025fdd3264 /src
parent22d3ef40d0d3817f58729eeb66c0ea9e34b0b661 (diff)
OOP: add sliceable and change naming
Diffstat (limited to 'src')
-rw-r--r--src/05_oop.md82
1 files changed, 80 insertions, 2 deletions
diff --git a/src/05_oop.md b/src/05_oop.md
index 549b88d..0e14c02 100644
--- a/src/05_oop.md
+++ b/src/05_oop.md
@@ -779,7 +779,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/>
-### *Llamable*: `__call__`
+### *Callable*: `__call__`
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
@@ -814,7 +814,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.
-### *Accesible*: `__getitem__` y `__setitem__`
+### *Subscriptable*: `__getitem__` y `__setitem__`
Tal y como el método anterior describía cómo se aplican las paréntesis a un
objeto, el protocolo que se muestra en este apartado describe el comportamiento
@@ -828,6 +828,11 @@ mediante los corchetes llama automáticamente al método `__getitem__` y cuando
se intenta asociar un campo a un valor llama al método `__setitem__` del
objeto.
+Aunque en otros protocolos aquí descritos hemos inventado un nombre para este
+documento, Python a este protocolo le denomina *subscriptable* así que cuando
+intentes acceder usando corchetes a un objeto que no soporta el protocolo, el
+error que saltará te utilizará la misma nomenclatura que nosotros.
+
El siguiente ejemplo muestra el protocolo en funcionamiento en una clase sin
funcionamiento alguno. Lo lógico y funcional sería utilizar estos dos métodos
para facilitar el acceso a campos de estas clases o para crear clases que
@@ -851,6 +856,79 @@ Fíjate en que reciben diferente cantidad de argumentos de entrada cada uno de
los métodos. El método `__setitem__` necesita indicar no sólo qué *item* desea
alterarse, sino su también su valor.
+#### *Slice notation*
+
+Se trata de una forma avanzada de seleccionar las posiciones de un objeto, el
+nombre viene de *slice*, rebanada, y significa que puede coger secciones del
+objeto en lugar de valores únicos. Piénsalo como en una barra de pan cortada en
+rebanadas de la que quieres seleccionar qué rebanadas te interesan en bloque.
+
+No todos los objetos soportan *slicing*, pero los que lo hacen permiten acceder
+a grupos de valores en el orden en el que están indicando el inicio del grupo
+(inclusive), el final (no inclusive) y el salto de un elemento al siguiente.
+
+Además, los valores del *slice* pueden ser negativos. Añadir un número negativo
+al salto implica que el salto se hace hacia atrás. Añadirlo en cualquier de los
+otros dos valores, inicio o final de grupo, implica que se cuenta el elemento
+desde el final de la colección en dirección opuesta a la normal.
+
+La sintaxis de los *slice*s es la siguiente: `[inicio:fin:salto]`.
+Cada uno de los valores es opcional y si no se añaden se comportan de la
+siguiente manera:
+
+- Inicio: primer elemento
+- 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
+> 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.
+
+Dada una lista de los números naturales del 1 al 100, ambos incluidos, de
+nombre `l` se muestran unos casos de *slicing*.
+
+``` python
+>>> l[-5:]
+[95, 96, 97, 98, 99]
+>>> l[6:80:5]
+[6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, 76]
+>>> l[60:0:-5]
+[60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5]
+```
+
+La sintaxis de los *slice*s mostrada sólo tiene sentido a la hora de acceder a
+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__` y `__setitem__`) 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:
+... def __getitem__(self, item):
+... print(item)
+...
+>>> bobby = Dog()
+>>> bobby[1:100]
+slice(1, 100, None)
+>>> bobby[1:100:9]
+slice(1, 100, 9)
+>>> bobby[1:100:-9]
+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.
+
+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*
+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