# Scope y Namespaces

## 1. ¿Qué son el `Scope` y los `Namespaces` en Python 3?

Antes de hablar sobre clases, es importante que comprendamos los conceptos de `Scope` y `Namespace` en Python.

Un **`Namespace`** es un mapeo de nombres a objetos. Los `Namespaces` se crean en diferentes momentos y tienen diferentes tiempos de vida.

Durante la ejecución de un programa en Python 3 se crean diferentes `Namespaces` que pueden ser accesibles desde diferentes `scopes`:

* El **`Namespace` por defecto** que se crea cuando el intérprete de Python se inicia es el que contiene los nombres por defecto de Python. Este `Namespace` nunca se borra.

```python
dir(__builtins__)
```

```python
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'BlockingIOError',
 'BrokenPipeError',
 'BufferError',
 'BytesWarning',
 'ChildProcessError',
 'ConnectionAbortedError',
 'ConnectionError',
 'ConnectionRefusedError',
 'ConnectionResetError',
 'DeprecationWarning',
 'EOFError',
 'Ellipsis',
 
 ...
```

* El **`Namespace` global** para un módulo se crea cuando se lee la definición del módulo y, normalmente, dura hasta que el intérprete se cierra.

```python
var_modulo = 10
```

```python
globals()
```

```
...

  'tuple',
  'type',
  'vars',
  'zip'],
 '_i3': 'var_modulo = 10',
 'var_modulo': 10,
 '_i4': 'globals()'}
```

* El **`Namespace` local** para una función o cualquier otra estructura de Python se crea cuando se llama a la función, y se borra cuando la función termina. Las invocaciones recursivas tienen cada una su propio `Namespace`.

```python
def func():
    var_local_func = 5
    var_local_func = 10
    print(locals())
func()
{'var_local_func': 10}
```

## 2. `scope` en Python 3

El `scope` es una región de un programa en Python donde un `Namespace` es directamente accesible.

En Python existen diferentes `scopes` desde los que se puede acceder a los `Namespaces` con algunas particularidades que debemos tener en cuenta:

* **El `scope` local (o de función)** es el bloque de código o cuerpo de cualquier función o expresión en Python. Contiene los nombres que se definen dentro de la función. Estos nombres sólo serán visibles desde el código de la función. Se crea en la llamada a la función, no en la definición de la misma, por lo que habrá tantos `scopes` locales diferentes como llamadas a la función.

```python
def func():
    var_local_func = 10
    print(var_local_func)
func()
10
```

La variable `var_local_func` definida en el Namespace local de la función no es accedida desde un scope global.

```python
var_local_func
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[11], line 1
----> 1 var_local_func

NameError: name 'var_local_func' is not defined
```

* **El `scope` no local** es un ámbito especial que sólo existe para las funciones anidadas. En estos casos el `scope` local es el cuerpo de la función anidada y el `scope` no local es el ámbito de la función externa. Los nombres del `scope` no local son visibles desde el código de las funciones internas y externas.

```python
def func():
    var_nonlocal_func = 10
    def func2():
        var_local_func2 = 2
```

```python
def func():
    var_nonlocal_func = 10
    print("Namespace func", locals())
    def func2():
        var_local_func2 = 5
        print("Namespace func2", locals())
    func2()
    
func()

Namespace func {'var_nonlocal_func': 10}
Namespace func2 {'var_local_func2': 5}
```

La variable `var_no_local_func` es visible desde el código de `func2` pero la variable `var_local_func2` no es visible desde el código de `func`

```python
def func():
    var_nonlocal_func = 10
    def func2():
        var_local_func2 = 5
        print(var_nonlocal_func)
    func2()
    
func()
10
```

```python
def func():
    var_nonlocal_func = 10
    print(var_local_func2)
    def func2():
        var_local_func2 = 5
    func2()
    
func()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[26], line 1
----> 1 func()

Cell In[25], line 3, in func()
      1 def func():
      2     var_nonlocal_func = 10
----> 3     print(var_local_func)
      4     def func2():
      5         var_local_func = 5

NameError: name 'var_local_func' is not defined
```

* **El `scope` global (o de módulo)** es el ámbito superior de un programa, script o módulo de Python. Este ámbito de Python contiene todos los nombres que se definen en el nivel superior de un programa o módulo. Los nombres en este ámbito de Python son visibles desde cualquier parte de tu código.

```python
var_global = 15

print(var_global)
15
def func_global():
    def func_local():
        print(var_global)
    func_local()
    
func_global()
15
```

La variable `var_global` es accesible desde el código de `func` y cualquier otra función interna. Las variables definidas en `func` no son accesibles desde este scope.

```python
def func():
    var_local_func = 10
    
var_local_func
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[42], line 1
----> 1 var_local_func

NameError: name 'var_local_func' is not defined
```

* **El `scope` por defecto** es un ámbito especial de Python que se crea o carga cada vez que ejecutas un script o abres una sesión interactiva. Este ámbito contiene nombres como palabras clave, funciones, excepciones y otros atributos que están incorporados en Python. Los nombres en este ámbito de Python también están disponibles desde cualquier parte de tu código. Es cargado automáticamente por Python cuando ejecutas un programa o script.

```python
# Scope global
True

# Scope no local
def func():
    True
    
# Scope local
def func():
    def func2():
        True
    func2()
```

**Una de las cosas importantes es que los nombres que se encuentran en un scope determinado puede ser accedidos desde un scope externo pero no pueden ser actualizados o modificados**

```python
contador = 0

def actualizar_contador():
    contador += 1
    
actualizar_contador()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Cell In[55], line 1
----> 1 actualizar_contador()

Cell In[54], line 2, in actualizar_contador()
      1 def actualizar_contador():
----> 2     contador += 1

UnboundLocalError: local variable 'contador' referenced before assignment
```

## 3. Sentencias `global` y `nonlocal`

Teniendo en cuenta el comportamiento por defecto que hemos visto en los apartados anteriores, Python nos proporciona las dos sentencias `global` y `nonlocal` para modificarlo si lo necesitásemos.

```python
contador = 0

def actualizar_contador():
    global contador
    contador += 1
actualizar_contador()

contador
1
```

```python
def funcion():
    contador = 0
    def actualizar_contador():
        nonlocal contador
        contador += 1
    actualizar_contador()
    print(contador)
    
funcion()
1
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://afsh4ck.gitbook.io/desarrollo-con-python/orientacion-a-objetos/scope-y-namespaces.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
