Vientos de Libertad el blog de Juanjo Conti – en borrador permanente – abstracto, lúdico y digital
Browsing all posts in: Aprendiendo Python

os.path en el settings.py de Django para mayor comodidad

Marzo 3

En el archivo de configuración settings.py de un proyecto Django, por lo general tenemos que setear variables como MEDIA_ROOT o STATIC_DOC_ROOT. Su contenido en una instalación Windows suele ser algo como: 'C:\Windows\camino\hasta\mi\projecto'. Y en Linux: '/home/usuario/camino/a/mi/proyecto'. El problema surge cuando el proyecto es desarrollado en varias máquinas a la vez, y con distintos sistemas operativos. Más aún, si hacemos lo anterior, seguramente versionaremos el proyecto y con él, al archivo de configuración. No sería raro que tras una actualización, el archivo se actualice con los valores que puso algún compañero de trabajo.

Mi solución es definir primero una variable para el proyecto:

PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))

Luego podemos usarla para definir el path absoluto a la carpeta con archivos de media:

MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media')

nuestros templates:

TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(PROJECT_PATH, 'templates')
)

o cualquier otra variable de configuración que requiere una ruta de directorios.

Con esta solución podemos cambiar el proyecto de carpeta, disco o computadora y seguirá funcionando.

Aplicar un decorador a todas las funciones de un módulo en Python

Febrero 6

En la lista de PyAr preguntaron si había alguna forma de aplicar un decorador a todos las funciones de un módulo. Envié una solución sin probarla, que al verla unos días más tarde parece bastante buena :)

La comento aquí con un ejemplo. modulo.py contiene definiciones de funciones:

def a():
    pass

def b():
    print 42

def c():
    a()
    b()

y decoradores.py un decorador que imprime el nombre de la función llamada:

def nombrador(f):
    def inner(*a, **kw):
        print "Ejecutando %s" % f.__name__
        return f(*a, **kw)
    return inner

(Si no sabés lo que es un decorador, podés leer mi post Decoradores en Python I: Introducción)

En lugar de modificar las definiciones de funciones en modulo.py para aplicar el decorador a cada una de las funciones, ya sea usando el azúcar sintáctica de Python:

@nombrador
def a():
    ...

o mediante una llamada a la función:

a = nombrador(a)

podemos agregar el siguiente código al final de modulo.py:

for n,v in locals().items():
   if inspect.isfunction(v) and n != 'nombrador':
       locals()[n] = nombrador(v)

Vamos a explicarlo:

la llamada a la función built-in locals retorna un diccionario representando el espacio de nombres local: cada clave es un string representando el nombre de un objeto y cada valor es el objeto en si. Iteramos sobre la lista de pares (key, value) del mencionado dict y por cada uno verificamos si:

a) es una función (inspect.isfunction es apropiado para esto)
b) el nombre no es el del decorador que queremos aplicar (para no aplicar el decorador sobre si mismo!)

Si las condiciones a y b se cumplen, podemos guardar en el diccionario del espacio de nombres, bajo el nombre de la función que cumplió las condiciones, una versión decorada de la misma.

Agregamos algo más de código a modulo.py para que se llame a las funciones cuando lo ejecutemos:

if __name__ == '__main__':
    a()
    b()
    c()

Esta es la salida obtenida:

juanjo@fenix:~/python/muchosdecos$ python modulo.py
Ejecutando a
Ejecutando b
42
Ejecutando c
Ejecutando a
Ejecutando b
42

¿Querés probarlo? Bajá muchos.zip

Nota: para acceder a locals() no se puede utilizar iteritems por que el diccionario cambia durante la ejecución.

Generación espontanea de código

Enero 8

Software does not make itself. Code does not spontaneously come from the ether of the universe.

800px-Big_bang
El párrafo completo:

Software does not make itself. Code does not spontaneously come from the ether of the universe. Python is no exception to this rule. Since Python made its public debut back in 1991 many people beyond the BDFL (Benevolent Dictator For Life, Guido van Rossum) have helped contribute time and energy to making Python what it is today; a powerful, simple programming language available to all.

Fuente: http://python.org/dev/intro/

Big refactoring en dyntaint.py

Enero 8

Ayer llevé acabo un refactoring importante en mi proyecto Taint Mode para Python.

En resumen: dyntaint.py es un módulo que permite seguirle la huella a datos que ingresan a un programa con el objetivo de evitar que lleguen a ciertas áreas sensibles. Por ejemplo, que “42 or 1=1″ no llegue a una consulta SQL que se ejecutará contra una base de datos.

Si lo anterior no te dice nada, te recomiendo que leas mi presentación sobre el tema.

Hasta hoy, el registro de qué variables estaban manchas con qué tipo de manchas en una corrida del programa se llevaba en una estructura de datos auxiliar llamada TAINTED. Básicamente un diccionario en el cual cada clave se corresponde con un tipo de mancha (o vulnerabilidad), y cada valor es un conjunto de variables manchadas con el tipo de mancha de la clave correspondiente.

El refactoring consistió en cambiar de este esquema a uno en el cual cada variable manchada tiene un atributo (taints) que es un conjunto de identificadores de manchas. Entonces si antes tenía algo como:

{XSS: set(['manchado1', 'manchado2']),
SQLI: set(['manchado1'])}

ahora tengo:

>>> manchado1.taints
set([XSS, SQLI])
>>> manchado2.taints
set([XSS])

La revisión 59 también incluye algunas otras limpiezas. Para ver los cambios:

svn diff -r 58:59 http://svn.juanjoconti.com.ar/dyntaint/

Después de hacer las modificaciones necesarias, y corregir errores, las pruebas corren ok:

Ran 84 tests in 0.006s

OK

Podemos discutir los cambios en los comentarios.

Decoradores en Python (III) – Clases decoradoras

Diciembre 30

Siguiendo con la serie de posts sobre decoradores en Python, y fiel al espíritu que los originó (ir mostrando lo que voy aprendido a medida que necesito resolver problemas específicos o descubro aplicaciones concretas) hoy les traigo un nuevo uso para los decoradores en Python: funciones caché.
Read the rest of this entry »

Conjuntos en Python

Noviembre 2

Cuando estaba preparando mi charla introductoria a Python, le pasé mis slides a un amigo para que me diga su opinión y una de las cosas que me dijo fue

Nunca senti que set sea algo nativo de python, le daria mas importancia a los diccionarios, aunque tal vez tu publico este mas interesado en sets, no lo se.

Me sorprendió el comentario. Para mi, set es un tipo de dato muy útil y poderoso. En este post voy a intentar hacer una apología de set, el tipo de dato que incorpora Python para representar la noción matemática de conjunto.
Read the rest of this entry »

Charla Bienvenido a Python en Instituto Libre 09

Octubre 21

flyingEl viernes por la tarde se llevó a cabo el evento Instituto Libre 09 en la ciudad de Coronda, más precisamente en el Instituto Superior de Profesorado Nº 6 Dr. Leopoldo Chizzini Melo.

Presenté una charla llamada Bienvenido a Python. Es una introducción al lenguaje de programación Python bastante práctica y con muchos ejemplos. Los slides están disponibles en formato pdf bajo una licencia CC. El documento fue generado a partir de un archivo de texto utilizando rst2pdf.

Ejercicio 16 – proyecto Euler

Septiembre 18

De vez en cuando se me da por hacer saries de posts en el blog. Sagas. Una de las últimas fue la saga sobre resoluciones a problemas del proyecto Euler. Lo que intentaba era resolverlos con alguna característica interesante de Python y luego explicarla. Luego de los primeros, los ejercisios se volvieron más matemáticos y encontraba menos cosas interesantes de Python que comentar, así que dejé de postear mis soluciones. Acabo de encontrar esta entre mis archivos y creo que tiene algo de originalidad como para merecer ser publicada:

¿Cuál es la suma de los dígitos del número 2^(1000)?

>>> 2**1000
107150860718626732094842504906000[algunos números fueron intencionalmente borrados]
>>> a = 2**1000
>>> sum((int(x) for x in list(str(a))))
1366

¿A alguien se le ocurre otra forma?

Charla relámpago: Comet en Twisted

Septiembre 6

Ayer en las charlas relámpago de PyCon Argentina 2009 mostré cómo utilizar Comet desde Twisted. Tenía un slide disparador que no pude mostrar por que OpenOffice no se abrió y como las charlas relámpago duran 5 minutos, no podía darme el lujo de investigar que pasaba.

“No importa”, dije, “de todas formas el slide tenía solo cuatro palabras”. Y para hacer justicia sobre las Leyes de Murphy publico aquí ese slide:

cometEl ejemplo que mostré en vivo puede bajarse del sitio web de Nevow/Athena.

El Tutorial de Python en Español

Agosto 30

Hoy se ha publicado la versión HTML y PDF del tutoril oficial de Python en español. La traducción la hemos realizado varios voluntarios del grupo de usuarios de Python de Argentina.

http://python.org.ar/pyar/Tutorial
python-logo-master-v3-TM

Django Middleware

Agosto 15

Mientras estudié el tema para hacer un experimento, traduje el 80% de la documentación oficial. La dejo aquí más algunos condimentos personales.

Middleware

Es un sub framework que permite modificaciones al sistema de procesamiento de request/response de Django. Es un sistema de plugins liviano y de bajo nivel que permite alterar globalmente las entradas y salidas de Django.

Cada componente middleware es responsable de hacer alguna función específica.

Activar componentes middleware

Para hacerlo, añadirlo a la lista MIDDLEWARE_CLASSES en la configuración de Django (settings.py). En esta lista, cada componente se representa por un string: el camino completo al nombre de la clase. Por ejemplo:

MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)

En el tratamiento de requests y la generación de responses, existen dos faces:

1) Fase Request (se llama a los métodos process_request() y process_view())
2) Fase Response (se llama a los métodos process_response() y process_exception())

En la primera, las clases son aplicadas desde la primera a la última, según el orden de la lista mencionada. En la segunda fase, se aplican en orden inverso; podemos pensarlo como una cebolla:

middleware

Una instalación de Django puede funcionar sin ningún middleware, pero esto no es recomendado.

Para escribir middleware propios

Cada componente es una clase Python, que no tiene que extender a ninguna clase en particular y debe definir uno o más de los siguientes métodos.

process_request(self, request)

request es un objeto HttpRequest. El método es llamada por cada request, antes de que Django decida que vista ejecutar.

Debe retornar None o un objeto HttpResponse. Si retorna None, Django seguirá procesando el request, ejecutando los otros middlewares y luego la vista apropiada. Si retorna un objeto HttpResponse, Django no hará nada más, solo retornar ese objeto.

process_view(self, request, view_func, view_args, view_kwargs)

request es un objeto HttpRequest. view_func es la función Python que Django está por usar. (Es el objeto function, no el nombre de la función en un string). view_args es un alista de argumentos posicionales que serán pasados a la vista. Y view_kwargs es un diccionario de argumentos de palagra clave que serán pasados a la vista. Ni view_args ni view_kwargs incluye al primer argumento de la vista (request).

process_view() es llamado antes de que Django ejecute la vista. Debe retornar None o un objeto HttpResponse. Si retorna None, Django seguirá procesando el request, ejecutando otros process_view() y luego la vista apropiada. Si retorna un objeto HttpResponse, Django no hará nada más, solo retornar ese objeto.

process_response(self, request, response)

request es un objeto HttpRequest. response es un objeto HttpResponse retornado por una vista de Django.

process_response() debe retornar un objeto HttpResponse. Puede altener el objeto response dado o puede crear uno nuevo.

A diferencia de proces_request() y process_view(), este siempre es ejecutado.

process_exception(self, request, exception)

request es un objeto HttpRequest. exception es un objeto Exception lanzado por la vista.

Django llama a process_exception() cuando la vista lanza una excepción. process_exception() debe retornar None o un objeto HttpResponse. Si retorna un objeto HttpResponse, la respuesta es devuelta al navegador. De lo contrario, el sistema por defecto para manejo de excepciones entra en acción.

__init__

Por lo general estas clases no tienen estado; simplemente contienen a los anteriores métodos. Se puede usar el método __init__ pero se debe tener en cuenta que Django inicializa estas clases sin argumentos y que el método __init__ es llamado solo una vez, cuando el servidor web arranca.

Experimento

Con lo anterior en mente, me quedaron algunas dudas al respecto:

  • si modifico los argumentos de view_func en process_view y retorno None, ¿estos son pasados modificados a la vista?
  • si lo anterior es falso, puedo lograr el mismo efecto haciendo:
    def process_view(self,request, view_func, view_args, view_kwargs):
        # modificar request, view_args y view_kwargs
        return view_func(request, view_args, view_kwargs)

    ?

  • ¿puedo definir process_response de tal forma que examine response?

Una gran oportunidad de conocer Python!

Agosto 2

El 4 y 5 de Septiembre se va a llevar a cabo en Capital Federal el evento PyCon Argentina 2009.

Organizado por la comunidad, con entrada libre y gratuita, se llevará a cabo en la Universidad de Belgrano. Habrá charlas plenarias, programadas y relámpago.


PyCon 2009 Argentina

¿Querés conocer Python? ¿Venís oyendo sobre este lenguaje por mucho tiempo y todavía no te animaste a probarlo? Esta es tu oportunidad! Te esperamos!

Decoradores en Python (II) – Decoradores con parámetros

Julio 16

Un año después del primer artículo, llega el segundo. ¿Por qué tardó tanto? Por lo general mis artículos técnicos surgen de algún problema que se me presenta y para el cual necesito investigar antes de poder solucionarlo. El artículo anterior cubría todo lo que necesité hacer con decoradores en Python hasta el mes pasado, cuando necesité decoradores con parámetros.

Si no leíste el artículo anterior, te recomiendo que lo hagas antes de seguir: Decoradores en Python (I).
Read the rest of this entry »

La historia de Python: El problema con la división entre enteros

Julio 9

El siguiente texto es una traducción del artículo How Everything Became an Executable Statement de Guido van Rossum publicado en http://python-history.blogspot.com/.
Read the rest of this entry »

Euler 8 (Python)

Julio 2

Enunciado 8

Encontrar el mayor producto de cinco dígitos consecutivos en este número de 100 dígitos:

73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450

Solución

>>> b = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450
>>> s = str(b)
>>> s
'7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450'
>>> l = list(s)
>>> l
['7', '3', '1', '6', '7', '1', '7', '6', '5', '3', '1', '3', '3', '0', '6', '2', '4', '9', '1', '9', '2', '2', '5', '1', '1', '9', '6', '7', '4', '4', '2', '6', '5', '7', '4', '7', '4', '2', '3', '5', '5', '3', '4', '9', '1', '9', '4', '9', '3', '4', '9', '6', '9', '8', '3', '5', '2', '0', '3', '1', '2', '7', '7', '4', '5', '0', '6', '3', '2', '6', '2', '3', '9', '5', '7', '8', '3', '1', '8', '0', '1', '6', '9', '8', '4', '8', '0', '1', '8', '6', '9', '4', '7', '8', '8', '5', '1', '8', '4', '3', '8', '5', '8', '6', '1', '5', '6', '0', '7', '8', '9', '1', '1', '2', '9', '4', '9', '4', '9', '5', '4', '5', '9', '5', '0', '1', '7', '3', '7', '9', '5', '8', '3', '3', '1', '9', '5', '2', '8', '5', '3', '2', '0', '8', '8', '0', '5', '5', '1', '1', '1', '2', '5', '4', '0', '6', '9', '8', '7', '4', '7', '1', '5', '8', '5', '2', '3', '8', '6', '3', '0', '5', '0', '7', '1', '5', '6', '9', '3', '2', '9', '0', '9', '6', '3', '2', '9', '5', '2', '2', '7', '4', '4', '3', '0', '4', '3', '5', '5', '7', '6', '6', '8', '9', '6', '6', '4', '8', '9', '5', '0', '4', '4', '5', '2', '4', '4', '5', '2', '3', '1', '6', '1', '7', '3', '1', '8', '5', '6', '4', '0', '3', '0', '9', '8', '7', '1', '1', '1', '2', '1', '7', '2', '2', '3', '8', '3', '1', '1', '3', '6', '2', '2', '2', '9', '8', '9', '3', '4', '2', '3', '3', '8', '0', '3', '0', '8', '1', '3', '5', '3', '3', '6', '2', '7', '6', '6', '1', '4', '2', '8', '2', '8', '0', '6', '4', '4', '4', '4', '8', '6', '6', '4', '5', '2', '3', '8', '7', '4', '9', '3', '0', '3', '5', '8', '9', '0', '7', '2', '9', '6', '2', '9', '0', '4', '9', '1', '5', '6', '0', '4', '4', '0', '7', '7', '2', '3', '9', '0', '7', '1', '3', '8', '1', '0', '5', '1', '5', '8', '5', '9', '3', '0', '7', '9', '6', '0', '8', '6', '6', '7', '0', '1', '7', '2', '4', '2', '7', '1', '2', '1', '8', '8', '3', '9', '9', '8', '7', '9', '7', '9', '0', '8', '7', '9', '2', '2', '7', '4', '9', '2', '1', '9', '0', '1', '6', '9', '9', '7', '2', '0', '8', '8', '8', '0', '9', '3', '7', '7', '6', '6', '5', '7', '2', '7', '3', '3', '3', '0', '0', '1', '0', '5', '3', '3', '6', '7', '8', '8', '1', '2', '2', '0', '2', '3', '5', '4', '2', '1', '8', '0', '9', '7', '5', '1', '2', '5', '4', '5', '4', '0', '5', '9', '4', '7', '5', '2', '2', '4', '3', '5', '2', '5', '8', '4', '9', '0', '7', '7', '1', '1', '6', '7', '0', '5', '5', '6', '0', '1', '3', '6', '0', '4', '8', '3', '9', '5', '8', '6', '4', '4', '6', '7', '0', '6', '3', '2', '4', '4', '1', '5', '7', '2', '2', '1', '5', '5', '3', '9', '7', '5', '3', '6', '9', '7', '8', '1', '7', '9', '7', '7', '8', '4', '6', '1', '7', '4', '0', '6', '4', '9', '5', '5', '1', '4', '9', '2', '9', '0', '8', '6', '2', '5', '6', '9', '3', '2', '1', '9', '7', '8', '4', '6', '8', '6', '2', '2', '4', '8', '2', '8', '3', '9', '7', '2', '2', '4', '1', '3', '7', '5', '6', '5', '7', '0', '5', '6', '0', '5', '7', '4', '9', '0', '2', '6', '1', '4', '0', '7', '9', '7', '2', '9', '6', '8', '6', '5', '2', '4', '1', '4', '5', '3', '5', '1', '0', '0', '4', '7', '4', '8', '2', '1', '6', '6', '3', '7', '0', '4', '8', '4', '4', '0', '3', '1', '9', '9', '8', '9', '0', '0', '0', '8', '8', '9', '5', '2', '4', '3', '4', '5', '0', '6', '5', '8', '5', '4', '1', '2', '2', '7', '5', '8', '8', '6', '6', '6', '8', '8', '1', '1', '6', '4', '2', '7', '1', '7', '1', '4', '7', '9', '9', '2', '4', '4', '4', '2', '9', '2', '8', '2', '3', '0', '8', '6', '3', '4', '6', '5', '6', '7', '4', '8', '1', '3', '9', '1', '9', '1', '2', '3', '1', '6', '2', '8', '2', '4', '5', '8', '6', '1', '7', '8', '6', '6', '4', '5', '8', '3', '5', '9', '1', '2', '4', '5', '6', '6', '5', '2', '9', '4', '7', '6', '5', '4', '5', '6', '8', '2', '8', '4', '8', '9', '1', '2', '8', '8', '3', '1', '4', '2', '6', '0', '7', '6', '9', '0', '0', '4', '2', '2', '4', '2', '1', '9', '0', '2', '2', '6', '7', '1', '0', '5', '5', '6', '2', '6', '3', '2', '1', '1', '1', '1', '1', '0', '9', '3', '7', '0', '5', '4', '4', '2', '1', '7', '5', '0', '6', '9', '4', '1', '6', '5', '8', '9', '6', '0', '4', '0', '8', '0', '7', '1', '9', '8', '4', '0', '3', '8', '5', '0', '9', '6', '2', '4', '5', '5', '4', '4', '4', '3', '6', '2', '9', '8', '1', '2', '3', '0', '9', '8', '7', '8', '7', '9', '9', '2', '7', '2', '4', '4', '2', '8', '4', '9', '0', '9', '1', '8', '8', '8', '4', '5', '8', '0', '1', '5', '6', '1', '6', '6', '0', '9', '7', '9', '1', '9', '1', '3', '3', '8', '7', '5', '4', '9', '9', '2', '0', '0', '5', '2', '4', '0', '6', '3', '6', '8', '9', '9', '1', '2', '5', '6', '0', '7', '1', '7', '6', '0', '6', '0', '5', '8', '8', '6', '1', '1', '6', '4', '6', '7', '1', '0', '9', '4', '0', '5', '0', '7', '7', '5', '4', '1', '0', '0', '2', '2', '5', '6', '9', '8', '3', '1', '5', '5', '2', '0', '0', '0', '5', '5', '9', '3', '5', '7', '2', '9', '7', '2', '5', '7', '1', '6', '3', '6', '2', '6', '9', '5', '6', '1', '8', '8', '2', '6', '7', '0', '4', '2', '8', '2', '5', '2', '4', '8', '3', '6', '0', '0', '8', '2', '3', '2', '5', '7', '5', '3', '0', '4', '2', '0', '7', '5', '2', '9', '6', '3', '4', '5', '0']
>>> i = [int(x) for x in l]
>>> i
[7, 3, 1, 6, 7, 1, 7, 6, 5, 3, 1, 3, 3, 0, 6, 2, 4, 9, 1, 9, 2, 2, 5, 1, 1, 9, 6, 7, 4, 4, 2, 6, 5, 7, 4, 7, 4, 2, 3, 5, 5, 3, 4, 9, 1, 9, 4, 9, 3, 4, 9, 6, 9, 8, 3, 5, 2, 0, 3, 1, 2, 7, 7, 4, 5, 0, 6, 3, 2, 6, 2, 3, 9, 5, 7, 8, 3, 1, 8, 0, 1, 6, 9, 8, 4, 8, 0, 1, 8, 6, 9, 4, 7, 8, 8, 5, 1, 8, 4, 3, 8, 5, 8, 6, 1, 5, 6, 0, 7, 8, 9, 1, 1, 2, 9, 4, 9, 4, 9, 5, 4, 5, 9, 5, 0, 1, 7, 3, 7, 9, 5, 8, 3, 3, 1, 9, 5, 2, 8, 5, 3, 2, 0, 8, 8, 0, 5, 5, 1, 1, 1, 2, 5, 4, 0, 6, 9, 8, 7, 4, 7, 1, 5, 8, 5, 2, 3, 8, 6, 3, 0, 5, 0, 7, 1, 5, 6, 9, 3, 2, 9, 0, 9, 6, 3, 2, 9, 5, 2, 2, 7, 4, 4, 3, 0, 4, 3, 5, 5, 7, 6, 6, 8, 9, 6, 6, 4, 8, 9, 5, 0, 4, 4, 5, 2, 4, 4, 5, 2, 3, 1, 6, 1, 7, 3, 1, 8, 5, 6, 4, 0, 3, 0, 9, 8, 7, 1, 1, 1, 2, 1, 7, 2, 2, 3, 8, 3, 1, 1, 3, 6, 2, 2, 2, 9, 8, 9, 3, 4, 2, 3, 3, 8, 0, 3, 0, 8, 1, 3, 5, 3, 3, 6, 2, 7, 6, 6, 1, 4, 2, 8, 2, 8, 0, 6, 4, 4, 4, 4, 8, 6, 6, 4, 5, 2, 3, 8, 7, 4, 9, 3, 0, 3, 5, 8, 9, 0, 7, 2, 9, 6, 2, 9, 0, 4, 9, 1, 5, 6, 0, 4, 4, 0, 7, 7, 2, 3, 9, 0, 7, 1, 3, 8, 1, 0, 5, 1, 5, 8, 5, 9, 3, 0, 7, 9, 6, 0, 8, 6, 6, 7, 0, 1, 7, 2, 4, 2, 7, 1, 2, 1, 8, 8, 3, 9, 9, 8, 7, 9, 7, 9, 0, 8, 7, 9, 2, 2, 7, 4, 9, 2, 1, 9, 0, 1, 6, 9, 9, 7, 2, 0, 8, 8, 8, 0, 9, 3, 7, 7, 6, 6, 5, 7, 2, 7, 3, 3, 3, 0, 0, 1, 0, 5, 3, 3, 6, 7, 8, 8, 1, 2, 2, 0, 2, 3, 5, 4, 2, 1, 8, 0, 9, 7, 5, 1, 2, 5, 4, 5, 4, 0, 5, 9, 4, 7, 5, 2, 2, 4, 3, 5, 2, 5, 8, 4, 9, 0, 7, 7, 1, 1, 6, 7, 0, 5, 5, 6, 0, 1, 3, 6, 0, 4, 8, 3, 9, 5, 8, 6, 4, 4, 6, 7, 0, 6, 3, 2, 4, 4, 1, 5, 7, 2, 2, 1, 5, 5, 3, 9, 7, 5, 3, 6, 9, 7, 8, 1, 7, 9, 7, 7, 8, 4, 6, 1, 7, 4, 0, 6, 4, 9, 5, 5, 1, 4, 9, 2, 9, 0, 8, 6, 2, 5, 6, 9, 3, 2, 1, 9, 7, 8, 4, 6, 8, 6, 2, 2, 4, 8, 2, 8, 3, 9, 7, 2, 2, 4, 1, 3, 7, 5, 6, 5, 7, 0, 5, 6, 0, 5, 7, 4, 9, 0, 2, 6, 1, 4, 0, 7, 9, 7, 2, 9, 6, 8, 6, 5, 2, 4, 1, 4, 5, 3, 5, 1, 0, 0, 4, 7, 4, 8, 2, 1, 6, 6, 3, 7, 0, 4, 8, 4, 4, 0, 3, 1, 9, 9, 8, 9, 0, 0, 0, 8, 8, 9, 5, 2, 4, 3, 4, 5, 0, 6, 5, 8, 5, 4, 1, 2, 2, 7, 5, 8, 8, 6, 6, 6, 8, 8, 1, 1, 6, 4, 2, 7, 1, 7, 1, 4, 7, 9, 9, 2, 4, 4, 4, 2, 9, 2, 8, 2, 3, 0, 8, 6, 3, 4, 6, 5, 6, 7, 4, 8, 1, 3, 9, 1, 9, 1, 2, 3, 1, 6, 2, 8, 2, 4, 5, 8, 6, 1, 7, 8, 6, 6, 4, 5, 8, 3, 5, 9, 1, 2, 4, 5, 6, 6, 5, 2, 9, 4, 7, 6, 5, 4, 5, 6, 8, 2, 8, 4, 8, 9, 1, 2, 8, 8, 3, 1, 4, 2, 6, 0, 7, 6, 9, 0, 0, 4, 2, 2, 4, 2, 1, 9, 0, 2, 2, 6, 7, 1, 0, 5, 5, 6, 2, 6, 3, 2, 1, 1, 1, 1, 1, 0, 9, 3, 7, 0, 5, 4, 4, 2, 1, 7, 5, 0, 6, 9, 4, 1, 6, 5, 8, 9, 6, 0, 4, 0, 8, 0, 7, 1, 9, 8, 4, 0, 3, 8, 5, 0, 9, 6, 2, 4, 5, 5, 4, 4, 4, 3, 6, 2, 9, 8, 1, 2, 3, 0, 9, 8, 7, 8, 7, 9, 9, 2, 7, 2, 4, 4, 2, 8, 4, 9, 0, 9, 1, 8, 8, 8, 4, 5, 8, 0, 1, 5, 6, 1, 6, 6, 0, 9, 7, 9, 1, 9, 1, 3, 3, 8, 7, 5, 4, 9, 9, 2, 0, 0, 5, 2, 4, 0, 6, 3, 6, 8, 9, 9, 1, 2, 5, 6, 0, 7, 1, 7, 6, 0, 6, 0, 5, 8, 8, 6, 1, 1, 6, 4, 6, 7, 1, 0, 9, 4, 0, 5, 0, 7, 7, 5, 4, 1, 0, 0, 2, 2, 5, 6, 9, 8, 3, 1, 5, 5, 2, 0, 0, 0, 5, 5, 9, 3, 5, 7, 2, 9, 7, 2, 5, 7, 1, 6, 3, 6, 2, 6, 9, 5, 6, 1, 8, 8, 2, 6, 7, 0, 4, 2, 8, 2, 5, 2, 4, 8, 3, 6, 0, 0, 8, 2, 3, 2, 5, 7, 5, 3, 0, 4, 2, 0, 7, 5, 2, 9, 6, 3, 4, 5, 0]
>>> def mult(lista):
...     return reduce(mul, lista, 1)
...
>>> mult([])
1
>>> mult([1,2,3])
6
>>> for x in range(1001-5):
...     m = mult(i[x:x+5])
...     if m > maxmult:
...             maxmult = m
...
>>> maxmult
40824