1. Introspección
En Python, todo es un objeto. Instrospección es cuando cierto código observa otros módulos o funciones en la memoria como objetos, obteniendo información de ellos o manipulándolos.
2. Ejemplo apihelper.py
def info(object, spacing=10, collapse=1):Repaso de secciones anteriores
"""Print methods and doc strings.
Takes module, class, list, dictionary, or string."""
methodList = [method for method in dir(object) if callable(getattr(object, method))]
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
if __name__ == "__main__":
print info.__doc__
- info es una función con tres parámetros.
- info tiene documentación multilínea. No tiene ningún return, esta función se usará por sus efectos mas que por sus valores
- El código dentro de la función está indentado
- El truco if __name__ permite que el módulo provea una funcionalidad cuando se lo corre por si mismo, sin interferir en casos que sea usado por otros módulos
- La expresión if usa == para comparación, los paréntesis no son necesarios
Uso de apihelper.py
>>> from apihelper import info
>>> li = []
>>> info(li)
append L.append(object) -- append object to end
count L.count(value) -> integer -- return number of occurrences of value
extend L.extend(list) -- extend list by appending list elements
index L.index(value) -> integer -- return index of first occurrence of value
insert L.insert(index, object) -- insert object before index
pop L.pop([index]) -> item -- remove and return item at index (default last)
remove L.remove(value) -- remove first occurrence of value
reverse L.reverse() -- reverse *IN PLACE*
sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1
>>> import odbchelper
>>> info(odbchelper)
buildConnectionString Build a connection string from a dictionary Returns string.
>>> info(odbchelper, 30)
buildConnectionString Build a connection string from a dictionary Returns string.
>>> info(odbchelper, 30, 0)
buildConnectionString Build a connection string from a dictionary
Returns string.
3. Usando argumentos opcionales y con nombres
Python permite definir valores por defecto para los argumentos de funciones. Cuando la función es llamada sin argumentos, estos toman los valores por defecto.
Ejemplo:
def info(object, spacing=10, collapse=1):
info(odbchelper)
info(odbchelper, 12)
info(odbchelper, collapse=0)
info(spacing=15, object=odbchelper)
Casos como la última línea pueden generar mucha confusión hasta que uno descubre que los argumentos son simples diccionarios.
Mas Lectura
- Python Tutorial explica cuando y como son evaluados los argumentos por defecto, lo cual es de suma importancia cuando los valores son una lista o expresión con efectos colaterales.
4. Using type, str, dir, and Other Built-In Functions
4.1. La función type
La función type devuelve el tipo de un objeto arbitrario. La lista de tipos posibles son listados en el módulo type. Este método es de mucha ayuda cuando existen funciones que pueden tomar distintos tipos.
>>> type(1)
<type 'int'>
>>> li = []
>>> type(li)
<type 'list'>
>>> import odbchelper
>>> type(odbchelper)
<type 'module'>
>>> import types
>>> type(odbchelper) == types.ModuleType
True
4.2. La función str
str convierte datos en un string. Todos los tipos de datos soportan str.
>>> str(1)
'1'
>>> horsemen = ['war', 'pestilence', 'famine']
>>> horsemen
['war', 'pestilence', 'famine']
>>> horsemen.append('Powerbuilder')
>>> str(horsemen)
"['war', 'pestilence', 'famine', 'Powerbuilder']"
>>> str(odbchelper)
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>"
>>> str(None)
'None'
4.3. La función dir
>>> li = []
>>> dir(li)
['append', 'count', 'extend', 'index', 'insert',
'pop', 'remove', 'reverse', 'sort']
>>> d = {}
>>> dir(d)
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', 'values']
>>> import odbchelper
>>> dir(odbchelper)
['__builtins__', '__doc__', '__file__', '__name__', 'buildConnectionString']
4.4. La función callable
>>> import string
>>> string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
>>> string.join
<function join at 00C55A7C>
>>> callable(string.punctuation)
False
>>> callable(string.join)
True
>>> print string.join.__doc__
join(list [,sep]) -> string
Return a string composed of the words in list, with
intervening occurrences of sep. The default separator is a
single space.
(joinfields and join are synonymous)
4.5. Otras funciones built-in
type, str, dir, y todas las funciones internas de python están agrupadas en un módulo especial llamado __builtin__
>>> from apihelper import info
>>> import __builtin__
>>> info(__builtin__, 20)
ArithmeticError Base class for arithmetic errors.
AssertionError Assertion failed.
AttributeError Attribute not found.
EOFError Read beyond end of file.
EnvironmentError Base class for I/O related errors.
Exception Common base class for all exceptions.
FloatingPointError Floating point operation failed.
IOError I/O operation failed.
[...snip...]
Mas lectura de funciones Built-In
5. Obteniendo referencias de objetos con getattr
Las funciones son objetos en Python. Usando el método getattr es posible obtener una referencia de una función sin saber su nombre hasta estar en tiempo de ejecución.>>> li = ["Larry", "Curly"]
>>> li.pop
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop")
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe")
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear")
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop")
Traceback (innermost last):
File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop'
>>> li = [1,2,3,4,5]
>>> pop = getattr(li, "pop")
>>> li.append(6)
>>> li.append(7)
>>> li.append(8)
>>> pop()
8
>>> li
[1, 2, 3, 4, 5, 6, 7]
getattr y módulos
>>> import odbchelper
>>> odbchelper.buildConnectionString
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString")
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method)
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method))
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
True
>>> callable(getattr(object, method))
True
getattr como dispatcher
Un uso comun de getattr es como implementación del patron dispatcher. Por ejemplo, si se posee un programa cuya salida soporta diferentes formatos, se puede definir una función para cada formato y una sola función dispatcher para llamar a la función adecuada.Supongamos que tenemos un módulo statsout defines con tres funciones output_html, output_xml, and output_text. Y luego, se define una función:
import statsout
def output(data, format="text"):
output_function = getattr(statsout, "output_%s" % format)
return output_function(data)
Valores por defecto de getattr
import statsout
def output(data, format="text"):
output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
return output_function(data)
6. Filtrando listas
Las listas por comprensión pueden combinarse con un mecanismo de filtrado y como resultado unos elementos son mapeados y otros removidos.
[mapping-expression for element in source-list if filter-expression]
Ejemplo
>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"]Volviendo a apihelper.py
>>> [elem for elem in li if len(elem) > 1]
['mpilgrim', 'foo']
>>> [elem for elem in li if elem != "b"]
['a', 'mpilgrim', 'foo', 'c', 'd', 'd']
>>> [elem for elem in li if li.count(elem) == 1]
['a', 'mpilgrim', 'foo', 'c']
methodList = [method for method in dir(object) if callable(getattr(object, method))]
Mas lectura
- Python Tutorial discute sobre otro modo de filtrar sobre listas usando la función interna filter.
7. La peculiar naturaleza de and y or
En python, and y or realizan operaciones lógicas, pero no retornan valores booleanos; en lugar de ellos devuelven uno de los valores comparados.>>> 'a' and 'b'
'b'
>>> '' and 'b'
''
>>> 'a' and 'b' and 'c'
'c'
- and evalua de izquierda a derecha. 0, '', [], (), {}, None son False en un contexto booleano, todo lo demás True.
- Si cualquier valor es false, and devuelve el primer atributo falso
- Si todos son verdaderos se devuelve el último valor
>>> 'a' or 'b'
'a'
>>> '' or 'b'
'b'
>>> '' or [] or {}
{}
>>> def sidefx():
... print "in sidefx()"
... return 1
>>> 'a' or sidefx()
'a'
- or evalua todo los elementos de izquierda a derecha. Si un valor es verdadero, or lo retorna inmediatamente
- si todos los elementos son falsos, or devuelve el último elemento
Mas Lectura
8. Funciones lambda
Python soporta una funcionalidad interesante que permite definir mini-funciones de una línea. Tomado de Lisp, las funciones lambda pueden usarse en cualquier sitio que se requiera una función.>>> def f(x):
... return x*2
...
>>> f(3)
6
>>> g = lambda x: x*2
>>> g(3)
6
>>> (lambda x: x*2)(3)
6
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
>>> s = "this is\na\ttest"
>>> print s
this is
a test
>>> print s.split()
['this', 'is', 'a', 'test']
>>> print " ".join(s.split())
'this is a test'
Mas lectura sobre funciones lambda
- Python Knowledge Base discute sobre la llamda de funciones lambda de forma indirecta.
- Python Tutorial muestra como acceder a variables declarada fuera de la lambda function. (PEP 227 como se cambiará esto en el futuro)
- The Whole Python FAQ posee ejempos de progamas de una linea ofuscados usando lambda.
9. Integrando todo
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
for method in methodList
>>> import odbchelper
>>> object = odbchelper
>>> method = 'buildConnectionString'
>>> getattr(object, method)
<function buildConnectionString at 010D6D74>
>>> print getattr(object, method).__doc__
Build a connection string from a dictionary of parameters.
Returns string.
¿ Por qué usar str en una doc string ?
>>> def foo(): print 2
>>> foo()
2
>>> foo.__doc__
>>> foo.__doc__ == None
True
>>> str(foo.__doc__)
'None'
>>> s = 'buildConnectionString'
>>> s.ljust(30)
'buildConnectionString '
>>> s.ljust(20)
'buildConnectionString'
Imprimiendo una lista
>>> li = ['a', 'b', 'c']
>>> print "\n".join(li) 1
a
b
c
Ahora se deberia entender sin problemas
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
10. Resumen
Apihelper deberia tener sentido ahora:
def info(object, spacing=10, collapse=1):
"""Print methods and doc strings.
Takes module, class, list, dictionary, or string."""
methodList = [method for method in dir(object) if callable(getattr(object, method))]
processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(str(getattr(object, method).__doc__)))
for method in methodList])
if __name__ == "__main__":
print info.__doc__
Uso de apihelper.py
>>> from apihelper import info
>>> li = []
>>> info(li)
append L.append(object) -- append object to end
count L.count(value) -> integer -- return number of occurrences of value
extend L.extend(list) -- extend list by appending list elements
index L.index(value) -> integer -- return index of first occurrence of value
insert L.insert(index, object) -- insert object before index
pop L.pop([index]) -> item -- remove and return item at index (default last)
remove L.remove(value) -- remove first occurrence of value
reverse L.reverse() -- reverse *IN PLACE*
sort L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1
Ejemplo: usando gettattr para ejecutar métodos de listas en tiempo de ejecución
def codigo():
lista = []
while len(lista) < 3:
lista.append(raw_input('un numero: ') )
orden = ''
while orden != 'end':
orden = raw_input('++ >>> ')
f = getattr(lista,orden, dir(lista) )
if type(f)==type([]):
print 'No es un comando de lista, los validos son: \n', f
else:
print 'f: ', str(f())
print 'lista: ', lista
Ejercicios
- Escribir los méteodos output_html, output_xml y output_text. Los dos primeros métodos toman como parámetros opcionales los campos en los cuales está el texto, por ejemplo:
>>> output_html('data')
'<html><head>data</head><body>data</body></html>'
>>> output_xml('data',)
'<xml>data</xml>'
>>> output_text('data', 'title')
'data'>>> output_html('data', 'b')
'<html><head>data</head><body><b>data</b></body></html>'
>>> output_xml('data', 'title')
'<xml><title>data</title></xml>'
>>> output_text('data', 'title')
'Title: data' -
Verificar str con valores de los diferentes tipos definidos en el módulo de type.
-
Usando listas por compresión, filtrado y range hacer y una expresión para calcular lo que esta en italica:
- un programa que calcule el producto de los números pares del 0 al 100
- un programa que calcule el producto de los números impares del 0 al 100
- un programa que calcule el producto de los números primos del 0 al 100 (Opcional)
- suc es una función que dado un número n retorna n+1, usando una función lambda escribir suc
- Usando la función cmp escribir un método que permita comparar si 3 o 2 números a, b, c cumplen a < b < c y tambien a < b. No usar el operador < ni >