Creando Funciones Funcionales

PythonPythonBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Esta sección introduce la idea de utilizar funciones para crear otras funciones.

Introducción

Considere la siguiente función.

def add(x, y):
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Esta es una función que devuelve otra función.

>>> a = add(3,4)
>>> a
<function add.<locals>.do_add at 0x7f27d8a38790>
>>> a()
Adding 3 4
7

Variables locales

Observe cómo la función interna se refiere a las variables definidas por la función externa.

def add(x, y):
    def do_add():
        ## `x` y `y` se definen arriba de `add(x, y)`
        print('Adding', x, y)
        return x + y
    return do_add

Observe además que esas variables siguen existiendo de alguna manera después de que add() haya terminado.

>>> a = add(3,4)
>>> a
<function do_add at 0x6a670>
>>> a()
Adding 3 4      ## ¿De dónde provienen estos valores?
7

Cerraduras

Cuando una función interna se devuelve como resultado, esa función interna se conoce como una cerradura.

def add(x, y):
    ## `do_add` es una cerradura
    def do_add():
        print('Adding', x, y)
        return x + y
    return do_add

Característica esencial: Una cerradura retiene los valores de todas las variables necesarias para que la función funcione correctamente más adelante. Piense en una cerradura como una función más un entorno adicional que contiene los valores de las variables en las que depende.

Usando Cerraduras

Las cerraduras son una característica esencial de Python. Sin embargo, su uso suele ser sutil. Aplicaciones comunes:

  • Uso en funciones de devolución de llamada.
  • Evaluación diferida.
  • Funciones decoradoras (más adelante).

Evaluación diferida

Considere una función como esta:

def after(seconds, func):
    import time
    time.sleep(seconds)
    func()

Ejemplo de uso:

def greeting():
    print('Hello Guido')

after(30, greeting)

after ejecuta la función suministrada... más tarde.

Las cerraduras llevan información adicional.

def add(x, y):
    def do_add():
        print(f'Adding {x} + {y} -> {x+y}')
    return do_add

def after(seconds, func):
    import time
    time.sleep(seconds)
    func()

after(30, add(2, 3))
## `do_add` tiene las referencias x -> 2 e y -> 3

Repetición de código

Las cerraduras también se pueden utilizar como técnica para evitar la excesiva repetición de código. Puedes escribir funciones que generen código.

Ejercicio 7.7: Usar Cerraduras para Evitar la Repetición

Una de las características más poderosas de las cerraduras es su uso en la generación de código repetitivo. Si se vuelve a revisar el Ejercicio 5.7, recuerde el código para definir una propiedad con comprobación de tipos.

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price
  ...
    @property
    def shares(self):
        return self._shares

    @shares.setter
    def shares(self, value):
        if not isinstance(value, int):
            raise TypeError('Expected int')
        self._shares = value
  ...

En lugar de escribir repetidamente ese código una y otra vez, se puede crear automáticamente utilizando una cerradura.

Cree un archivo typedproperty.py y coloque el siguiente código en él:

## typedproperty.py

def typedproperty(name, expected_type):
    private_name = '_' + name
    @property
    def prop(self):
        return getattr(self, private_name)

    @prop.setter
    def prop(self, value):
        if not isinstance(value, expected_type):
            raise TypeError(f'Expected {expected_type}')
        setattr(self, private_name, value)

    return prop

Ahora, pruébelo definiendo una clase como esta:

from typedproperty import typedproperty

class Stock:
    name = typedproperty('name', str)
    shares = typedproperty('shares', int)
    price = typedproperty('price', float)

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Intente crear una instancia y verificar que la comprobación de tipos funcione.

>>> s = Stock('IBM', 50, 91.1)
>>> s.name
'IBM'
>>> s.shares = '100'
... debería obtener un TypeError...
>>>

Ejercicio 7.8: Simplificando Llamadas a Funciones

En el ejemplo anterior, los usuarios pueden encontrar llamadas como typedproperty('shares', int) un poco verbosas para escribir, especialmente si se repiten muchas veces. Agregue las siguientes definiciones al archivo typedproperty.py:

String = lambda name: typedproperty(name, str)
Integer = lambda name: typedproperty(name, int)
Float = lambda name: typedproperty(name, float)

Ahora, reescriba la clase Stock para usar estas funciones en lugar de las anteriores:

class Stock:
    name = String('name')
    shares = Integer('shares')
    price = Float('price')

    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Ah, eso es un poco mejor. La principal lección aquí es que las cerraduras y lambda a menudo se pueden usar para simplificar el código y eliminar la repetición molesta. Esto suele ser bueno.

Resumen

¡Felicitaciones! Has completado el laboratorio de Funciones de Retorno. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.