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))