Herramientas de usuario

Herramientas del sitio


script:python:new:advanced

¡Esta es una revisión vieja del documento!


Python avanzado

Pequeños conceptos

Comprehesion lists

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'}

Generators

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" 

yield

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

Context manager (with)

  • Son usados para estar seguros de que un recurso queda liberado después de utilizarlo.
  • Su sintaxis es: 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.

Reflection

# 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.
  • El método 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])

Expresiones lambda

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.

Sintaxis

lambda <parametro1, parametro2…> : <línea de código devuelta>

a = lambda x,y : x*y
a(3,4)						# devolvería 12

Llamada a una expresión lambda sin almacenar

(lambda x: x*2)(3)				# devolvería 6

Map (high-order function)

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]

Filter (high-order function)

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]

Reduce (high-order function)

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

Ejemplos de uso

Función de comparación por longitud:

my_cmp = (lambda x,y: cmp(len(x), len(y)))
my_cmp('abc', 'de')

Relación con las comprehesion lists

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]

Tipos de datos

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

Decorators

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.

*args y **kwargs

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.

script/python/new/advanced.1379243419.txt.gz · Última modificación: 2020/05/09 09:24 (editor externo)