Se dice que un lenguaje de programación tiene funciones de primera clase cuando las funciones en ese lenguaje se tratan como cualquier otra variable.
Por ejemplo, en un lenguaje de este tipo, una función puede ser
Cuatro propiedades importantes:
def square(x):
return x*x
f = square(5)
print(square)
print(f)
g = square
print(g.__name__)
g(36)
# Los attributos o metodos que tiene asociado
# la función
dir(square)
Hemos pasado una función a otra. Es posible, por ende, pasar funciones como argumentos y devolver funciones como resultado de una función.
del square
g.__name__
Function objects and their names are two separate concerns.
Dado que la función g
apunta a la underlying function, lo podemos utilizar.
Hemos borrado el pointer square...no el pointer g
f(10)
## Vamos a definilr la siguiente función sensilla
def gritar(texto):
return texto.upper() + '!'
gritar('hola')
ladrar = gritar
ladrar("guau")
func = [gritar, str.lower, str.capitalize]
func
for f in func:
print(f.__name__, f("me escuchas"))
for f in func:
print(f("que haces"))
Se podría llamar a la función directamente del data structure
func[0]('me explico')
Vamos a poner como parametro la funcion que nos interesa
def saludar(fun):
saludos = fun("Hola, qué haces")
print(saludos)
saludar(gritar)
def square(x):
return x*x
def applier(funct, x):
return funct(x)
applier(square, 7)
Ejemplo:
La función map: toma una función y un array como argumentos y ejecuta cada valor de ese array a través de la función proporcionada y devuelve un nuevo array con esos resultados
def square(x):
return x * x
def sroot(x):
return x **(1/2)
def my_map(func, arg_list):
result = []
for i in arg_list:
result.append(func(i))
return result
squares = my_map(square, [1, 2, 3, 4, 5])
print(squares)
squaresR = my_map(sroot, [1, 2, 3, 4, 5])
print(squaresR)
Idea: podemos pasar cualquier funcion como argumento
def hablar(texto):
def suave(t):
return t.lower() + '...'
return suave(texto)
hablar('Shhh')
suave('shhh')
Que pasaria si queremos acceder a la funcion suave desde fuera de hablar
Importante: mirar que ahora no ejecuto la función
def get_hablar_func(volume):
def suave(texto):
return texto.lower()
def gritar(texto):
return texto.upper()
if volume > 0.5:
return gritar
else:
return suave
Hablarsuave = get_hablar_func(0.3)
Hablarsuave("HABLAR")
Hablarsuave.__name__
no solo las funciones pueden retornar otras funciones, estas innner functions tambien pueden capturar y llevar estados de las funciones padres. Veamos como
def get_hablar_func(texto, volume):
def suave():
return texto.lower()
def gritar():
return texto.upper()
if volume > 0.5:
return gritar
else:
return suave
Hablarfuerte = get_hablar_func("calla", 0.7)
Hablarfuerte
Hablarfuerte()
def tablas_mult(n):
def tablas(x):
return x * n
return tablas
tabla_3 = tablas_mult(3)
tabla_3(2)
tabla_5 = tablas_mult(5)
tabla_5(4)
def tablas_mult(n):
def tablas(x):
return x * n
return tablas(3)
tabla_5 = tablas_mult(5)
tabla_5
Python nos permite definir nuestras funciones en un ámbito local, es decir, dentro de una función
Las funciones locales están sujetas a las mismas reglas de ámbito que otras funciones, esto nos lleva a la regla LEGB para la búsqueda de nombres en python - la comprobación comienza con el ámbito local, luego el ámbito envolvente, luego el ámbito global y finalmente el ámbito incorporado LEGB - Local, Envolvente, Global, Integrado
x = 'global'
def outer_func():
y = 'enclose'
def inner_func():
z = 'local'
print(f"x: {x }, y: {y}, z: {z}")
return inner_func()
outer_func()
Las funciones locales que hemos visto hasta ahora no tienen una forma definida de interactuar con el ámbito que las rodea.
Esto es, mueren una vez que se ejectuan.
inner_func()
Modifiquemos la función inner para que pueda seguire viva una vez que termina de ejecutarse la funcion outer
def outer_func():
x = 5
def inner_func(y = 3):
return (x + y)
return inner_func
a = outer_func()
a.__name__
print(a)
print(a())
La función inner_func
hace referencia a la función outer_func
para obtener el valor de x.
La función local puede hacer referencia al ámbito externo mediante closures
o cierres. Los closures
mantienen referencias a objetos del ámbito anterior.
Closure is commonly used in what is referred to as Function Factory - these are functions that return other functions.
The returned functions are specialized.
The Function Factory takes in argument(s), creates local function that creates its own argument(s) and also uses the argument(s) passed to the function factory.
This is possible with closures
print(a(6))
Vamos a cambiar la función anterior
def outer_func(x):
def inner_func(y):
return (x + y)
return inner_func
suma_3 = outer_func(3)
suma_3.__name__
suma_3(5)
def multiply_by(num):
def multiply_by_num(k):
return num * k
return multiply_by_num
five = multiply_by(5)
print(five(2)) # 10
print(five(4)) # 20
decimal = multiply_by(10)
print(decimal(20)) # 200
print(decimal(3)) # 30
closure
es un record, un registro que almacena una función junto a su entorno, the enviromment.
The environment
is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.
Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
def outer_f():
message = 'Hi'
def inner_f():
print(message)
return inner_f()
outer_f()
Squemos el parentesis de la función inner
def outer_f():
message = 'Hi'
def inner_f():
print(message)
return inner_f
out = outer_f()
out
out.__name__
We return a function: hence, out makes reference to a function
out()
A closure is an inner functions that remembers and have access to variables in the local scope it was created (even after the outer function has finish executing)
Lets add parameters to the functions within a scope
def outer_f(msg):
message = msg
edad = 55
def inner_f(prefix):
print(prefix, message)
return inner_f
-Cuando el programa define la función, predefine las variables dentro de outer_f, i.e., las inicializa pero no las evalua
-message
-edad
-inner
Cuando llamo a outer("Hola")
lo que hace es evaluar lo que está en su código:
asigna a message
"Hola"
Esta es la variable llamada free variable
asigna a edad
22
inicializa inner
:
inner
no se ejecutó,no tiene valor asignado.f_hola = outer_f("Hola")
# Ahora llammos a la función inner
f_hola("Probando un closure")
# Veamos otro ejemplo
def contador(inicio):
#print(inicio)
def incremento(paso = 1):
nonlocal inicio
inicio = inicio + paso
return inicio
return incremento
inc_0 = contador(0)
print(inc_0.__name__)
print(inc_0.__code__.co_freevars)
print(inc_0.__defaults__)
print(inc_0())
print(inc_0())
print(inc_0())
Cuando llamo a outer_f
lo que hace es
print(inc_0(5))
print(inc_0(5))
print(inc_0(5))
# Closures tienen mucho que ver
# con programación por objetos
class Counter():
def __init__(self, start, step):
self.start = start
self.step = step
def __call__(self):
self.start = self.start + self.step
return self.start
count_0 = Counter(0,1)
count_0()
count_0()