Closures

Los closures es un concepto de programación que se encuentra en diferentes lenguajes de programación, como pueden ser: Java, Groovy, Scala, Python,… En la entrada de hoy, Closures, definiré el concepto de closures y mostraré unos ejemplos en lenguaje Python.

Un Closure es aquella función que hereda el contexto de otra función con lo cual permite heredar las variables utilizadas en la primera. No hay que confundir un closures con el concepto de función anónima.

Las funciones closures pueden ser definidas en una clase o bien en funciones pertenecientes a un módulo. El ejemplo típico que mostraré es el cálculo de una media. En los siguientes apartados, mostraré ejemplos con diferentes escenarios.

Closures en una clase

En el presente ejemplo, definiré una clase Averager que define la función __call__ en la cual se define la funcionalidad para el cálculo de la media a partir de los valores que se le pasan por parámetro; y, el almacén de los valores insertados, será una lista que será atributo de la clase. El snippet del código de ejemplo es el siguiente:

class Averager():
    def __init__(self):
        self.series = []

    def __call__(self, new_value):
        self.series.append(new_value)
        total = sum(self.series)
        return total/len(self.series)
        
def __ejemplo1() -> None:
    print(f'-*- Ejemplo de Clouser: cálculo de la media utilizando una clase con método call -*-')
    avg = Averager()
    print(f'avg(10)={avg(10)}')
    print(f'avg(11)={avg(11)}')
    print(f'avg(12)={avg(12)}')

La salida por consola del snippet mostrado es el siguiente:

    -*- Ejemplo de Clouser: cálculo de la media utilizando una clase con método call -*-
    avg(10)=10.0
    avg(11)=10.5
    avg(12)=11.0 

Closures en una función

El cálculo de un closure se puede definir en una función de un módulo con estructura parecida a la de una clase; la diferencia reside en dónde se encuentra el almacén de valores, los cuáles estarán en una función cuyos valores serán heredados por otra función ubicada en un nivel superior, es decir, una función se define dentro de otra función. El snippet del código de ejemplo es el siguiente:

def make_averager():
    series = []

    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager

def __ejemplo2() -> None:
    print(f'-*- Ejemplo de Clouser: cálculo de la media utilizando un clouser  -*-')
    avg = make_averager()
    print(f'avg(10)={avg(10)}')
    print(f'avg(11)={avg(11)}')
    print(f'avg(12)={avg(12)}')

La salida por consola del snippet mostrado es el siguiente:

-*- Ejemplo de Clouser: cálculo de la media utilizando un clouser  -*-
avg(10)=10.0
avg(11)=10.5
avg(12)=11.0  

Para el cálculo de la media se utiliza las funciones sum y len; y, además, se utiliza una lista con los valores que son insertados en el conjunto de cálculo.

Closures en una función usando el operador nonlocal

El ejemplo anterior puede ser definido de una forma más eficiente, si se va realizando la suma y el conteo de elementos de una forma directa conforme se van añadiendo los elementos. Para realizar este enfoque, hay que definir dos variables count y total en la función de primer nivel; y, en la función de segundo nivel, utilizar el operador nonlocal para identificar que no son variables locales y son variables de la función del nivel superior. El cálculo de la media se realiza con los valores de estas variables eliminando el uso de funciones y el almacén de valores. El snippet del código es el siguiente:

def make_averager2() -> None:
    count = 0
    total = 0

    def averager(new_value):
        nonlocal count, total
        count += 1
        total += new_value
        return total/count

    return averager


def __ejemplo3() -> None:
    print(f'-*- Ejemplo de Couser: utilizando una función con la clausula nonlocal -*-')
    avg = make_averager2()
    print(f'avg(10)={avg(10)}')
    print(f'avg(11)={avg(11)}')
    print(f'avg(12)={avg(12)}')

La salida por consola del snippet mostrado es el siguiente:

-*- Ejemplo de Couser: utilizando una función con la clausula nonlocal -*-
avg(10)=10.0
avg(11)=10.5
avg(12)=11.0 

Como se puede comprobar en los ejemplos, un closure es una definición de un función la cual contiene otra función; y, en está última, se pueden utilizar el contexto de ejecución de la primera.

Este concepto de closures es la base de los decoradores en Python los cuales son aquellas funciones que se ejecutan al invocar a una función.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s