In [6]:
(lambda x: x ** 2)(8)
Out[6]:
Aunque en la mayoría de los casos se utilice como un argumento para otra función, también puede ser almacenada, comom cualquier objeto.
In [7]:
f = lambda x: x ** 2
f(7)
Out[7]:
In [8]:
g = lambda:7
print(g)
print(g())
In [9]:
help(map)
In [10]:
def square(x):
return x ** 2
numbers = range(20)
print(map(square, numbers))
Con una función anónima:
In [11]:
print(map(lambda x: x**2, range(20)))
In [12]:
print(map(str.upper, "foo bar fuzz".split()))
In [13]:
help(filter)
In [14]:
filter(lambda x: not x % 3, range(1, 30))
Out[14]:
In [15]:
filter(lambda x: x.isupper(), "HolA")
Out[15]:
In [16]:
filter(str.isupper, "HolA")
Out[16]:
In [17]:
str.isupper("A")
Out[17]:
In [18]:
help(reduce)
In [19]:
reduce(lambda x,y:x+y, range(5))
Out[19]:
In [20]:
import operator
reduce(operator.add, range(5))
Out[20]:
In [21]:
f = lambda x: reduce(operator.mul, range(1, x+1), 1)
f(6)
Out[21]:
In [22]:
reduce(operator.add, map(lambda x:x**2, range(5)))
Out[22]:
In [23]:
# con map
map_squares = map(lambda x: x**2, range(20))
print(map_squares)
# con LC (list comprenhension)
lc_squares = [x**2 for x in range(20)]
print(lc_squares)
In [24]:
[2**x for x in range(16)]
Out[24]:
De hecho es más potente que map(), porque se puede aplicar a varios iteradores.
In [25]:
print( [x+y+z for x in 'abc' for y in '12' for z in 'XYZ'] )
Y uno de los iteradores puede depender del otro:
In [26]:
[x for y in [1, 2, 3] for x in range(y+1)]
Out[26]:
Y es anidable (para crear estructuras anidadas):
In [27]:
[[x+y for x in 'abc'] for y in '12']
Out[27]:
Permite además que la inserción en la lista resultante sea condicionada. Por tanto, más potente que filter():
In [28]:
filter_3_multiples = filter(lambda x: not x % 3, range(1, 30))
print(filter_3_multiples)
lc_3_multiples = [x for x in range(1, 30) if not x % 3]
print(lc_3_multiples)
In [29]:
{ord(x) for x in "foo bar fuzz"}
Out[29]:
In [30]:
grades ={"john":3, "mary":5}
{key.upper():value for key,value in grades.items()}
Out[30]:
In [31]:
grades.items()
Out[31]:
In [32]:
{x:ord(x) for x in "hola"}
Out[32]:
In [33]:
{category:set() for category in ["vowels", "consonants"]}
Out[33]:
In [34]:
def bob_say(text):
print("Bob says: " + text)
bob_say("hello")
Escribamos un decorador que genere una traza de las invocaciones.
In [35]:
def trace(func):
def deco(text):
print("-- function '{}' invoked".format(func.__name__))
func(text)
print("-- function '{}' ends".format(func.__name__))
return deco
deco_bob_say = trace(bob_say)
deco_bob_say("bye")
Pero lo interesante es que los decoradores se pueden aplicar se una forma más compacta en la propia definición de la función:
In [36]:
@trace
def bob_say(text):
print("Bob says: " + text)
bob_say("hello")
Una función thread safe:
In [37]:
import threading
lock = threading.Lock()
def syncronized(func):
def deco():
try:
lock.acquire()
func()
finally:
lock.release()
return deco
@syncronized
def say():
print("hello")
say()
In [38]:
import threading
def syncronized(lock):
def with_lock(func):
def deco():
try:
lock.acquire()
func()
finally:
lock.release()
return deco
return with_lock
lock = threading.Lock()
@syncronized(lock)
def say():
print("hello")
say()
In [39]:
from functools import partial
print(partial.__doc__)
In [40]:
def my_pow(x, y):
return x ** y
square = partial(my_pow, y=2)
print(square(8))
cube = partial(my_pow, y=3)
print(cube(3))