¡Esta es una revisión vieja del documento!
Crean una lista a partir de otras.
Siguen el formato [variable_devuelta bucle(s) condicion(es)]:
>>> lst = [4, 6, 1, 8, 3, 0, 5, 9, 10, 7, 2] # Creación de la lista inicial >>> big = [n for n in lst if n > 5] # Selección de números mayores que 5 >>> big [6, 8, 9, 10, 7] >>> sbig = sorted([n for n in lst if n > 5]) # Y ordenados >>> sbig [6, 7, 8, 9, 10] >>> sbig = sorted(n for n in lst if n > 5) # Aunque entre paréntesis no es necesario poner los corchetes >>> sbig [6, 7, 8, 9, 10] >>> lst2 = [3, 5, 9] >>> [(n, n2) for n in lst for n2 in lst2 if n == n2] [(3, 3), (5, 5), (9, 9)] >>> [n for n in lst for n2 in lst2 if n == n2] [3, 5, 9] >>> listOfWords = ["this","is","a","list","of","words"] >>> [ word[0] for word in listOfWords ] ['t', 'i', 'a', 'l', 'o', 'w'] >>> ['object ' + arg.__class__.__name__ if isinstance(arg, types.ObjectType) else arg for arg in args]
El siguiente crea pares de género (primero para news y luego para romance) y palabra en mayúsculas:
genre_word = [(genre, word.upper()) for genre in ['news', 'romance'] for word in words(categories=genre)]
Pueden ser combinadas con funciones del sistema como any o all:
myList = (2, 3, 5, 6) print any(x == 1 for x in myList) # False print any(x == 5 for x in myList) # True
También los diccionarios pueden ser creados así:
user_email = {user.name: user.email for user in users_list if user.email}
Esto crearía un objeto del estilo:
user_email = {'alfredgg': 'alfredgg@yahoo.es', 'diabolika': 'diabolikalau@gmail.com'}
O los sets:
users_first_names = {user.first_name for user in users} other = {x+x for x in 'patata'} # -> {'pp', 'aa', 'tt'}
Los generators son valores que se van generando on the fly, es decir, sólo puedes recoger un valor una vez ya que la lista no se guarda en memoria.
La sintaxis es la misma pero con paréntesis:
mygenerator = (x*x for x in range(3)) for i in mygenerator : print(i)
Los generadores no soportan el indexado (gen[3] daría error) ni el slicing (gen[:3] también daría error). Tampoco pueden concatenarse a las listas ni podremos realizar las funciones que hacemos con estas (consultar su tamaño, ordenarlo…).
Podemos realizar las siguientes acciones sobre ellos:
.next(), devuelve el siguiente valor.list(g), convertiría el generador en una lista.filtered_gen = (item for item in my_list if item > 3) filtered_gen.next() filtered_gen.next() filtered_gen.next()
Otro ejemplo:
def route(*args): return ':'.join(str(arg) for arg in args) route('user', 32, 'friends') # -> "user:32:friends"
Es una especie de return dentro de un bucle pero que conserva su valor para la próxima vez que se le invoque. Es decir, retorna un generator.
def func(l): while True: # Genera valores infinitos for v in l: yield v it = func([1, 9, 5, 6, 3]) it.next() # 1 it.next() # 9 it.next() # 5
with <inicialización> as <nombre de variable>: <bloque>. # Este código no libera file_handle si ocurre una excepción (MAL!): file_handle = open(path_to_file, 'r') for line in file_handle.readlines(): if raise_exception(line): print('No! An Exception!') ############ # Código CORRECTO: with open(path_to_file, 'r') as file_handle: for line in file_handle: if raise_exception(line): print('No! An Exception!')
Podemos definir clases que trabajen como context manager definiendo los métodos __enter__ y __exit__, o funciones a partir del módulo contextlib.
# sin reflection: Foo().hello() # con reflection: getattr(globals()['Foo'](), 'hello')()
getattr recoge un atributo por string de una clase, colección… dir devuelve en string los atributos y métodos de ese objeto.getmro de inspect devuelve la herencia de una clase.El siguiente ejemplo recoge las propiedades\métodos de un objeto y mira su herencia, si vienen de DataType realizará una acción con ellas.
def load (self, config): import inspect attrs = [getattr(self, prop) for prop in dir(self)] for attr in attrs: types = inspect.getmro(attr.__class__) if types[len(types)-1] == DataType: attr.load(config[attr.configname])
Una expresión lambda es una función sencilla de una sola línea. Son virtuales y no tienen un nombre con el que llamarlas. Se crean, se ejecutan y desaparecen, pero puedes guardarlas en una variable y llamarlas desde ella.
lambda <parametro1, parametro2…> : <línea de código devuelta>
a = lambda x,y : x*y a(3,4) # devolvería 12
(lambda x: x*2)(3) # devolvería 6
Realiza una llamada a una función para cada elemento del array:
lst = [1, 5, 8, 6, 4, 2, 3] map ((lambda x : x*3), lst) # retornaría: [3, 15, 24, 18, 12, 6, 9]
Retorna los elementos de una lista que al evaluarla con una función devuelven True:
lst = [1, 5, 8, 6, 4, 2, 3] filter((lambda x : x < 5), lst) # retornaría: [1, 4, 2, 3]
Ejecuta una función para cada elemento de un array acumulando su valor:
lst = [1, 5, 8, 6, 4, 2, 3] reduce((lambda x,y : x+y), lst) # retornaría: 29, la suma de todos los elementos de la colección
Función de comparación por longitud:
my_cmp = (lambda x,y: cmp(len(x), len(y))) my_cmp('abc', 'de')
Pueden ser usadas en vez de las comprehesion lists (y son preferibles):
map(lambda x: x**2, range(5)) # -> [0, 1, 4, 9, 16] filter(lambda x: x % 2, range(10)) # -> [1, 3, 5, 7, 9]
Comprobar el tipo de una variable:
type(o) is str isinstance(o, str)
Saber si es una subclase:
issubclass(type(o), str)
Saber si…
isinstance(obj, types.FunctionType) # es función
Para comprobar si es un string (del que sea) estas dos son válidas:
isinstance(o, basestring) isinstance(o, (str, unicode))
Las funciones son objetos del lenguaje, como tal pueden ser pasadas a otras funciones como parámetros.
Un decorador no es más que una función que recibe otra, le añade funcionalidad y la devuelve:
def identity_decorator(func): def wrapper(): func() return wrapper def a_function(): print "I'm a normal function." decorated_function = identity_decorator(a_function) decorated_function() # >> I'm a normal function
El scope de wrapper es identity_decorator. En el siguiente ejemplo se utiliza un mutable object (diccionarios, listas, instancias…). Un simple integer sería inmutable dentro de wrapper y de solo lectura.
def logging_decorator(func): def wrapper(): wrapper.count += 1 print "The function I modify has been called {0} times(s).".format( wrapper.count) func() wrapper.count = 0 return wrapper def a_function(): print "I'm a normal function." modified_function = logging_decorator(a_function) modified_function() # >> The function I modify has been called 1 time(s). # >> I'm a normal function. modified_function() # >> The function I modify has been called 2 time(s). # >> I'm a normal function.
Para acceder a los parámetros de una llamada aprovecharemos *args (que contiene los parámetros por defecto de la función) y kwargs (los especificados no por defecto en forma de diccionario). Puedes mirar el apartado de funciones.
=== Wraps en functools ===
Es un decorador que mantiene las propiedades el objeto función (como __name__, __class__...), que si no se utilizase quedaría con las propiedas de la función que lo decora.
<code python>
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
…
</code>
=== Sintaxis ===
Podemos emular el código anterior utilizando la sintaxis @nombre_func:
<code python>
def some_function():
print “I'm happiest when decorated.”
# Here we will make the assigned variable the same name as the wrapped function
some_function = logging_decorator(some_function)
# We can achieve the exact same thing with this syntax:
@logging_decorator
def some_function():
print “I'm happiest when decorated.”
</code>
=== Decoradores de clases ===
También pueden ser añadidos a clases para, por ejemplo, agregar nuevos parámetros:
<code python>
foo = ['important', 'foo', 'stuff']
def add_foo(klass):
klass.foo = foo
return klass
@add_foo
class Person(object):
pass
</code>
=== O clases de decoradores ===
<code python>
class IdentityDecorator(object):
def init(self, func):
self.func = func
def call(self):
self.func()
@IdentityDecorator
def a_function():
print “I'm a normal function.”
</code>
=== Argumentos a decoradores ===
<code python>
from functools import wraps
def argumentative_decorator(gift):
def func_wrapper(func):
@wraps(func)
def returned_wrapper(*args, kwargs):
print “I don't like this ” + gift + “ you gave me!”
return func(gift, *args, kwargs)
return returned_wrapper
return func_wrapper
@argumentative_decorator(“sweater”)
def grateful_function(gift):
print “I love the ” + gift + “! Thank you!”
grateful_function()
# » I don't like this sweater you gave me!
# » I love the sweater! Thank you!
</code>
<code python>
from functools import wraps
GLOBAL_NAME = “Brian”
def print_name(function=None, name=GLOBAL_NAME):
def actual_decorator(function):
@wraps(function)
def returned_func(*args, kwargs):
print “My name is ” + name
return function(*args, kwargs)
return returned_func
if not function: # User passed in a name argument
def waiting_for_func(function):
return actual_decorator(function)
return waiting_for_func
else:
return actual_decorator(function)
@print_name
def a_function():
print “I like that name!”
@print_name(name='Matt')
def another_function():
print “Hey, that's new!”
a_function()
# » My name is Brian
# » I like that name!
another_function()
# » My name is Matt
# » Hey, that's new!
</code>
=== Ejemplo memoization ===
Este decorator hace que, cuando la función es llamada con los mismos parámetros devuelva el mismo resultado (memoriza el cálculo):
<code python>
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def an_expensive_function(arg1, arg2, arg3):
</code>
=== Notas ===
===== El lenguaje =====
Python trabaja internamente con objetos. Pocos son los datos que se guardan como valores y lo que comunmente denominamos “variables” serían mejor llamadas “nombres”. Y es que una asignación es un enlace de un nombre a un objeto.
<code python>
»> foo = Foo()
»> foo = 10
»> print(foo.add)
<method-wrapper 'add' of int object at 0x8502c0>
</code>
Podríamos comprobar el string que devuelve una referencia con otra para saber si pertenecen al mismo objeto (ya que ese string representa la dirección de memoria en la que está almacenado). Aún así la forma de hacerlo correctamente es usando is. is
también es el comando indicado para comparar con None. Esto es porque None'' es un objeto singleton dentro del lenguaje.