Módulos en Python

La programación modular consiste en subdividir programas de manera organizada para mejorar la legibilidad y aumentar significativamente la reutilización de código. Les mostraré su utilidad con este ejemplo: tenemos un script que se encarga de crear figuras geométricas:

class Triangulo:
	nombre = "Triángulo"

	def __init__(self, lados):
		self.lados = lados

		print("Se creó un triángulo!")

class Circulo:
	nombre = "Círculo"

	def __init__(self, radio):
		self.radio = radio

		print("Se creó un círculo!")

class Cuadrado:
	nombre = "Cuadrado"

	def __init__(self, lado):
		self.lado = lado

		print("Se creó un cuadrado!")

a = Triangulo((3, 2, 2))
b = Circulo(1)
c = Cuadrado(2)

Como se definen todas las clases en el mismo script, se vuelve un poco largo el código y si alguien quiere leerlo probablemente le parezca enredado, así que la mejor forma de solucionar este problema es separarlo en varios archivos:

figuras.py:

class Triangulo:
	nombre = "Triángulo"

	def __init__(self, lados):
		self.lados = lados

		print("Se creó un triángulo!")

class Circulo:
	nombre = "Círculo"

	def __init__(self, radio):
		self.radio = radio

		print("Se creó un círculo!")

class Cuadrado:
	nombre = "Cuadrado"

	def __init__(self, lado):
		self.lado = lado

		print("Se creó un cuadrado!")

script.py:

a = Triangulo((3, 2, 2))
b = Circulo(1)
c = Cuadrado(2)

Claro, así todavía no funciona pues solo lo separamos, si queremos utilizar las clases tenemos que importar el archivo que las contiene (figuras.py) y para esto existen dos formas:

  • Crear un objeto que contenga todos los elementos (variables, funciones, clases, etc…) del archivo:
import figuras

a = figuras.Triangulo((3, 2, 2))
b = figuras.Circulo(1)
c = figuras.Cuadrado(2)

Se puede utilizar la palabra reservada as para renombrar el objeto import figuras as fig en caso de que ya exista algún elemento del script con el mismo identificador o simplemente para reducir su tamaño.

  • Agregar los elementos del archivo importado al actual:
from figuras import *

a = Triangulo((3, 2, 2))
b = Circulo(1)
c = Cuadrado(2)

El * simboliza todos los elementos, si se quisiera importar alguno en específico solo haría falta cambiar el * por su nombre from figuras import Triangulo. Al igual que el método anterior puede usarse la palabra reservada as pero solo cuando se importa un elemento from figuras import Triangulo as Tri o varios separados por comas from figuras import Triangulo as Tri, Circulo as Cir.

 

En ambos casos, el archivo figuras.py debe estar en la misma carpeta que script.py. Los archivos que se importan son llamados Módulos y su función principal es contener las herramientas necesarias para ejecutar ciertos procesos. Python también usa Paquetesque son simplemente carpetas que contienen módulos, en el mundo de la programación se les conoce como bibliotecas o repositorios de código; para configurar paquetes solo hace falta crear en el interior de una carpeta un archivo vacío llamado __init__.py, bastante sencillo no? para usar los módulos dentro de un paquete tenemos que anteponer el nombre del paquete cuando lo importamos, adaptemos el ejemplo para entenderlo mejor aún:

.
├─ geometria/
│  ├── __init__.py
│  ├── circulo.py
│  ├── cuadrado.py
│  └── triangulo.py
└─ script.py

circulo.py:

class Circulo:
	nombre = "Círculo"

	def __init__(self, radio):
		self.radio = radio

		print("Se creó un círculo!")

cuadrado.py:

class Cuadrado:
	nombre = "Cuadrado"

	def __init__(self, lado):
		self.lado = lado

		print("Se creó un cuadrado!")

triangulo.py:

class Triangulo:
	nombre = "Triángulo"

	def __init__(self, lados):
		self.lados = lados

		print("Se creó un triángulo!")

script.py:

import geometria.circulo, geometria.cuadrado, geometria.triangulo

a = geometria.triangulo.Triangulo((3, 2, 2))
b = geometria.circula.Circulo(1)
c = geometria.cuadrado.Cuadrado(2)

 

Si se crean más subpaquetes, el proceso sería el mismo:

.
└─ paquete/
   ├── __init__.py
   └─ subpaquete/
      ├── __init__.py
      └── modulo.py

Y para importar los módulos:

import paquete.subpaquete.modulo
import paquete.subpaquete.modulo as modulo  # Se puede usar "as"

 

Hasta ahora, los módulos y paquetes que creemos deben estar en la misma carpeta del script que los importará, si queremos alterar este comportamiento tenemos que decirle a Python donde queremos que los busque y para esto existen dos formas:

  • Temporal (mientras dure la ejecución del script):

Hay una variable de Python que contiene una lista de ubicaciones donde buscar módulos, solo haría falta agregarle la ruta a la carpeta que contiene los módulos y ya podríamos usarlos aunque con una diferencia, no será necesario crear el archivo__init__.py ni anteponer el nombre del paquete (solo en la carpeta principal, sus subcarpetas si necesitan que se hagan las dos). Les explico con un ejemplo:

.
├─ geometria/
│  ├── circulo.py
│  ├── cuadrado.py
│  └── triangulo.py
└─ Python/
   └── script.py

script.py:

import sys

sys.path += ["../geometria"]

import triangulo

a = triangulo.Triangulo((3, 2, 2))

Resultado:

Se creó un triángulo!

Como ven, sys.path soporta rutas relativas, pero esto es un poco peligroso pues esta ruta será relativa a la carpeta desde la que se está ejecutando el script y no en la que está almacenado, es decir, si lo ejecutamos desde /home/ntrrg/Descargas/Python/ (que es donde yo lo guardé) entonces ../geometria equivaldrá a /home/ntrrg/Descargas/geometria/y el resultado sería el esperado; si se ejecuta desde /home/ntrrg/Descargas/ entonces equivaldrá a /home/ntrrg/geometria/ y apuntará a una ruta que no existe. Con una ruta absoluta quedaría así:

script.py:

import sys

sys.path += ["/home/ntrrg/Descargas/geometria"]

import triangulo

a = triangulo.Triangulo((3, 2, 2))
  • Permanente:

Se debe agregar la siguiente línea en el archivo /etc/profile y reiniciar la sesión de usuario:

export PYTHONPATH=$PYTHONPATH:/home/ntrrg/Descargas/geometria

# Para agregar varias ubicaciones:
export PYTHONPATH=$PYTHONPATH:<ruta>:<otraRuta>

Me disculpo con los que estén usando Windows, pero realmente no conozco el equivalente a este método.

 

Todas las sentencias escritas en el cuerpo de los módulos serán ejecutadas cuando se importen, aunque es posible crear un bloque que se ejecute solo en caso de que el módulo sea llamado como un script, que utilidad tiene esto? bueno, imaginen que quieren obtener el área de un triángulo usando el módulo triangulo.py, sería muy fastidioso tener que crear un script solo para importarlo, así que les muestro como usarlo directamente:

# El primer parámetro será la altura y el segundo la base
python3 geometria/triangulo.py 3 3

Y los cambios en el módulo:

triangulo.py:

class Triangulo:
	nombre = "Triángulo"

	def __init__(self, lados):
		self.lados = lados

		print("Se creó un triángulo!")

# Bloque que se ejecuta solo al llamarse como script

if __name__ == "__main__":
	import sys  # Esta biblioteca nos deja leer los parámetros

	# sys.argv[0] contiene la ruta con la que fue ejecutado el módulo
	base = int(sys.argv[1])
	altura = int(sys.argv[2])

	print(base * altura / 2)

 

Por último, el estándar de Python tiene ciertas recomendaciones al momento de importar módulos:

  • Escribir las instrucciones de importación en las primeras líneas del script.
  • Ordenar los paquetes/módulos en tres secciones:
      <paquetes/módulos de Python>
      # Una línea vacía
      <paquetes/módulos de herramientas externas>
      # Una línea vacía
      <paquetes/módulos propios>
    
  • Organizar alfabéticamente los paquetes/módulos.

Python: validations

 

Validaciones

Las validaciones son técnicas que permiten asegurar que los valores con los que se vaya a operar estén dentro de determinado dominio.

Estas técnicas son particularmente importantes al momento de utilizar entradas del usuario o de un archivo (o entradas externas en general) en nuestro código, y también se las utiliza para comprobar precondiciones. Al uso intensivo de estas técnicas se lo suele llamar programación defensiva.

Si bien quien invoca una función debe preocuparse de cumplir con las precondiciones de ésta, si las validaciones están hechas correctamente pueden devolver información valiosa para que el invocante pueda actuar en consecuencia.

Hay distintas formas de comprobar el dominio de un dato. Se puede comprobar el contenido; que una variable sea de un tipo en particular; o que el dato tenga determinada característica, como que deba ser “comparable”, o “iterable”.

También se debe tener en cuenta qué hará nuestro código cuando una validación falle, ya que queremos darle información al invocante que le sirva para procesar el error. El error producido tiene que ser fácilmente reconocible. En algunos casos, como por ejemplo cuando se quiere devolver una posición, devolver -1 nos puede asegurar que el invocante lo vaya a reconocer. En otros casos, levantar una excepción es una solución más elegante.

En cualquier caso, lo importante es que el resultado generado por nuestro código cuando funciona correctamente y el resultado generado cuando falla debe ser claramente distinto. Por ejemplo, si el código debe devolver un elemento de una secuencia, no es una buena idea que devuelva None en el caso de que la secuencia esté vacía, ya que None es un elemento válido dentro de una secuencia.

Comprobaciones por contenido

Cuando queremos validar que los datos provistos a una porción de código contengan la información apropiada, ya sea porque esa información la ingresó un usuario, fue leída de un archivo, o porque por cualquier motivo es posible que sea incorrecta, es deseable comprobar que el contenido de las variables a utilizar estén dentro de los valores con los que se puede operar.

Estas comprobaciones no siempre son posibles, ya que en ciertas situaciones puede ser muy costoso corroborar las precondiciones de una función. Es por ello que este tipo de comprobaciones se realizan sólo cuando sea posible.

Por ejemplo, la función factorial está definida para los números naturales incluyendo el 0. Es posible utilizar assert (que es otra forma de levantar una excepción) para comprobar las precondiciones de factorial.

def factorial(n):
    """ Calcula el factorial de n.
    Pre: n debe ser un entero, mayor igual a 0
    Post: se devuelve el valor del factorial pedido
    """
    assert n >= 0, "n debe ser mayor igual a 0"
    fact=1
    for i in xrange(2,n+1):
        fact*=i
    return fact

Entrada del usuario

En el caso particular de una porción de código que trate con entrada del usuario, no se debe asumir que el usuario vaya a ingresar los datos correctamente, ya que los seres humanos tienden a cometer errores al ingresar información.

Por ejemplo, si se desea que un usuario ingrese un número, no se debe asumir que vaya a ingresarlo correctamente. Se lo debe guardar en una cadena y luego convertir a un número, es por eso que es recomendable el uso de la función raw_input ya que devuelve una cadena que puede ser procesada posteriormente.

def lee_entero():
    """ Solicita un valor entero y lo devuelve.
        Si el valor ingresado no es entero, lanza una excepción. """
    valor = raw_input("Ingrese un número entero: ")
    return int(valor)

Esta función devuelve un valor entero, o lanza una excepción si la conversión no fue posible. Sin embargo, esto no es suficiente. En el caso en el que el usuario no haya ingresado la información correctamente, es necesario volver a solicitarla.

def lee_entero():
    """ Solicita un valor entero y lo devuelve.
        Mientras el valor ingresado no sea entero, vuelve a solicitarlo. """
    while True:
        valor = raw_input("Ingrese un número entero: ")
        try:
            valor = int(valor)
            return valor
        except ValueError:
            print "ATENCIÓN: Debe ingresar un número entero."

Podría ser deseable, además, poner un límite a la cantidad máxima de intentos que el usuario tiene para ingresar la información correctamente y, superada esa cantidad máxima de intentos, levantar una excepción para que sea manejada por el código invocante.

def lee_entero():
    """ Solicita un valor entero y lo devuelve.
        Si el valor ingresado no es entero, da 5 intentos para ingresarlo
        correctamente, y de no ser así, lanza una excepción. """
    intentos = 0
    while intentos < 5:
        valor = raw_input("Ingrese un número entero: ")
        try:
            valor = int(valor)
            return valor
        except ValueError:
            intentos += 1
    raise ValueError, "Valor incorrecto ingresado en 5 intentos"

Por otro lado, cuando la entrada ingresada sea una cadena, no es esperable que el usuario la vaya a ingresar en mayúsculas o minúsculas, ambos casos deben ser considerados.

def lee_opcion():
    """ Solicita una opción de menú y la devuelve. """
    while True:
        print "Ingrese A (Altas) - B (Bajas) - M (Modificaciones): ",
        opcion = raw_input().upper()
        if opcion in ["A", "B", "M"]:
            return opcion

Comprobaciones por tipo

En esta clase de comprobaciones nos interesa el tipo del dato que vamos a tratar de validar, Python nos indica el tipo de una variable usando la función type(variable). Por ejemplo, para comprobar que una variable contenga un tipo entero podemos hacer:

if type(i) != int:
    raise TypeError, "i debe ser del tipo int"

Sin embargo, ya hemos visto que tanto las listas como las tuplas y las cadenas son secuencias, y muchas de las funciones utilizadas puede utilizar cualquiera de estas secuencias. De la misma manera, una función puede utilizar un valor numérico, y que opere correctamente ya sea entero, flotante, o complejo.

Es posible comprobar el tipo de nuestra variable contra una secuencia de tipos posibles.

if type(i) not in (int, float, long, complex):
    raise TypeError, "i debe ser numérico"

Si bien esto es bastante más flexible que el ejemplo anterior, también puede ser restrictivo ya que – como se verá más adelante – cada programador puede definir sus propios tipos utilizando como base los que ya están definidos. Con este código se están descartando todos los tipos que se basen en int,float, long o complex.

Para poder incluir estos tipos en la comprobación a realizar, Python nos provee de la funciónisinstance(variable, tipos).

if not isinstance(i, (int, float, long, complex) ):
    raise TypeError, "i debe ser numérico"

Con esto comprobamos si una variable es de determinado tipo o subtipo de éste. Esta opción es bastante flexible, pero existen aún más opciones.

ADVERTENCIA: Hacer comprobaciones sobre los tipos de las variables suele resultar demasiado restrictivo, ya que es muy posible que una porción de código que opere con un tipo en particular funcione correctamente con otros tipos de variables que se comporten de forma similar.

Es por eso que hay que tener mucho cuidado al limitar el uso de una variable por su tipo, y en muchos casos es preferible limitarlas por sus propiedades, como el ejemplo anterior, en que se requería que se pudiera convertir a un entero.

Para la mayoría de los tipos básicos de Python existe una función que se llama de la misma manera que el tipo que devuelve un elemento de ese tipo, por ejemplo, int() devuelve 0, dict() devuelve {} y así. Además, estas funciones suelen poder recibir un elemento de otro tipo para tratar de convertirlo, por ejemplo, int(3.0) devuelve 3, list("Hola") devuelve ['H', 'o', 'l', 'a'].

Usando está conversión conseguimos dos cosas: podemos convertir un tipo recibido al que realmente necesitamos, a la vez que tenemos una copia de este, dejando el original intacto, que es importante cuando estamos tratando con tipos mutables.

Por ejemplo, si se quiere contar con una función de división entera que pueda recibir diversos parámetros, podría hacerse de la siguiente manera.

def division_entera(x,y):
    """ Calcula la división entera después de convertir los parámetros a
    enteros. """
    try:
        dividendo = int(x)
        divisor = int(y)
        return dividendo/divisor
    except ValueError:
        raise ValueError, "x e y deben poder convertirse a enteros"
    except ZeroDivisionError:
        raise ZeroDivisionError, "y no puede ser cero"

De esta manera, la función division_entera puede ser llamada incluso con cadenas que contengan expresiones enteras. Que este comportamiento sea deseable o no, depende siempre de cada caso.

Comprobaciones por características

Otra posible comprobación, dejando de lado los tipos, consiste en verificar si una variable tiene determinada característica o no. Python promueve este tipo de programación, ya que el mismo intérprete utiliza este tipo de comprobaciones. Por ejemplo, para imprimir una variable, Python convierte esa variable a una cadena, no hay en el interprete una verificación para cada tipo, sino que busca una función especial, llamada __str__, en la variable a imprimir, y si existe, la utiliza para convertir la variable a una cadena.

NOTAPython utiliza la idea de duck typing, que viene del concepto de que si algo parece un pato, camina como un pato y grazna como un pato, entonces, se lo puede considerar un pato.

Esto se refiere a no diferenciar las variables por los tipos a los que pertenecen, sino por las funciones que tienen.

Para comprobar si una variable tiene o no una función Python provee la función hasattr(variable, atributo), donde atributo puede ser el nombre de la función o de la variable que se quiera verificar. Se verá más sobre atributos en la unidad de Programación Orientada a Objetos.

Por ejemplo, existe la función __add__ para realizar operaciones de suma entre elementos. Si se quiere corroborar si un elemento es sumable, se lo haría de la siguiente forma.

if not hasattr(i,"__add__"):
    raise TypeError, "El elemento no es sumable"

Sin embargo, que el atributo exista no quiere decir que vaya a funcionar correctamente en todos los casos. Por ejemplo, tanto las cadenas como los números definen su propia “suma”, pero no es posible sumar cadenas y números, de modo que en este caso sería necesario tener en cuenta una posible excepción.

Por otro lado, en la mayoría de los casos se puede aplicar la frase: es más fácil pedir perdón que permiso, atribuída a la programadora Grace Hopper. Es decir, en este caso es más sencillo hacer la suma dentro de un bloque try y manejar la excepción en caso de error, que saber cuáles son los detalles de la implementación de __add__ de cada tipo interactuante.

SOURCEhttp://librosweb.es/libro/algoritmos_python/capitulo_12/validaciones.html

Python: ‘open()’ built-in function

The argument mode points to a string beginning with one of the following
 sequences (Additional characters may follow these sequences.):

 ``r''   Open text file for reading.  The stream is positioned at the
         beginning of the file.

 ``r+''  Open for reading and writing.  The stream is positioned at the
         beginning of the file.

 ``w''   Truncate file to zero length or create text file for writing.
         The stream is positioned at the beginning of the file.

 ``w+''  Open for reading and writing.  The file is created if it does not
         exist, otherwise it is truncated.  The stream is positioned at
         the beginning of the file.

 ``a''   Open for writing.  The file is created if it does not exist.  The
         stream is positioned at the end of the file.  Subsequent writes
         to the file will always end up at the then current end of file,
         irrespective of any intervening fseek(3) or similar.

 ``a+''  Open for reading and writing.  The file is created if it does not
         exist.  The stream is positioned at the end of the file.  Subse-
         quent writes to the file will always end up at the then current
         end of file, irrespective of any intervening fseek(3) or similar.

SOURCEhttp://stackoverflow.com/questions/1466000/python-open-built-in-function-difference-between-modes-a-a-w-w-and-r

Python testing: pytest

pytest: helps you write better programs

a mature full-featured Python testing tool

 

GET IThttps://pytest.org/latest/index.html

Python: assert

Tanto las precondiciones como las postcondiciones son aseveraciones (en inglés assert). Es decir, afirmaciones realizadas en un momento particular de la ejecución sobre el estado computacional. Si llegaran a ser falsas significaría que hay algún error en el diseño o utilización del algoritmo.

Para comprobar estas afirmaciones desde el código en algunos casos podemos utilizar la instrucción assert, está instrucción recibe una condición a verificar y, opcionalmente, un mensaje de error que devolverá en caso que la condición no se cumpla.

>>> n=0
>>> assert n!=0, "El divisor no puede ser 0"
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
AssertionError: El divisor no puede ser 0

 

De este modo: assert –>Condición que debe cumplirse

ADVERTENCIA: es importante tener en cuenta que assert está pensado para ser usado en la etapa de desarrollo. Un programa terminado nunca debería dejar de funcionar por este tipo de errores.

 

 

SOURCEhttp://librosweb.es/libro/algoritmos_python/capitulo_10/pre_y_postcondiciones.html

Construcción de programas

Cuando nos piden que hagamos un programa debemos seguir una cierta cantidad de pasos para asegurarnos de que tendremos éxito en la tarea. La acción irreflexiva (me piden algo, me siento frente a la computadora y escribo rápidamente y sin pensarlo lo que me parece que es la solución) no constituye una actitud profesional (e ingenieril) de resolución de problemas. Toda construcción tiene que seguir una metodología, un protocolo de desarrollo, dado.

Existen muchas metodologías para construir programas, pero en este curso aplicaremos una metodología sencilla, que es adecuada para la construcción de programas pequeños, y que se puede resumir en los siguientes pasos:

1. Analizar el problema. Entender profundamente cuál es el problema que se trata de resolver, incluyendo el contexto en el cual se usará.

Una vez analizado el problema, asentar el análisis por escrito.

2. Especificar la solución. éste es el punto en el cual se describe qué debe hacer el programa, sin importar el cómo. En el caso de los problemas sencillos que abordaremos, deberemos decidir cuáles son los datos de entrada que se nos proveen, cuáles son las salidas que debemos producir, y cuál es la relación entre todos ellos.

Al especificar el problema a resolver, documentar la especificación por escrito.

3. Diseñar la solución. éste es el punto en el cuál atacamos el cómo vamos a resolver el problema, cuáles son los algoritmos y las estructuras de datos que usaremos. Analizamos posibles variantes, y las decisiones las tomamos usando como dato de la realidad el contexto en el que se aplicará la solución, y los costos asociados a cada diseño.

Luego de diseñar la solución, asentar por escrito el diseño, asegurándonos de que esté completo.

4. Implementar el diseño. Traducir a un lenguaje de programación (en nuestro caso, y por el momento, Python) el diseño que elegimos en el punto anterior.

La implementación también se debe documentar, con comentarios dentro y fuera del código, al respecto de qué hace el programa, cómo lo hace y por qué lo hace de esa forma.

5. Probar el programa. Diseñar un conjunto de pruebas para probar cada una de sus partes por separado, y también la correcta integración entre ellas. Utilizar el depurador como instrumento para descubir dónde se producen ciertos errores.

Al ejecutar las pruebas, documentar los resultados obtenidos.

6. Mantener el programa. Realizar los cambios en respuesta a nuevas demandas.

Cuando se realicen cambios, es necesario documentar el análisis, la especificación, el diseño, la implementación y las pruebas que surjan para llevar estos cambios a cabo.

 

SOURCEhttp://librosweb.es/libro/algoritmos_python/capitulo_2/construccion_de_programas.html

Python: raw_input vs input

Python 2:

raw_input() takes exactly what the user typed and passes it back as a string. input() takes the raw_input() and performs an eval() on it as well. The main difference is that input() expects a syntactically correct python statement where raw_input() does not.

Python 3:

raw_input() was renamed to input() and the old input() was removed. If you want to use the old input(), you can do eval(input()).

 

SOURCEhttp://stackoverflow.com/questions/4915361/whats-the-difference-between-raw-input-and-input-in-python3-x

Python: declaring private and public vars

class ClaseOtroEjemplo:
def __init__(self):
self.publico = 'variable publica'
       self.__privado = 'variable privada'
   def obtener_privado(self):
print self.__privado

 

SOURCEhttp://frontendlabs.io/1305--tutorial-basico-de-python-parte-iv-programacion-orientada-a-objetos

Strings binding and division methods

6.5.1. Unir una cadena de forma iterativa

Método: join(iterable)

Retorna: la cadena unida con el iterable (la cadena es separada por cada uno de los elementos del iterable).

>>> formato_numero_factura = ("Nº 0000-0", "-0000 (ID: ", ")") 
>>> numero = "275" 
>>> numero_factura = numero.join(formato_numero_factura) 
>>> print numero_factura 
Nº 0000-0275-0000 (ID: 275)

6.5.2. Partir una cadena en tres partes, utilizando un separador

Método: partition("separador")

Retorna: una tupla de tres elementos donde el primero es el contenido de la cadena previo al separador, el segundo, el separador mismo y el tercero, el contenido de la cadena posterior al separador.

>>> tupla = "http://www.eugeniabahit.com".partition("www.") 
>>> print tupla 
('http://', 'www.', 'eugeniabahit.com') 
 
>>> protocolo, separador, dominio = tupla 
>>>> print "Protocolo: {0}\nDominio: {1}".format(protocolo, dominio) 
Protocolo: http:// 
Dominio: eugeniabahit.com

6.5.3. Partir una cadena en varias partes, utilizando un separador

Método: split("separador")

Retorna: una lista con todos elementos encontrados al dividir la cadena por un separador.

>>> keywords = "python, guia, curso, tutorial".split(", ") 
>>> print keywords 
['python', 'guia', 'curso', 'tutorial']

6.5.4. Partir una cadena en en líneas

Método: splitlines()

Retorna: una lista donde cada elemento es una fracción de la cadena divida en líneas.

>>> texto = """Linea 1
Linea 2
Linea 3
Linea 4
""" 
>>> print texto.splitlines() 
['Linea 1', 'Linea 2', 'Linea 3', 'Linea 4'] 
>>> texto = "Linea 1\nLinea 2\nLinea 3" 
>>> print texto.splitlines() 
['Linea 1', 'Linea 2', 'Linea 3']

 

SOURCE: http://librosweb.es/libro/python/capitulo_6/metodos_de_union_y_division.html