summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2019-11-28 12:18:13 +0100
committerEkaitz Zarraga <ekaitz@elenq.tech>2019-11-28 12:35:42 +0100
commit2c79243054ae96d98f2e7e4b84322485f0c421a1 (patch)
treeb47344b48a05db91c69d099c4360f2069f209974 /src
parent61ed59ec54c6a3d45127eece963d3f2c51d5c886 (diff)
Scope dinámico
Diffstat (limited to 'src')
-rw-r--r--src/04_funciones.md166
1 files changed, 91 insertions, 75 deletions
diff --git a/src/04_funciones.md b/src/04_funciones.md
index c99ef04..669e796 100644
--- a/src/04_funciones.md
+++ b/src/04_funciones.md
@@ -195,70 +195,6 @@ pero sí al revés. Los casos son los siguientes:
[^generator-expression]: <https://www.python.org/dev/peps/pep-0289/>
-### Global
-
-Hemos hablado de qué sentencias crean nuevos contextos, pero no hemos hablado
-de qué pasa si esos nuevos contextos crean referencias cuyo nombre es idéntico
-al de las referencias globales.
-
-Partiendo de lo que se acaba de explicar, y antes de adentrarnos en ejemplos,
-si se crea una función (o cualquiera de las otras estructuras) python creará un
-contexto para ella. Una vez creado, al crear una variable en este nuevo
-contexto, python añadirá una nueva entrada en su tabla hija con el nombre de la
-variable. Al intentar consultarla, python encontrará que en su tabla hija
-existe la variable y tomará el valor con el que la declaramos. Cuando la
-función termine, la tabla de contexto asociada a la función será eliminada.
-Esto siempre es así, independientemente del nombre de referencia que hayamos
-seleccionado. Por tanto, si el nombre ya existía en alguno de los contextos
-padre, lo ocultaremos, haciendo que dentro de esta función se encuentre el
-nombre recién declarado y no se llegue a buscar más allá. Cuando la función
-termine, como el contexto asociado a ésta no está en la zona de búsqueda de la
-función madre, en la función madre el valor seguirá siendo el que era.
-
-Ilustrándolo en un ejemplo:
-
-``` python
->>> a = 1
->>> def f():
-... a = 2
-... print(a)
-...
->>> a
-1
->>> f()
-2
-```
-
-Aunque el nombre de la referencia declarada en el interior sea el mismo que el
-de una referencia externa su declaración no afecta, lógicamente, al exterior
-ya que ocurre en un contexto independiente.
-
-Para afectar a la referencia externa, python dispone de la sentencia `global`.
-La sentencia `global` afecta al bloque de código actual, indicando que los
-identificadores listados deben interpretarse como globales. De esta manera, si
-se reasigna una referencia dentro de la función, no será el contexto propio el
-que se altere, sino el contexto padre que la albergue.
-
-``` python
->>> a = 1
->>> def f():
-... global a
-... a = 2
-... print(a)
-...
->>> f()
-2
->>> a
-2
-```
-
-> NOTA: Te recomiendo, de todas formas, que no edites valores globales desde
-> el cuerpo de funciones. Es más elegante y comprensible si los efectos de las
-> funciones sólo se aprecian en los argumentos de entrada y salida.
-
-Para más detalles sobre limitaciones y excepciones, puedes buscar en la ayuda
-ejecutando `help("global")`.
-
### Scope léxico, Closures
Hemos dicho que las funciones pueden declararse dentro de funciones, pero no
@@ -332,6 +268,87 @@ A nivel práctico, las *closures* son útiles para muchas labores que iremos
desgranando de forma accidental. Si tienes claro el concepto te darás cuenta
dónde aparecen en los futuros ejemplos.
+### Scope dinámico: `global` y `nonlocal`
+
+Hemos hablado de qué sentencias crean nuevos contextos, pero no hemos hablado
+de qué pasa si esos nuevos contextos crean referencias cuyo nombre es idéntico
+al de las referencias que aparecen en contextos superiores.
+
+Partiendo de lo que se acaba de explicar, y antes de adentrarnos en ejemplos,
+si se crea una función (o cualquiera de las otras estructuras) python creará un
+contexto para ella. Una vez creado, al crear una variable en este nuevo
+contexto, python añadirá una nueva entrada en su tabla hija con el nombre de la
+variable. Al intentar consultarla, python encontrará que en su tabla hija
+existe la variable y tomará el valor con el que la declaramos. Cuando la
+función termine, la tabla de contexto asociada a la función será eliminada.
+Esto siempre es así, independientemente del nombre de referencia que hayamos
+seleccionado. Por tanto, si el nombre ya existía en alguno de los contextos
+padre, lo ocultaremos, haciendo que dentro de esta función se encuentre el
+nombre recién declarado y no se llegue a buscar más allá. Cuando la función
+termine, como el contexto asociado a ésta no está en la zona de búsqueda de la
+función madre, en la función madre el valor seguirá siendo el que era.
+
+Ilustrándolo en un ejemplo:
+
+``` python
+>>> a = 1
+>>> def f():
+... a = 2
+... print(a)
+...
+>>> a
+1
+>>> f()
+2
+```
+
+Aunque el nombre de la referencia declarada en el interior sea el mismo que el
+de una referencia externa su declaración no afecta, lógicamente, al exterior
+ya que ocurre en un contexto independiente.
+
+Para afectar a la referencia global, python dispone de la sentencia `global`.
+La sentencia `global` afecta al bloque de código actual, indicando que los
+identificadores listados deben interpretarse como globales. De esta manera, si
+se reasigna una referencia dentro de la función, no será el contexto propio el
+que se altere, sino el contexto global, el padre de todos los contextos.
+
+``` python
+>>> a = 1
+>>> def f():
+... global a
+... a = 2
+... print(a)
+...
+>>> f()
+2
+>>> a
+2
+```
+
+> NOTA: Te recomiendo, de todas formas, que no edites valores globales desde
+> el cuerpo de funciones. Es más elegante y comprensible si los efectos de las
+> funciones sólo se aprecian en los argumentos de entrada y salida.
+
+Para más detalles sobre limitaciones y excepciones, puedes buscar en la ayuda
+ejecutando `help("global")`.
+
+El caso de `nonlocal` es similar, sin embargo, está diseñado para trabajar en
+contextos anidados. Es decir, en lugar de saltar a acceder a una variable
+global, `nonlocal` la busca en cualquier contexto que no sea el actual.
+`nonlocal` comienza a buscar las referencias en el contexto padre y va saltando
+hacia arriba en la jerarquía en busca de la referencia. Para saber más:
+`help("nonlocal")`.
+
+La diferencia principal entre ambas es que `global` puede crear nuevas
+referencias, ya que se sabe a qué contexto debe afectar: al global. Sin
+embargo, `nonlocal` necesita que la referencia a la que se pretende acceder
+esté creada, ya que no es posible saber a qué contexto se pretende acceder.
+
+Las sentencias `global` y `nonlocal` son tramposas, ya que son capaces de
+alterar el comportamiento del *scope léxico* y convertirlo en *scope dinámico*.
+El *scope dinámico* es el caso opuesto al léxico, en el que las funciones
+acceden a valores definidos en el contexto donde se ejecutan, no donde se
+crean.
## Argumentos de entrada y llamadas
@@ -610,26 +627,25 @@ sencilla, y todos los conceptos importantes relacionados con ellas. Un
conocimiento que es útil en python, pero que puede ser extendido a casi
cualquier lenguaje.
-Partiendo del *scope*, has aprendido la diferencia entre lo *local* y lo
-*global*. Después, has comprendido que las funciones en python son sólo un
-valor más, como puede ser un `int`, y que pueden declararse en cualquier lugar,
-lo que te abre la puerta a querer declarar funciones sencillas sin nombre, que
-se conocen como funciones *lambda*.
+Tras un sencillo acercamiento al *scope*, has comprendido que las funciones en
+python son sólo un valor más, como puede ser un `int`, y que pueden declararse
+en cualquier lugar, lo que te abre la puerta a querer declarar funciones
+sencillas sin nombre, que se conocen como funciones *lambda*.
Una vez has aclarado que las funciones son ciudadanos de primera clase
(*first-class citizens*) ya estabas preparado para afrontar la realidad del
*scope* donde has tratado los contextos y cómo funcionan definiendo el concepto
del *scope léxico* que, colateralmente, te ha enseñado lo que es una *closure*,
-un método para implementarlo.
+un método para implementarlo. Pero también has tenido ocasión de aprender que
+en python es posible crear casos de *scope dinámico* mediante las sentencias
+`global` y `nonlocal`, que pueden ser útiles, pero es mejor no abusar de ellas.
Pero no había quedado claro en su momento cómo funcionaban los argumentos de
entrada y las llamadas a las funciones, así que has tenido ocasión de ver por
primera vez lo que es un *callable* en python, aunque se te ha prometido
-analizarlo en el futuro.
-
-El último detalle que has aprendido son los tipos de argumentos de entrada que
-pueden recibir las funciones, *positional* y *keyword*, y cómo se utilizan en
-todas sus posibles formas.
+analizarlo en el futuro. Lo que sí que has tenido ocasión de tratar son los
+argumentos *positional* y *keyword*, y cómo se utilizan en todas sus posibles
+formas.
Finalmente, para agrupar todo esto en un único concepto, se te han mostrado los
*decorators*, aunque de forma muy general, con el fin de que vieras que todo lo