Introducción a la Computación 0.5em Python avanzado

Introducción a la Computación
Python avanzado
Maximiliano Geier
Facultad de Ciencias Exactas y Naturales, UBA
24/06/2015
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
1 / 32
Programando a lo Python
Cada lenguaje de programación tiene múltiples formas de escribir las mismas cosas.
Recursión
for, while
Clases
Estructuras de datos
Ciertas construcciones son más comunes que otras, se habla de “idioms”.
En Python el “código idiomático” se lo conoce como pythónico1 .
Lo que se intenta es mejorar la legibilidad del código.
No necesariamente más lı́neas (ni menos) implica más legible, el criterio depende
mucho del lenguaje.
Vamos a ver algunos ejemplos de “idioms” en Python (y otros truquitos varios).
1
http://docs.python-guide.org/en/latest/writing/style/
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
2 / 32
Manejo de strings “tradicional”
Python es muy cómodo para procesar cadenas de caracteres. Algunos ejemplos de cosas
que se pueden hacer:
Construir una lista separando una cadena por algún separador:
>>> ’e.jem.plo’.split(’.’)
[’e’, ’jem’, ’plo’]
Volver a armarla:
>>> ’-’.join(’e.jem.plo’.split(’.’))
’e-jem-plo’
Particionar por un separador:
>>> ’primero:segundo:tercero’.partition(’:’)
(’primero’, ’:’, ’segundo:tercero’)
Separar el nombre de un archivo:
>>> ’/home/mgeier/laberinto.txt’.rpartition(’/’)
(’/home/mgeier’, ’/’, ’laberinto.txt’)
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
3 / 32
Manejo de strings “tradicional” (continuación)
Reemplazar texto:
>>> ’sarasa’.replace(’s’, ’z’)
’zaraza’
Prefijo:
>>> ’preprefijo’.startswith(’pre’)
True
>>> ’preprefijo’.startswith(’prefi’)
False
Eliminar un final de lı́nea:
>>> ’ejemplo\n’.rstrip(’\n’)
’ejemplo’
Espacios delante y detrás:
>>> ’
muchos espacios
’muchos espacios’
Maximiliano Geier (UBA)
’.strip()
Python avanzado
24/06/2015
4 / 32
Context managers
Podemos separar en un bloque con with el código que hace uso de un recurso, como
ser un archivo.
with open(’ejemplo_tp.txt’, ’r’) as f:
dim = f.readline()
cargarDimension(dim)
laberinto = f.readlines()
cargarLaberinto(laberinto)
Al salir del bloque with, el archivo f quedó cerrado (no hace falta llamar a
f.close() explı́citamente).
Podemos definir nuestros propios context managers en una clase:
__enter__(self)
__end__(self)
Los objetos que definen sus propios context managers son en general los que
modelan recursos que se piden y liberan (un archivo, una conexión de red, etc).
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
5 / 32
Listas por comprensión
Es una construcción que permite aplicar una función a cada elemento de una lista y
almacenar su resultado en una lista nueva todo en una sola lı́nea.
a = [fn(x) for x in lista]
Esto es equivalente al siguiente código:
a = []
for x in lista:
a.append(fn(x))
Un uso interesante es “anidarlas”:
with open(’tp_ej1.txt’, ’r’) as f:
puntos = [tuple(float(y) for y in x.split()) for x in f]
O filtrar elementos (“los x de lista tales que sean pares”):
pares = [x for x in lista if x % 2 == 0]
Ojo con la legibilidad:
a = [(float(x), float(y)) for x, y in [(’{:.4f}’.format( \
random.random()*10), ’{:.4f}’.format( \
random.random()*10)) for _ in range(10)]]
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
6 / 32
Regular expressions
Regular expressions (o regexp) es la forma moderna de procesar cadenas de
caracteres.
Usamos un lenguaje muy sucinto para describir patrones en las cadenas de
caracteres. Cada lenguaje de programación tiene su variante de lenguaje para
describir expresiones regulares, pero son bastante parecidos entre sı́.
En Python esto está implementado en el módulo re.
Podemos dar una definición recursiva de expresiones regulares:
Un sı́mbolo (que puede ser una letra, número o signo) es una expresión regular. Los
caracteres especiales se escapan con una barra invertida (\).
Los sı́mbolos ^ y $ corresponden a principio y final de lı́nea respectivamente.
El punto . es una expresión regular que corresponde a cualquier caracter.
[c1 c2 c3 ] es una expresión regular (“cualquier caracter que sea c1 , c2 o c3 ”).
\d ≡ [0-9] ≡ [0123456789] (cualquier dı́gito decimal).
[^\d] corresponde a cualquier caracter que no sea un dı́gito decimal.
\w ≡ [a-zA-Z] (cualquier letra).
Si a y b son expresiones regulares, ab es una expresión regular. (“a y después b”).
a|b es una expresión regular (“a o b”).
a* es una expresión regular (“a repetido 0 o más veces”).
a+ es una expresión regular (“a repetido 1 o más veces”).
Podemos capturar en grupos partes del match usando los paréntesis.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
7 / 32
Regular expressions (ejemplos)
Para procesar un timestamp:
>>> re.match(’(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})’, \
... ’2015-05-06T10:56:04’).groups()
(’2015’, ’05’, ’06’, ’10’, ’56’, ’04’)
Para procesar el laberinto:
with open(’laberinto.txt’, ’r’) as f:
dim = [int(x) for x in re.match(’Dim\((\d+),(\d+)\)’, \
f.readline()).groups()] # dim = [4, 4]
grid = []
for linea in f:
grid.append([[bool(int(y)) for y in x.split(’,’)] for x in \
[x for x in re.split(’[\[\]]’, linea.rstrip(’\n’)) \
if len(x) > 0]])
# grid[0] = [[True,True,False,False],[False,True,True,False], \
#
[True,True,False,True],[False,True,False,True]]
Buscar todos los números decimales truncando hasta un dı́gito fraccionario:
>>> s = ’{2, [4, 5]; 6; {3.1 -0.344}}’
>>> re.findall(’([^\d]|^)((\-|)\d+(\.\d|))’, s)
[(’{’,’2’,’’,’’),(’[’,’4’,’’,’’),(’ ’,’5’,’’,’’),(’ ’,’6’,’’,’’),
(’{’,’3.1’,’’,’.1’),(’ ’,’-0.3’,’-’,’.3’)]
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
8 / 32
Ordenamiento
Python incluye un algoritmo de ordenamiento que es una variante del mergesort. Para
usarlo existen dos formas:
Sobre la misma lista (in place):
>>> a = [1,5,7,23,0,-4,3,7,4,4,2]
>>> a.sort()
>>> a
[-4, 0, 1, 2, 3, 4, 4, 5, 7, 7, 23]
En una lista nueva:
>>> a = [1,5,7,23,0,-4,3,7,4,4,2]
>>> b = sorted(a)
>>> b
[-4, 0, 1, 2, 3, 4, 4, 5, 7, 7, 23]
>>> a
[1, 5, 7, 23, 0, -4, 3, 7, 4, 4, 2]
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
9 / 32
Formas de iterar listas
Con range (iterar los ı́ndices):
for i in range(len(a)):
print(a[i])
Con in lista (iterar los elementos):
for x in a:
print(x)
Esta forma funciona con cualquier “iterable” (cualquier estructura que uno pueda
recorrer)
Se puede definir cómo funciona el in para tipos propios definiendo el método especial
__iter__(self)
enumerate (ı́ndices y elementos al mismo tiempo):
for i, x in enumerate(a):
print(i, x)
Dos (o más) listas al mismo tiempo (ojo con las longitudes de a y de b):
for x, y in zip(a, b):
print(x, y)
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
10 / 32
Definiendo nuestro
iter
Recorrer filas y columnas en un tablero de ajedrez:
class Celda(object):
def __init__(self):
self.c = False
class Tablero(object):
def __init__(self, n):
self.tab = [[Celda() for _ in range(n)] for _ in range(n)]
def __iter__(self):
for f in self.tab:
for c in f:
yield c
Luego, recorremos con for celda in Tablero(n).
Árbol Binario:
class ArbolBinario(object):
...
def __iter__(self):
for r in self.inorder():
yield r
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
11 / 32
Otras formas de iterar
Recorrer en orden inverso:
>>> for x in reversed(a):
...
print(x, end=’ ’)
0 7 3 8 4 1
Recorrer una lista de forma ascendente en los ı́ndices pares y luego
descendente en los impares:
def asc_desc(a):
r = []
for n, x in enumerate(a):
if n % 2 == 1:
r.append(x)
else:
yield x
for x in reversed(r):
yield x
Luego recorremos con un for normal:
>>> a = [1, 4, 8, 3, 7, 0]
>>> for x in asc_desc(a):
...
print(x, end=’ ’)
1 8 7 0 3 4
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
12 / 32
itertools
Hay muchas formas de iterar ya definidas en el módulo itertools2 :
>>> [’’.join(x) for x in itertools.permutations(’HOLA’, 2)]
[’HO’, ’HL’, ’HA’, ’OH’, ’OL’, ’OA’, ’LH’, ’LO’, ’LA’,
’AH’, ’AO’, ’AL’]
Los
>>>
>>>
...
[1,
iteradores se pueden componer:
a = [1, 4, 8, 3, 7, 0]
list(itertools.chain(itertools.islice(a, 0, None, 2), \
reversed(a[1::2])))
8, 7, 0, 3, 4]
Ojo con la legibilidad (bis):
for n, elem in enumerate(lst):
for comb in itertools.imap(lambda x: (elem,) + x, \
itertools.combinations(itertools.compress(lst, \
repeat_except(1, size, 0, n)), r_tail)):
yield comb
2
http://docs.python.org/3/library/itertools.html
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
13 / 32
Formas de iterar diccionarios
También podemos usar el for variable in para iterar diccionarios:
>>> d = {1: ’Esteban’, 2:’Maria Elena’, 3:’Maximiliano’, 4:’Zombie’}
>>> for k in d:
...
print(k, end=’ ’)
1 2 3 4
>>> for v in d.values():
...
print(v, end=’ ’)
Esteban Maria Elena Maximiliano Zombie
>>> for t in d.items():
...
print(t, end=’ ’)
(1, ’Esteban’) (2, ’Maria Elena’) (3, ’Maximiliano’) (4, ’Zombie’)
Cada contenedor en Python (listas, tuplas, diccionarios, etc.) tiene definida su forma
de recorrerse. El uso del for nos abstrae esta noción sin que tengamos que
preocuparnos por qué estamos recorriendo.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
14 / 32
Otros métodos especiales
Además de __init__, __enter__, __leave__ y __iter__, otros métodos con nombres
especiales permiten definir cómo funcionan ciertas operaciones entre objetos de esa clase.
__eq__(self, b): igualdad entre self y b (devuelve un booleano).
__del__(self): destructor de la clase (no devuelve nada).
__str__(self): conversión de self a string (devuelve una cadena de caracteres).
Ejemplo: __str__(self) para ArbolBinario.
def __str__(self):
return ’, ’.join(self.inorder())
Uso:
>>> print(ab)
1, 3, 2
__repr__(self): representación textual para self.
def __repr__(self):
return str(self)
Uso:
>>> print([ab])
[’1, 3, 2’]
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
15 / 32
Equivalencias entre formas de iterar a lo Python y el while
for x in obj:
try:
i = iter(obj)
while True:
x = next(i)
# hago algo
except StopIteration:
pass
Para que esto funcione, tiene que estar definido __iter__(self) para el tipo de obj.
if x in obj:
try:
found = False
i = iter(obj)
while not next(i) == x:
pass
except StopIteration:
pass
else:
found = True
if found:
# hago algo
Este if requiere además que esté definido __eq__(self, b) para el tipo de x.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
16 / 32
Tuple unpacking
Pasar tuplas como listas de argumentos a funciones:
def f(x1, x2, x3):
return x1 + x2 + x3
x = (4, 5, 6)
print(f(*x))
También permite asignar los elementos de una tupla a variables separadas en
una lı́nea:
x1, x2, x3 = x
Swap:
>>> a = 1; b = 2
>>> b, a = a, b
>>> print(a, b)
2 1
Trasponer una matriz:
>>> a = [[0,4], [1,-1], [2,5], [3,0]]
>>> print(list(zip(*a)))
[(0, 1, 2, 3), (4, -1, 5, 0)]
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
17 / 32
Keyword arguments
Permite identificar parámetros de funciones por nombre en lugar de por su posición.
De esta manera algún argumento (o muchos o todos) podrı́an ser opcionales.
Se pueden mezclar con argumentos posicionales.
Los keyword arguments se acceden como un diccionario, donde la clave es el nombre
del parámetro.
def fn(*args, **kwargs):
if ’arg1’ in kwargs:
print(’me pasaron el argumento arg1’)
fn2(kwargs[’arg1’], args[0])
else:
fn3(**kwargs)
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
18 / 32
Funciones como parámetros de otras funciones
Las funciones en Python también son objetos (de la clase function), y como tales tienen
sus propios atributos y se los puede pasar como parámetro a otras funciones.
Factorización de código:
def tomarTiempos(fn, *args, **kwargs):
inicio = time.time()
res = fn(*args, **kwargs)
fin = time.time()
print(’función: {0} tiempo: {1}’.format(fn.__name__, fin-inicio))
return res
# llama a miTP(param1, param2, param3)
tomarTiempos(miTP, param1, param2, param3)
Funciones anónimas:
>>> a = [(2,’segundo’),(3,’tercero’),(4,’cuarto’),(1,’primero’)]
>>> sorted(a, key=lambda x: x[0])
[(1, ’primero’), (2, ’segundo’), (3, ’tercero’), (4, ’cuarto’)]
>>> sorted(a, key=lambda x: x[1])
[(4, ’cuarto’), (1, ’primero’), (2, ’segundo’), (3, ’tercero’)]
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
19 / 32
Funciones como parámetros de otras funciones (continuación)
Decorators:
def tomarTiempos(fn):
def f(*args, **kwargs):
inicio = time.time()
res = fn(*args, **kwargs)
fin = time.time()
print(’función: {0} tiempo: {1}’.format(fn.__name__, fin-inicio))
return res
return f
@tomarTiempos
def miTP(param1, param2):
# hacer el TP
return 0
Notar el def anidado.
Llamar a miTP definido con el decorator es equivalente al siguiente código:
f = tomarTiempos(miTP)
f(param1, param2) # f va a llamar a miTP
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
20 / 32
Esquemas de recursión
Muchos programas recursivos tienen estructuras parecidas.
Utilizando alto orden (funciones como parámetro), se pueden generalizar ciertas
funciones recursivas para que sean más fáciles de escribir y de entender. Los tres
esquemas de recursión que trae Python son map, filter y reduce.3
map(fn, lista): devuelve una lista como resultado de aplicar la función fn a cada
elemento de lista.
def map(fn, a):
if len(a) == 0:
return []
else:
return [fn(a[0])] + map(fn, a[1:])
3
http://www.python-course.eu/lambda.php
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
21 / 32
Esquemas de recursión (continuación)
filter(fn, a): devuelve una lista con todos los elementos de lista donde la
función fn devuelva True.
def filter(fn, a):
if len(a) == 0:
return []
else:
if fn(a[0]):
return [a[0]] + filter(fn, a[1:])
else:
return filter(fn, a[1:])
reduce(fn, a): aplica una función fn de dos parámetros a cada elemento de a y el
resultado de reducir el resto de la lista.
def reduce(fn, a):
if len(a) == 1:
return a[0]
else:
return fn(a[0], reduce(fn, a[1:]))
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
22 / 32
Esquemas de recursión (ejemplos)
Lista con los cuadrados de cada elemento:
def cuadrados(a):
return map(lambda x: x**2, a)
Lista de valores absolutos:
def listaDeAbs(a):
return map(abs, a)
Buscar y eliminar:
def buscarYEliminar(a, x):
return filter(lambda y: y != x, a)
Suma de todos los elementos de a:
def suma(a):
return reduce(lambda x, y: x + y, a)
Cantidad de apariciones:
def cantidadApariciones(a, x):
return suma(map(lambda w: 1 if w == x else 0, a))
Todos los elementos de a son True:
def todosTrue(a):
return reduce(lambda x, y: x and y, a)
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
23 / 32
Esquemas de recursión (más ejemplos)
Todos pares:
def todosPares(a):
return todosTrue(map(lambda x: x % 2 == 0, a))
Máximo de una lista:
def maximo(a):
return reduce(lambda x, y: x if x > y else y, a)
Suma de los valores absolutos de cada elemento de a:
def sumaAbsolutos(a):
return suma(listaDeAbs(a))
Nota de implementación: en Python 3 map y filter no devuelven listas sino
generadores. Los generadores son estructuras iterables (con for), pero su ventaja es que
no se construyen los resultados hasta que se intenta acceder a ellos, mientras que su
desventaja es que no vamos a obtener todos los elementos hasta que no hagamos un for.
Para lograr este efecto hay que usar listas por comprensión.
Finalmente, para usar reduce hay que importarla del módulo functools.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
24 / 32
MapReduce
El concepto de map se aplica a la programación paralela para resolver problemas del
tipo embarrassingly parallel.
Map, combinado con los pasos de shuffle y reduce, inspiraron un modelo de cómputo
para procesar distribuidamente grandes volúmenes de datos que se llama
MapReduce4 .
Este modelo, sumado a un filesystem distribuido para obtener los datos, permite
utilizar clusters para procesar enormes cantidades de datos en poco tiempo.
Hadoop es una plataforma libre de MapReduce muy utilizada.
Hay aplicaciones de MapReduce para Hadoop en múltiples campos del
conocimiento5 , siempre que se necesite procesar muchos datos.
Si bien Hadoop es una plataforma escrita en Java, los mappers y reducers de una
aplicación los podemos escribir en Python6 .
4
5
6
https://static.googleusercontent.com/media/research.google.com/es/us/archive/mapreduce-osdi04.pdf
http://bib.oxfordjournals.org/content/early/2013/02/07/bib.bbs088.full
http://www.michael-noll.com/tutorials/writing-an-hadoop-mapreduce-program-in-python/
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
25 / 32
Equivalencia entre listas por comprensión y esquemas recursivos
map:
[fn(x) for x in a]
map(fn, a)
filter:
[x for x in a if fn(x)]
filter(fn, a)
reduce no tiene una equivalencia en listas por comprensión:
# versión Pythónica
acc = a[0]
for x in a[1:]:
acc = fn(acc, x)
# reduce
acc = reduce(fn, a)
La forma preferida del creador de Python es el uso de listas por comprensión, o for
explı́citos en caso de que no se pueda.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
26 / 32
Duck Typing
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that
bird a duck.
James Whitcomb Riley (1849–1916).
El sistema de tipos de Python permite que una función pensada para operar con un tipo de
datos en particular, pueda funcionar con otro diferente siempre y cuando las operaciones
que dicha función tiene que hacer sobre los operandos estén soportadas por ambos tipos de
datos.
Estas reglas sobre los tipos se las conoce como Duck Typing.
En lenguajes que no hacen uso de Duck Typing, es necesario establecer otro tipo de
mecanismos para que objetos de diferentes clases puedan funcionar como parámetros de una
misma función.
class Pato(object):
def quack():
>>> p1 = Persona()
return ’Cuac’
>>> p2 = Pato()
class Persona(object):
>>> duckify(p1)
def quack():
’Imito a un pato’
return ’Imito a un pato’
>>> duckify(p2)
def duckify(d):
’Cuac’
return d.quack()
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
27 / 32
Introspection
A diferencia de ciertos lenguajes, Python nos permite pedir información del sistema de tipos a los
objetos del lenguaje:
isinstance(obj, cls): devuelve True si obj es una instancia de la clase cls (o de alguna
de sus subclases).
>>> l = Laberinto()
>>> isinstance(l, object)
True
issubclass(subcls, cls): devuelve True si subcls es una subclase de cls.
>>> issubclass(TypeError, Exception)
True
getattr(obj, s): devuelve el atributo o el método del objeto obj con nombre s:
>>> m = getattr(l, ’resuelto’)
>>> m
<bound method Laberinto.resuelto of <Laberinto object at 0x7f23a7f217f0>>
>>> m()
False
setattr(obj, k, v): define un atributo de nombre k en obj, con el valor v:
>>> setattr(obj, ’resuelto’, lambda: True)
>>> obj.resuelto()
True
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
28 / 32
Evaluación de código definido en runtime
eval ejecuta código definido en una cadena de caracteres (no se pueden asignar
variables nuevas):
>>> a = "__import__(’time’).time()"
>>> eval(a)
1400870545.0649445
>>> __import__(’time’).time()
1400870677.7443652
>>> eval(’2**4’)
16
>>> eval(’r = 2**4’)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
r = 2**4
^
SyntaxError: invalid syntax
El código se evalúa como una cadena de texto, ası́ que podrı́a venir de cualquier lado:
la consola, un archivo, Internet. Ojo si no se tiene control de qué ejecutamos:
>>> eval(’__import__("os").system("rm -rf /")’)
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
29 / 32
Ejecutando programas externos
Podemos utilizar Python para sistematizar la ejecución de experimentos computacionales,
es decir, una serie de comandos tediosos y repetitivos que tenemos que ejecutar
secuencialmente para obtener nuestro resultado.
Estas funciones están incluidas en el módulo subprocess.
Para ejecutar programas y capturar su salida:
>>> subprocess.check_output(’echo Hello, world!’, shell=True)
b’Hello, world!\n’
También podemos “usar” programas interactivos:
>>> with subprocess.Popen(shlex.split(’mkpasswd -s’), \
... stdin=subprocess.PIPE, stdout=subprocess.PIPE) as p:
...
password, err = p.communicate(b’hola\n’)
...
>>> password
b’m11/.T/.UGjJY\n’
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
30 / 32
Pidiendo ayuda
dir(obj): devuelve una lista con todos los métodos o atributos que tiene definido el objeto
obj.
>>> l = Laberinto()
>>> dir(l)
[’__class__’, ’__delattr__’, ’__dict__’, ’__dir__’, ’__doc__’, ’__eq__’,
’__format__’, ’__ge__’, ’__getattribute__’, ’__gt__’, ’__hash__’, ’__init__’,
’__le__’, ’__lt__’, ’__module__’, ’__ne__’, ’__new__’, ’__reduce__’,
’__reduce_ex__’, ’__repr__’, ’__setattr__’, ’__sizeof__’, ’__str__’,
’__subclasshook__’, ’__weakref__’, ’cargar’, ’esPosicionQueso’,
’esPosicionRata’, ’get’, ’getInfoCelda’, ’getPosicionQueso’,
’getPosicionRata’, ’parent’, ’resetear’, ’resolver’, ’resuelto’]
También se puede usar la tecla Tab para autocompletar nombres de métodos o clases desde
el intérprete de Python (no funciona en Python 2).
help(obj): muestra en un formato amigable el texto escrito en obj.__doc__.
Ejecutando desde la terminal pydoc3 -b, se abre un browser con la documentación de los
help de cada módulo que haya instalado en el sistema (incluyendo los de la carpeta desde la
que se ejecutó pydoc), vistos como una página web.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
31 / 32
Terminando
>>> import __hello__
Hello world!
>>> import antigravity
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough
to break the rules.
Although practicality beats purity.
If the implementation is hard
to explain, it’s a bad idea.
If the implementation is easy
to explain, it may be a good idea.
Maximiliano Geier (UBA)
Python avanzado
24/06/2015
32 / 32