¿Cuál lenguaje de programación debo aprender primero?

Aprender a programar es una de las mejores decisiones que puedes tomar en la actualidad. Sin embargo, con tantos lenguajes disponibles, es normal preguntarse: ¿cuál es el mejor lenguaje de programación para comenzar? En este artículo, te ayudaremos a elegir el más adecuado según tus objetivos.

Factores a considerar al elegir tu primer lenguaje

Antes de decidir, ten en cuenta estos factores:

  • Facilidad de aprendizaje: Algunos lenguajes tienen una sintaxis más sencilla y amigable para principiantes.
  • Aplicaciones y demanda laboral: Elige un lenguaje con salidas profesionales y que se use en proyectos de tu interés.
  • Comunidad y recursos: Es importante que haya documentación, tutoriales y foros donde puedas resolver dudas.

Los mejores lenguajes de programación para principiantes

1. Python – El más recomendado para principiantes

  • Facilidad: ⭐⭐⭐⭐⭐
  • Usos: Desarrollo web, inteligencia artificial, ciencia de datos, automatización.
  • Por qué elegirlo: Su sintaxis clara y simple lo convierte en la mejor opción para principiantes. Además, es ampliamente utilizado en la industria tecnológica.

2. JavaScript – Ideal para el desarrollo web

  • Facilidad: ⭐⭐⭐⭐
  • Usos: Desarrollo web, aplicaciones móviles, servidores.
  • Por qué elegirlo: Si te interesa el desarrollo web, JavaScript es esencial. Permite crear sitios interactivos y funciona en todos los navegadores.

3. Java – Un clásico con gran demanda laboral

  • Facilidad: ⭐⭐⭐
  • Usos: Desarrollo de aplicaciones móviles (Android), software empresarial, videojuegos.
  • Por qué elegirlo: Es un lenguaje potente, ampliamente utilizado en empresas y en el desarrollo de aplicaciones móviles con Android.

4. C# – Excelente para desarrollo de videojuegos

  • Facilidad: ⭐⭐⭐
  • Usos: Desarrollo de videojuegos (Unity), aplicaciones de escritorio, software empresarial.
  • Por qué elegirlo: Si te apasiona el desarrollo de videojuegos, C# es la mejor opción, ya que es el lenguaje principal de Unity.

5. C++ – Para los que buscan desafíos

  • Facilidad: ⭐⭐
  • Usos: Desarrollo de software de alto rendimiento, videojuegos, sistemas operativos.
  • Por qué elegirlo: Es más difícil que otros lenguajes, pero aprenderlo te dará una comprensión profunda de cómo funcionan los programas y la memoria.

Conclusión: ¿Cuál deberías elegir?

Si quieres un comienzo fácil y versátil, Python es la mejor opción. Si te interesa el desarrollo web, empieza con JavaScript. Para aplicaciones móviles, Java es una gran elección. Si sueñas con crear videojuegos, ve por C#. Y si buscas un reto, prueba con C++.

¿Ya decidiste cuál aprenderás primero? ¡Déjanos tu comentario y cuéntanos tu elección! 🚀

Herencia en Python

Herencia

La herencia es una de las premisas y técnicas de la POO la cual permite a los programadores crear una clase general primero y luego más tarde crear clases más especializadas que re-utilicen código de la clase general. La herencia también le permite escribir un código más limpio y legible.

Clase Base

Clase Base o también conocida como Clase abstracta le permite definir una clase que puede heredarse en otras clases los atributos y comportamientos definido en esta.

../_images/clase_abstracta.png

Clase base o abstracta

Use el diagrama anterior para ilustrar el concepto de la herencia, vea el caso de dos clases que tiene algo en común, ambas son personas, con atributos de datos personales y comportamiento típicos como hablar, comer, caminar, entonces para eso se crea una clase base llamada Persona. A continuación un ejemplo de la clase Persona con un método interno:

class Persona(object):
    """Clase que representa una Persona"""

    def __init__(self, cedula, nombre, apellido, sexo):
        """Constructor de clase Persona"""
        self.cedula = cedula
        self.nombre = nombre
        self.apellido = apellido
        self.sexo = sexo

    def __str__(self):
        """Devuelve una cadena representativa de Persona"""
        return "%s: %s, %s %s, %s." % (
            self.__doc__[25:34], str(self.cedula), self.nombre, 
            self.apellido, self.getGenero(self.sexo))

    def hablar(self, mensaje):
        """Mostrar mensaje de saludo de Persona"""
        return mensaje

    def getGenero(self, sexo):
        """Mostrar el genero de la Persona"""
        genero = ('Masculino','Femenino')
        if sexo == "M":
            return genero[0]
        elif sexo == "F":
            return genero[1]
        else:
            return "Desconocido"

En el ejemplo previo, es donde empieza a crear una clase (lo hace con la palabra class). La segunda palabra Persona es el nombre de la clase. La tercera palabra que se encuentra dentro de los paréntesis este hace referencia al objeto object, usando para indicar la clase de la cual precede.

La clase Persona tiene los métodos __init____str__hablar y getGenero. Sus atributos son cedulanombreapellido y sexo.

La instancia de dos nuevos objetos Persona seria de la siguiente forma:

persona1 = Persona("V-13458796", "Leonardo", "Caballero", "M")
persona2 = Persona("V-23569874", "Ana", "Poleo", "F")

El método constructor __init__ es un método especial el cual debe escribir como: MiClase(parámetros iniciales si hay cualquiera).

Usted puede llamar esos métodos y atributos con la siguiente notación: claseinstancia.metodo o claseinstancia.atributo.

>>> print persona1.nombre, persona1.apellido
>>> print persona1.getGenero(persona1.sexo)

El método __str__ es un método usando para imprimir la descripción de la instancia de objeto el cual debe mostrar como:

print "\n" + str(persona1) + "\n"

En el anterior código se usan para cierto formato para imprimir la instancia de objeto usando la sentencia print, concatenando el carácter \n para generar un salto de página y seguidamente convertir a formato cadena de caracteres usando la función str() a la instancia de objeto llamada persona2.

Herencia simple

La herencia simple se apoya en el uso de clases base para compartir sus atributos y comportamientos con otros clases derivadas como los siguiente ejemplos el objeto Supervisor y el objeto Obrero.

../_images/poo_herencia.png

Diagrama de herencia de Objetos

El siguiente es un ejemplo de la clase Supervisor que derivada de la clase Persona con función interna:

class Supervisor(Persona):
    """Clase que representa a un Supervisor"""

    def __init__(self, cedula, nombre, apellido, sexo, rol):
        """Constructor de clase Supervisor"""

        # Invoca al constructor de clase Persona
        Persona.__init__(self, cedula, nombre, apellido, sexo)

        # Nuevos atributos
        self.rol = rol
        self.tareas = ['10','11','12','13']

    def __str__(self):
        """Devuelve una cadena representativa al Supervisor"""
        return "%s: %s %s, rol: '%s', sus tareas: %s." % (
            self.__doc__[26:37], self.nombre, self.apellido, 
            self.rol, self.consulta_tareas())

    def consulta_tareas(self):
        """Mostrar las tareas del Supervisor"""
        return ', '.join(self.tareas)

Ahora, se creará una nueva clase Supervisor con los mismos métodos y atributos como la clase Persona, pero con dos nuevos atributos rol y tareas. No se copia la clase previa, pero si se hereda de ella.

La instancia del nuevo objeto Supervisor seria de la siguiente forma:

supervisor1 = Supervisor("V-16987456", "Jen", "Paz", "D", "Chivo")

Luego que generá la instancia del nuevo objeto Supervisor llamada supervisor1 se puede imprimir sus detalles de la siguiente forma:

print "\n" + str(supervisor1) + "\n"

Como la instancia de objeto supervisor1 hereda los atributo(s) y método(s) de la clase Persona usted puede reusarlo y llamarlo de la siguiente forma:

print "- Cedula de identidad: {0}.".format(supervisor1.cedula)
print "- Nombre completo: {0} {1}.".format(
	supervisor1.nombre, supervisor1.apellido)
print "- Genero: {0}.".format(
	supervisor1.getGenero(supervisor1.sexo))
print "- {0} {1} dijo: {2}".format(
	supervisor1.nombre, supervisor1.apellido, 
	supervisor1.hablar("A trabajar Leonardo!!!".upper()))

Si desea usar los atributo(s) y método(s) heredados de la clase Supervisor se puede imprimir de la siguiente forma:

print "- Rol: {0}.".format(supervisor1.rol)
print "- N. Tareas: {0}.".format(supervisor1.consulta_tareas())

El uso de las clases y la programación orientada a objetos, le permite a usted que pueda organizar el código con diferentes clases correspondientes a diferentes objetos que encontrará (una clase Persona, una clase Carro, una clase Departamento, etc.), con sus propios métodos y atributos. Luego puede usar la herencia para considerar las variaciones en torno a una clase base y reutilizar el código. Ej.: a partir de una clase base de Persona, usted puede crear clases derivadas como SupervisorJefeCuadrillaObrero, etc.

print """\nHola, Soy el {0} {1} {2}, mi cédula es '{3}', 
mi genero '{4}', con el rol '{5}' y mis tareas
asignadas '{6}'.""".format(
    supervisor1.__doc__[26:37].lower(),
    supervisor1.nombre, supervisor1.apellido, supervisor1.cedula, 
    supervisor1.getGenero(supervisor1.sexo), supervisor1.rol,
    supervisor1.consulta_tareas())

Función issubclass()

issubclass(), es una función integrada la cual le permite corroborar si un objeto es instancia de una clase.

 

Herencia múltiple

A diferencia de lenguajes como Java y C#, el lenguaje Python permite la herencia múltiple, es decir, se puede heredar de múltiples clases.

La herencia múltiple es la capacidad de una subclase de heredar de múltiples súper clases.

Esto conlleva un problema, y es que si varias súper clases tienen los mismos atributos o métodos, la subclase sólo podrá heredar de una de ellas.

En estos casos Python dará prioridad a las clases más a la izquierda en el momento de la declaración de la subclase:

class Destreza(object):
    """Clase la cual representa la Destreza de la Persona"""

    def __init__(self, area, herramienta, experiencia):
        """Constructor de clase Destreza"""
        self.area = area
        self.herramienta = herramienta
        self.experiencia = experiencia

    def __str__(self):
        """Devuelve una cadena representativa de la Destreza"""
        return """Destreza en el área %s con la herramienta %s, 
        tiene %s años de experiencia.""" % (
            str(self.area), self.experiencia, self.herramienta)


class JefeCuadrilla(Supervisor, Destreza):
    """Clase la cual representa al Jefe de Cuadrilla"""

    def __init__(self, cedula, nombre, apellido, sexo, 
        rol, area, herramienta, experiencia, cuadrilla):
        """Constructor de clase Jefe de Cuadrilla"""

        # Invoca al constructor de clase Supervisor
        Supervisor.__init__(self, cedula, nombre, apellido, sexo, 
            rol)
        # Invoca al constructor de clase Destreza
        Destreza.__init__(self, area, herramienta, experiencia)

        # Nuevos atributos
        self.cuadrilla = cuadrilla

    def __str__(self):
        """Devuelve cadena representativa al Jefe de Cuadrilla"""
        jq = "{0}: {1} {2}, rol '{3}', tareas {4}, cuadrilla: {5}"
        return jq.format(
            self.__doc__[28:46], self.nombre, self.apellido, 
            self.rol, self.consulta_tareas(), self.cuadrilla)

Method Resolution Order (MRO)

Ese es el orden en el cual el método debe heredar en la presencia de herencia múltiple. Usted puede ver el MRO usando el atributo __mro__.

>>> JefeCuadrilla.__mro__
(<class '__main__.JefeCuadrilla'>,
<class '__main__.Supervisor'>,
<class '__main__.Persona'>,
<class '__main__.Destreza'>,
<type 'object'>)
>>> Supervisor.__mro__
(<class '__main__.Supervisor'>,
<class '__main__.Persona'>,
<type 'object'>)
>>> Destreza.__mro__
(<class '__main__.Destreza'>,
<type 'object'>)

El MRO es calculado en Python de la siguiente forma:

Un método en la llamada derivada es siempre llamada antes de método de la clase base. En nuestro ejemplo, la clase JefeCuadrilla es llamada antes de las clases Supervisor o Destreza. Esas dos clases son llamada antes de la clase Persona y la clase Persona es llamada antes de la clase object.

Si hay herencia múltiple como JefeCuadrilla(Supervisor, Destreza), el método invoca a Supervisor primero por que ese aparece primero de izquierda a derecha.

Programación orientada a objetos en Python

Programación orientada a objetos

La programación orientada a objetos (POO, u OOP según sus siglas en inglés) es un paradigma de programación que viene a innovar la forma de obtener resultados. Los objetos manipulan los datos de entrada para la obtención de datos de salida específicos, donde cada objeto ofrece una funcionalidad especial.

Muchos de los objetos prediseñados de los lenguajes de programación actuales permiten la agrupación en bibliotecas o librerías, sin embargo, muchos de estos lenguajes permiten al usuario la creación de sus propias bibliotecas.

../_images/oop.jpg

Programación Orientada a Objetos – POO

Está basada en varias técnicas, como las siguientes:

  • herencia.
  • cohesión.
  • abstracción.
  • polimorfismo.
  • acoplamiento.
  • encapsulación.

La POO tiene sus raíces en la década del 60 con el lenguaje de programación Simula que en 1967, el cual fue el primer lenguaje que posee las características principales de un lenguaje orientado a objetos.

Smalltalk (de 1972 a 1980) es posiblemente el ejemplo canónico, y con el que gran parte de la teoría de la POO se ha desarrollado. Más su uso se popularizó a principios de la década de 1990.

En la actualidad, existe una gran variedad de lenguajes de programación que soportan la orientación a objetos.

Los objetivos de la POO son:

  • Organizar el código fuente, y
  • re-usar código fuente en similares contextos.

Nota

Más información consulte el articulo de Wikipedia Programación orientada a objetos.

POO en Python

El mecanismo de clases de Python agrega clases al lenguaje con un mínimo de nuevas sintaxis y semánticas.

En Python las clases es una mezcla de los mecanismos de clase encontrados en C++ y Modula-3.

Como es cierto para los módulos, las clases en Python no ponen una barrera absoluta entre la definición y el usuario, sino que más bien se apoya en la cortesía del usuario de no «forzar la definición».

Sin embargo, se mantiene el poder completo de las características más importantes de las clases: el mecanismo de la herencia de clases permite múltiples clases base, una clase derivada puede sobrescribir cualquier método de su(s) clase(s) base, y un método puede llamar al método de la clase base con el mismo nombre.

«Los objetos pueden tener una cantidad arbitraria de datos.»

En terminología de C++, todos los miembros de las clases (incluyendo los miembros de datos), son públicos, y todas las funciones miembro son virtuales.

Como en Modula-3, no hay atajos para hacer referencia a los miembros del objeto desde sus métodos: la función método se declara con un primer argumento explícito que representa al objeto, el cual se provee implícitamente por la llamada.

Como en Smalltalk, las clases mismas son objetos. Esto provee una semántica para importar y renombrar.

A diferencia de C++ y Modula-3, los tipos de datos integrados pueden usarse como clases base para que el usuario los extienda.

También, como en C++ pero a diferencia de Modula-3, la mayoría de los operadores integrados con sintaxis especial (operadores aritméticos, de subíndice, etc.) pueden ser redefinidos por instancias de la clase.

(Sin haber una terminología universalmente aceptada sobre clases, haré uso ocasional de términos de Smalltalk y C++. Usaría términos de Modula-3, ya que su semántica orientada a objetos es más cercana a Python que C++, pero no espero que muchos lectores hayan escuchado hablar de él).

Algunas particularidades de POO en Python son las siguientes:

  • Todo es un objeto, incluyendo los tipos y clases.
  • Permite herencia múltiple.
  • No existen métodos ni atributos privados.
  • Los atributos pueden ser modificados directamente.
  • Permite «monkey patching».
  • Permite «duck typing».
  • Permite la sobrecarga de operadores.
  • Permite la creación de nuevos tipos de datos.

A continuación se procede a definir algunos conceptos necesarios para entender la POO:

 

Objetos

Los objetos son abstracción de Python para data. Toda la data en un programa Python es representado por objectos o por relaciones entre objectos. (En cierto sentido, y en el código modelo de Von Neumann de una «computadora almacenada del programa» también es un código representado por los objetos.)

Cada objeto tiene una identidad, un tipo y un valor. Una identidad de objecto nunca cambia una vez es creada; usted puede pensar eso como la dirección de objeto en memoria. El operador in compara la identidad de dos objetos; la función id() devuelve un número entero representando la identidad (actualmente implementado como su dirección).

El tipo de un objeto también es inmutable. El tipo de un objeto determina las operaciones que admite el objeto (por ejemplo, «¿tiene una longitud?») Y también define los valores posibles para los objetos de ese tipo. La función «type()» devuelve el tipo de un objeto (que es un objeto en sí mismo). El valor *de algunos objetos puede cambiar. Se dice que los objetos cuyo valor puede cambiar son *mutables; los objetos cuyo valor no se puede cambiar una vez que se crean se llaman immutable. (El valor de un objeto contenedor inmutable que contiene una referencia a un objeto mutable puede cambiar cuando se cambia el valor de este último; sin embargo, el contenedor todavía se considera inmutable, porque la colección de objetos que contiene no se puede cambiar. Por lo tanto, la inmutabilidad no es estrictamente lo mismo que tener un valor incambiable, es más sutil.) La mutabilidad de un objeto está determinada por su tipo; por ejemplo, los números, las cadenas y las tuplas son inmutables, mientras que los diccionarios y las listas son mutables.

Los objetos son la clave para entender la POO. Si mira a nuestro alrededor encontrará un sin fin de objetos de la vida real: perro, escritorio, televisor, bicicleta, etc…

En Python puede definir una clase con la palabra reservada class, de la siguiente forma:

class Persona:
    pass

En el ejemplo anterior, el nombre de la clase es Persona y dentro del bloque de código usa la sentencia pass. Aunque no es requerido por el intérprete, los nombres de las clases se escriben por convención capitalizadas. Las clases pueden (y siempre deberían) tener comentarios.

../_images/objetos_clases.png

Diagrama de Objeto Persona

Estado de un objeto

El conjunto de datos y objetos relacionados con un objeto en un momento dado, se le conoce como «estado». Un objeto puede tener múltiples estados a lo largo de su existencia conforme se relaciona con su entorno y otros objetos.

Atributos

Los atributos o propiedades de los objetos son las características que puede tener un objeto, como el color. Si el objeto es Persona, los atributos podrían ser: cedulanombreapellidosexo, etc…

Los atributos describen el estado de un objeto. Pueden ser de cualquier tipo de dato.

class Persona:
    """Clase que representa una Persona"""
    cedula = "V-13458796"
    nombre = "Leonardo"
    apellido = "Caballero"
    sexo = "M"

Usted puede probar el código anterior, si lo transcribe en el consola interactiva Python como lo siguiente:

>>> class Persona:
...     """Clase que representa una Persona"""
...     cedula = "V-13458796"
...     nombre = "Leonardo"
...     apellido = "Caballero"
...     sexo = "M"
...
>>> macagua = Persona
>>> type(macagua)
<type 'classobj'>
>>> dir(macagua)
['__doc__', '__module__', 'apellido', 'cedula', 'nombre', 'sexo']
>>> macagua.cedula
'V-13458796'
>>> macagua.nombre
'Leonardo'
>>> macagua.apellido
'Caballero'
>>> macagua.sexo
'M'
>>> print "El objeto de la clase " + macagua.__name__ +"," \
... + macagua.__doc__ + "."
El objeto de la clase Persona, Clase que representa una Persona.
>>> print "Hola, mucho gusto, mi nombre es '"+ \
... macagua.nombre +" "+ \
... macagua.apellido +"', \nmi cédula de identidad es '"+  \
... macagua.cedula +"', y mi sexo es '"+  \
... macagua.sexo +"'."
Hola, mucho gusto, mi nombre es 'Leonardo Caballero',
mi cédula de identidad es 'V-13458796', y mi sexo es 'M'.

Si el nombre de un atributo esta encerrado entre dobles guiones bajos son atributos especiales.

  • __name__, describe el nombre del objeto o del método.
>>> macagua.__name__
'Persona'
  • __doc__, contiene la documentación de un módulo, una clase, o método especifico, escrita en el formato docstrings.
>>> macagua.__doc__
'Clase que representa una Persona'

Si el nombre de un atributo esta con dobles guiones bajos al principio son atributos «escondidos». A continuación un pseudo código que ilustra un ejemplo:

>>> ms_windows.__privado
'True'

>>> ms_windows.codigo_fuente.__no_tocar
'True'

En la sección encapsulación se describe esto a más profundidad.

 

Métodos

Los métodos describen el comportamiento de los objetos de una clase. Estos representan las operaciones que se pueden realizar con los objetos de la clase,

La ejecución de un método puede conducir a cambiar el estado del objeto.

Se definen de la misma forma que las funciones normales pero deben declararse dentro de la clase y su primer argumento siempre referencia a la instancia que la llama, de esta forma se afirma que los métodos son funciones, adjuntadas a objectos.

Nota

Usted puede encontrar ejemplos en las funciones de cadena de caractereslistasdiccionarios, etc.

Si el objeto es Persona, los métodos pueden ser: hablarcaminarcomerdormir, etc.

class Persona:
    """Clase que representa una Persona"""
    cedula = "V-13458796"
    nombre = "Leonardo"
    apellido = "Caballero"
    sexo = "M"
    
    def hablar(self, mensaje):
        """Mostrar mensaje de saludo de Persona"""
        return mensaje

La única diferencia sintáctica entre la definición de un método y la definición de una función es que el primer parámetro del método por convención debe ser el nombre self.

Usted puede probar el código anterior, si lo transcribe en el consola interactiva Python como lo siguiente:

>>> class Persona:
...     """Clase que representa una Persona"""
...     cedula = "V-13458796"
...     nombre = "Leonardo"
...     apellido = "Caballero"
...     sexo = "M"
...
...     def hablar(self, mensaje):
...         """Mostrar mensaje de saludo de Persona"""
...         return mensaje
...
>>>
>>> macagua = Persona
>>> Persona().hablar("Hola, soy la clase {0}.".format(
...     macagua.__name__))
'Hola, soy la clase Persona.'
>>> type(Persona().hablar)
<type 'instancemethod'>
>>> Persona().hablar.__doc__
'Mostrar mensaje de saludo de Persona'

Si crea una instancia de objeto para la clase Persona e intenta llamar al método hablar() esto lanzara una excepción TypeError, como sucede a continuación:

>>> macagua = Persona
>>> macagua.hablar("Hola Plone")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method hablar() must be called with Persona instance as first argument (got str instance instead)

Ámbito de los métodos

Los métodos cuentan con un espacio de nombres propio. En caso de no encontrar un nombre en su ámbito local, buscará en el ámbito superior hasta encontrar alguna coincidencia.

Los métodos pueden acceder y crear atributos dentro del objeto al que pertenecen, anteponiendo la palabra self y el operador de atributo «.» antes del nombre del atributo en cuestión.

 

Métodos especiales

Las clases en Python cuentan con múltiples métodos especiales, los cuales se encuentran entre dobles guiones bajos __<metodo>__().

Los métodos especiales más utilizados son __init__()__str__() y __del__().

 

__str__()

El método __str__() es un método especial, el cual se ejecuta al momento en el cual un objeto se manda a mostrar, es decir es una cadena representativa de la clase, la cual puede incluir formatos personalizados de presentación del mismo.

    def __str__(self):
        """Devuelve una cadena representativa de Persona"""
        return "%s: %s, %s %s, %s." % (
            self.__doc__[25:34], str(self.cedula), self.nombre, 
            self.apellido, self.getGenero(self.sexo))

 

__del__()

El método __del__() es un método especial, el cual se ejecuta al momento en el cual un objeto es descartado por el intérprete. El comportamiento de __del__() es muy similar a los «destructores» en otros lenguajes.

 

Métodos de clase

En ocasiones es necesario contar con métodos que interactúen con elementos de la clase de la cual el objeto es instanciado. Python permite definir métodos de clase para esto.

Los métodos de clase son aquellos que están ligados directamente con los atributos definidos en la clase que los contiene. Para definir un método de clase se utiliza el decorador @classmethod y por convención se utiliza cls como argumento inicial en lugar de self.

Del mismo modo, los métodos de clase utilizan el prefijo cls para referirse a los atributos de la clase.

class <Clase>(object):
    ...
    ...
    @classmethod
    def <metodo>(cls, <argumentos>):
        ...
        ...

 

Métodos estáticos

Los métodos estáticos hacen referencia a las instancias y métodos de una clase. Para definir un método estático se utiliza el decorador @staticmethod y no utiliza ningún argumento inicial.

Al no utilizar self, los métodos estáticos no pueden interactuar con los atributos y métodos de la instancia.

Para referirse a los elementos de la clase, se debe utilizar el nombre de la clase como prefijo.

class <Clase>(object):
    ...
    ...
    @staticmethod
    def <metodo>(<argumentos>):
        ...
        ...

Interfaces

La forma en que los métodos de un objeto pueden ser accedidos por otros objetos se conoce como «interfaz». Una interfaz bien definida permite a objetos de distinta índole interactuar entre sí de forma modular. La interfaz define el modo en que los objetos intercambian información.

Implementaciones

Una implementación corresponde al mecanismo interno que se desencadena en un método cuando éste es llamado. Las implementaciones procesan las entradas proveniente de las interfaces y actúan en consecuencia ya sea:

  • Modificando el estado del objeto.
  • Transfiriendo la información resultante del proceso interno a través de la interfaces.

Clases

Las clases definen las características del objeto.

Con todos los conceptos anteriores explicados, se puede decir que una clase es una plantilla genérica de un objeto. La clase proporciona variables iniciales de estado (donde se guardan los atributos) e implementaciones de comportamiento (métodos) necesarias para crear nuevos objetos, son los modelos sobre los cuáles serán construidos.

 

Instancias

Ya sabe que una clase es una estructura general del objeto. Por ejemplo, puede decir que la clase Persona necesita tener una cedula, un nombre, un apellido y una sexo, pero no va a decir cual es cedulanombreapellido y sexo, es aquí donde entran las instancias.

Una instancia es una copia específica de la clase con todo su contenido. Por ejemplo:

>>> persona1 = Persona("V-13458796", "Leonardo", "Caballero", "M")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: this constructor takes no arguments

La excepción TypeError indica que el método constructor no toma argumentos, esto se debe a que la momento de definir la clase a cada atributo se le asigno un valor (tipo de dato).

Usted puede definir el metodo constructor de la clase usando el método __init__().

 

Método __init__()

El método __init__() es un método especial, el cual se ejecuta al momento de instanciar un objeto. El comportamiento de __init__() es muy similar a los «constructores» en otros lenguajes. Los argumentos que se utilizan en la definición de __init__() corresponden a los parámetros que se deben ingresar al instanciar un objeto.

    def __init__(self, cedula, nombre, apellido, sexo):
        """Constructor de clase Persona"""
        self.cedula = cedula
        self.nombre = nombre
        self.apellido = apellido
        self.sexo = sexo

Función isinstance()

isinstance(), es una función integrada la cual le permite corroborar si un objeto es instancia de una clase.

Excepciones integradas en Python

Excepciones integradas

Las excepciones pueden ser objetos de una clase u objetos cadena. Aunque la mayoría de las excepciones eran objetos cadena en las anteriores versiones de Python, en Python 1.5 y versiones posteriores, todas las excepciones estándar han sido convertidas en objetos de clase y se anima a los usuarios a que hagan lo propio. Las excepciones están definidas en el módulo exceptions. Nunca es necesario importar este módulo explícitamente, pues las excepciones vienen proporcionadas por el espacio nominal interno.

Dos objetos de cadena distintos con el mismo valor se consideran diferentes excepciones. Esto es así para forzar a los programadores a usar nombres de excepción en lugar de su valor textual al especificar gestores de excepciones. El valor de cadena de todas las excepciones internas es su nombre, pero no es un requisito para las excepciones definidas por el usuario u otras excepciones definidas por módulos de biblioteca.

En el caso de las clases de excepción, en una sentencia try con una sentencia except que mencione una clase particular, esta sentencia también gestionará cualquier excepción derivada de dicha clase (pero no las clases de excepción de las que deriva ella). Dos clases de excepción no emparentadas mediante sub-clasificación nunca son equivalentes, aunque tengan el mismo nombre.

Las excepciones internas enumeradas a continuación pueden ser generadas por el intérprete o por funciones internas. Excepto en los casos mencionados, tienen un valor asociado indicando en detalle la causa del error. Este valor puede ser una cadena o tupla de varios elementos informativos (es decir, un código de error y una cadena que explica el código). El valor asociado es el segundo argumento a la sentencia raise. En las cadenas de excepción, el propio valor asociado se almacenará en la variable nombrada como el segundo argumento de la sentencia except (si la hay). En las clases de excepción, dicha variable recoge la instancia de la excepción. Si la clase de excepción deriva de la clase raíz estándar Exception, el valor asociado está disponible en el atributo args de la instancia de excepción y es probable que aparezca en otros atributos.

El código de usuario puede lanzar excepciones internas. Se puede usar para comprobar un gestor de excepciones o para informar de una condición de error del mismo modo que el intérprete lanza la misma excepción. Hay que ser precavido, pues nada incluye que el código de usuario lance una excepción inadecuada.

Las siguientes excepciones sólo se usan como clase base de otras excepciones.

BaseException
La clase base de todas las comunes excepciones. Deriva de la clase raíz __builtin__.object, es el tipo más básico.
Exception

La clase base de todas las excepciones no existentes. Deriva de la clase raíz BaseException.

La clase base de las excepciones. Todas las excepciones internas derivan de esta clase. Todas las excepciones definidas por usuario deberían derivarse de esta clase, aunque no es obligatorio (todavía). La función str(), aplicada a una instancia de una clase (o la mayoría de sus clases derivadas) devuelve un valor cadena a partir de sus argumentos o una cadena vacía si no se proporcionaron argumentos al constructor. Si se usa como secuencia, accede a los argumentos proporcionados al constructor (útil para compatibilidad con código antiguo). Los argumentos también están disponibles en el atributo args de la instancia, como tupla.

StandardError
La clase base para todas las excepciones internas excepto SystemExit. Deriva de la clase raíz Exception.
ArithmeticError
La clase base de las excepciones lanzadas por diversos errores aritméticos: OverflowErrorZeroDivisionError y FloatingPointError. Deriva de la clase raíz StandardError.
LookupError
La clase base de las excepciones lanzadas cuando una clave o índice utilizado en una correspondencia (diccionario) o secuencia son incorrectos: IndexErrorKeyError. Deriva de la clase raíz StandardError.
EnvironmentError
La clase base de las excepciones que pueden ocurrir fuera del sistema Python: IOErrorOSError. Cuando se crean excepciones de este tipo con una tupla de dos valores, el primer elemento queda disponible en el atributo errno de la instancia (se supone que es un número de error) y el segundo en el atributo strerror (suele ser el mensaje de error asociado). La propia tupla está disponible en el atributo args. Cuando se instancia una excepción EnvironmentError con una tupla de tres elementos, los primeros dos quedan disponibles como en el caso de dos elementos y el tercero queda en el atributo filename. Sin embargo, por compatibilidad con sistemas anteriores, el atributo args contiene sólo una tupla de dos elementos de los dos primeros argumentos del constructor. El atributo filename es None cuando se cree la excepción con una cantidad de argumentos diferente de 3. Los atributos errno y strerror son también None cuando la instancia no se cree con 2 ó 3 argumentos. En este último caso, args contiene los argumentos del constructor tal cual, en forma de tupla. Deriva de la clase raíz StandardError.

Las siguientes excepciones son las realmente lanzadas.

AssertionError
Se lanza cuando una sentencia assert es False. Deriva de la clase raíz StandardError.
AttributeError
Se lanza cuando una referencia o asignación a atributo fracasa (cuando un objeto no tenga referencias o asignaciones a atributos en absoluto, se lanza, la excepción TypeError.) Deriva de la clase raíz StandardError.
BufferError
Se lanza cuando un error Buffer sucede. Deriva de la excepción StandardError.
EOFError
Se lanza cuando las funciones internas (input() o raw_input()) alcanzan un end of file EOF (final de archivo) sin leer datos. N.B.: Los métodos read() y readline() de los objetos archivo devuelven una cadena vacía al alcanzar EOF. Deriva de la clase raíz StandardError.
FloatingPointError
Se lanza cuando falla una operación de coma flotante. Esta excepción siempre está definida, pero sólo se puede lanzar cuando Python esta configurado con la opción --with-fpectl o se ha definido el símbolo WANT_SIGFPE_HANDLER en el archivo config.h. Deriva de la clase raíz ArithmeticError.
GeneratorExit
Se lanza cuando la solicitud de salida de un generador Python sucede. Deriva de la excepción BaseException.
IOError
Se lanza cuando una operación de E/S (tal como una sentencia print, la función integrada open() o un método de un objeto archivo) fracasa por motivos relativos a E/S, por ejemplo, por no encontrarse un archivo o llenarse el disco. Esta clase se deriva de EnvironmentError. En la explicación anterior se proporciona información adicional sobre los atributos de instancias de excepción. Deriva de la clase raíz EnvironmentError.
ImportError
Se lanza cuando una sentencia import no encuentra la definición del módulo o cuando from ... import no encuentra un nombre a importar. Deriva de la clase raíz StandardError.
IndexError
Se lanza cuando un sub-índice de una secuencia se sale del rango. Los índices de corte se truncan silenciosamente al rango disponible. Si un índice no es un entero simple, se lanza TypeError. Deriva de la clase raíz LookupError.
IndentationError
Se lanza cuando una indentación incorrecta sucede. Deriva de la excepción SyntaxError.
KeyError
Se lanza cuando no se encuentra una clave de una correspondencia (diccionario) en el conjunto de claves existentes. Deriva de la clase raíz LookupError.
KeyboardInterrupt
Se lanza cuando el usuario pulsa la tecla de interrupción (normalmente con la combinación de teclas Control-C o DEL2.7). A lo largo de la ejecución se comprueba si se ha interrumpido regularmente. Las interrupciones ocurridas cuando una función input() o raw_input()) espera datos también lanzan esta excepción. Deriva de la clase raíz BaseException.
MemoryError
Se lanza cuando una operación agota la memoria pero aún se puede salvar la situación (borrando objetos). El valor asociado es una cadena que indica qué tipo de operación (interna) agotó la memoria. Obsérvese que por la arquitectura de gestión de memoria subyacente (la función de C malloc()), puede que el intérprete no siempre sea capaz de recuperarse completamente de esta situación. De cualquier modo, se lanza una excepción para que se pueda imprimir una traza, por si la causa fue un programa desbocado. Deriva de la clase raíz StandardError.
NameError
Se lanza cuando no se encuentra un nombre local o global. Sólo se aplica a nombre no calificados. El valor asociado es el nombre no encontrado. Deriva de la clase raíz StandardError.
NotImplementedError
Esta excepción se deriva de RuntimeError. En clases base definidas por el usuario, los métodos abstractos deberían lanzar esta excepción cuando se desea que las clases derivadas redefinan este método. Deriva de la clase raíz RuntimeError.
OSError
Esta clase se deriva de EnvironmentError y se usa principalmente como excepción os.error de os. En EnvironmentError hay una descripción de los posibles valores asociados.
OverflowError
Se lanza cuando el resultado de una operación aritmética es demasiado grande para representarse (desbordamiento). Esto no es posible en los enteros largos (que antes que rendirse lanzarían MemoryError). Por la falta de normalización de la gestión de excepciones de coma flotante en C, la mayoría de las operaciones de coma flotante, tampoco se comprueban. En el caso de enteros normales, se comprueban todas las operaciones que pueden desbordar excepto el desplazamiento a la izquierda, en el que las aplicaciones típicas prefieren perder bits que lanzar una excepción. Deriva de la clase raíz ArithmeticError.
RuntimeError
Se lanza cuando se detecta un error que no cuadra en ninguna de las otras categorías. El valor asociado es una cadena que indica qué fue mal concretamente. Esta excepción es mayormente una reliquia de versiones anteriores del intérprete; ya casi no se usa. Deriva de la clase raíz StandardError.
StopIteration
Se lanza cuando se indica el final desde iterator.next(). Deriva de la excepción Exception.
SyntaxError
Se lanza cuando el analizador encuentra un error en la sintaxis. Esto puede ocurrir en una sentencia import, en una sentencia exec, en una llamada a la función interna eval() o input(), o al leer el guion inicial o la entrada estándar (por ejemplo, la entrada interactiva). Si se usan excepciones de clase, las instancias de esta clase tienen disponibles los atributos filename (nombre del archivo), lineno (nº de línea), offset (nº de columna) y text (texto), que ofrecen un acceso más fácil a los detalles. En las excepciones de cadena, el valor asociado suele ser una tupla de la forma (mensaje, (nombreFichero, numLinea, columna, texto)). En las excepciones de clase, str() sólo devuelve el mensaje. Deriva de la clase raíz StandardError.
SystemError
Se lanza cuando el intérprete encuentra un error interno, pero la situación no parece tan grave como para perder la esperanza. El valor asociado es una cadena que indica qué ha ido mal (en términos de bajo nivel). Se debería dar parte de este error al autor o mantenedor del intérprete Python en cuestión. Se debe incluir en el informe la cadena de versión del intérprete Python (sys.version, que también se muestra al inicio de una sesión interactiva), la causa exacta del error y, si es posible, el código fuente del programa que provocó el error. Deriva de la clase raíz StandardError.
SystemExit
Lanzada por la función sys.exit(). Si no se captura, el intérprete de Python finaliza la ejecución sin presentar una pila de llamadas. Si el valor asociado es un entero normal, especifica el estado de salida al sistema (se pasa a la función de C exit()), Si es None, el estado de salida es cero (que indica una salida normal sin errores). En el caso de ser de otro tipo, se presenta el valor del objeto y el estado de salida será 1. Las instancias tienen un atributo code cuyo valor se establece al estado de salida o mensaje de error propuesto (inicialmente None). Además, esta excepción deriva directamente de Exception y no de la excepción StandardError, ya que técnicamente no es un error. Una llamada a sys.exit() se traduce a un error para que los gestores de limpieza final (las sentencias finally de las sentencias try) se puedan ejecutar y para que un depurador pueda ejecutar un guion sin riesgo de perder el control. Se puede usar la función os._exit() si es total y absolutamente necesario salir inmediatamente (por ejemplo, tras un fork() en el proceso hijo). Deriva de la clase raíz BaseException.
ReferenceError
Se lanza cuando se usó un proxy de referencia débil después de que el referente desapareció. Deriva de la excepción StandardError.
TabError
Se lanza cuando sucede una mezcla inadecuada de espacios y tabulaciones. Deriva de la excepción IndentationError.
TypeError
Se lanza cuando una operación o función interna se aplica a un objeto de tipo inadecuado. El valor asociado es una cadena con detalles de la incoherencia de tipos. Deriva de la clase raíz StandardError.
UnboundLocalError
Se lanza cuando se hace referencia a una variable local en una función o método, pero no se ha asignado un valor a dicha variable. Deriva de la excepción NameError.
UnicodeError
Se lanza cuando se da un error relativo a codificación/descodificación Unicode. Deriva de la excepción ValueError.
UnicodeDecodeError
Se lanza cuando un error al decodificar Unicode sucede. Deriva de la excepción UnicodeError.
UnicodeEncodeError
Se lanza cuando un error al codificar Unicode sucede. Deriva de la excepción UnicodeError.
UnicodeTranslateError
Se lanza cuando un error al traducir Unicode sucede. Deriva de la excepción UnicodeError.
ValueError
Se lanza cuando una operación o función interna recibe un argumento del tipo correcto, pero con un valor inapropiado y no es posible describir la situación con una excepción más precisa, como IndexError.
ZeroDivisionError
Se lanza cuando el segundo argumento de una operación de división o módulo es cero. El valor asociado es una cadena que indica el tipo de operandos y la operación. Deriva de la clase raíz ArithmeticError.
Warning
La clase base para las categorías de advertencias. Deriva de la excepción Exception.
BytesWarning
La clase base para las advertencias acerca de problemas relacionados con bytes y buffer, mas relacionado a la conversión desde str o comparando a str. Deriva de la excepción Warning.
DeprecationWarning
La clase base para las advertencias acerca de características obsoletas. Deriva de la excepción Warning.
FutureWarning
La clase base para las advertencias acerca de constructores que pueden ser cambiado sistemáticamente en el futuro. Deriva de la excepción Warning.
ImportWarning
La clase base para las advertencias acerca de probables errores en importar módulos. Deriva de la excepción Warning.
PendingDeprecationWarning
La clase base para las advertencias acerca de características las cuales serán obsoletas en el futuro. Deriva de la excepción Warning.
RuntimeWarning
La clase base para las advertencias acerca de comportamiento del tiempo de ejecución dudosa. Deriva de la excepción Warning.
SyntaxWarning
La clase base para las advertencias acerca de sintaxis dudosa. Deriva de la excepción Warning.
UnicodeWarning
La clase base para las advertencias acerca de problemas relacionado con Unicode, más relacionado a problemas de conversión. Deriva de la excepción Warning.
UserWarning
La clase base para las advertencias generadas por código de usuario. Deriva de la excepción Warning.

Errores y excepciones en Python

Errores y excepciones

Hasta ahora los mensajes de error no habían sido más que mencionados, pero si probaste los ejemplos probablemente hayas visto algunos. Hay (al menos) dos tipos diferentes de errores: errores de sintaxis y excepciones.

Errores de sintaxis

Los errores de sintaxis, también conocidos como errores de interpretación, son quizás el tipo de queja más común que tenés cuando todavía estás aprendiendo Python:

>>> while True print 'Hola Mundo'
Traceback (most recent call last):
...
    while True print 'Hola Mundo'
                   ^
SyntaxError: invalid syntax

El intérprete repite la línea culpable y muestra una pequeña “flecha” que apunta al primer lugar donde se detectó el error. Este es causado por (o al menos detectado en) el símbolo que precede a la flecha: en el ejemplo, el error se detecta en la sentencia print, ya que faltan dos puntos (':') antes del mismo. Se muestran el nombre del archivo y el número de línea para que sepas dónde mirar en caso de que la entrada venga de un programa.

Excepciones

Incluso si la sentencia o expresión es sintácticamente correcta, puede generar un error cuando se intenta ejecutarla. Los errores detectados durante la ejecución se llaman excepciones, y no son incondicionalmente fatales: pronto aprenderás cómo manejarlos en los programas en Python. Sin embargo, la mayoría de las excepciones no son manejadas por los programas, y resultan en mensajes de error como los mostrados aquí:

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects

La última línea de los mensajes de error indica qué sucedió. Las excepciones vienen de distintos tipos, y el tipo se imprime como parte del mensaje: los tipos en el ejemplo son: ZeroDivisionErrorNameError y TypeError.

La cadena mostrada como tipo de la excepción es el nombre de la excepción predefinida que ocurrió. Esto es verdad para todas las excepciones predefinidas del intérprete, pero no necesita ser verdad para excepciones definidas por el usuario (aunque es una convención útil). Los nombres de las excepciones estándar son identificadores incorporados al intérprete (no son palabras clave reservadas).

El resto de la línea provee un detalle basado en el tipo de la excepción y qué la causó.

La parte anterior del mensaje de error muestra el contexto donde la excepción sucedió, en la forma de un trazado del error listando líneas fuente; sin embargo, no mostrará líneas leídas desde la entrada estándar.

Excepciones integradas, es una lista las excepciones predefinidas y sus significados.

Manejando excepciones

Es posible escribir programas que manejen determinadas excepciones. Mirá el siguiente ejemplo, que le pide al usuario una entrada hasta que ingrese un entero válido, pero permite al usuario interrumpir el programa (usando Control-C o lo que sea que el sistema operativo soporte); notá que una interrupción generada por el usuario se señaliza generando la excepción KeyboardInterrupt.

>>> while True:
...     try:
...         x = int(raw_input(u"Por favor ingrese un número: "))
...         break
...     except ValueError:
...         print u"Oops!  No era válido. Intente nuevamente..."
...

La sentencia try funciona de la siguiente manera:

  • Primero, se ejecuta el bloque try (el código entre las sentencias try y except).
  • Si no ocurre ninguna excepción, el bloque except se saltea y termina la ejecución de la sentencia try.
  • Si ocurre una excepción durante la ejecución del bloque try, el resto del bloque se saltea. Luego, si su tipo coincide con la excepción nombrada luego de la palabra reservada except, se ejecuta el bloque except, y la ejecución continúa luego de la sentencia try.
  • Si ocurre una excepción que no coincide con la excepción nombrada en el except, esta se pasa a declaraciones try de más afuera; si no se encuentra nada que la maneje, es una excepción no manejada, y la ejecución se frena con un mensaje como los mostrados arriba.

Una sentencia try puede tener más de un except, para especificar manejadores para distintas excepciones. A lo sumo un manejador será ejecutado. Sólo se manejan excepciones que ocurren en el correspondiente try, no en otros manejadores del mismo try. Un except puede nombrar múltiples excepciones usando paréntesis, por ejemplo:

... except (RuntimeError, TypeError, NameError):
...     pass

El último except puede omitir nombrar qué excepción captura, para servir como comodín. Usá esto con extremo cuidado, ya que de esta manera es fácil ocultar un error real de programación. También puede usarse para mostrar un mensaje de error y luego re-generar la excepción (permitiéndole al que llama, manejar también la excepción):

import sys

try:
    f = open('numeros.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as (errno, strerror):
    print "Error E/S ({0}): {1}".format(errno, strerror)
except ValueError:
    print "No pude convertir el dato a un entero."
except:
    print "Error inesperado:", sys.exc_info()[0]
    raise

Las declaraciones try … except tienen un bloque else opcional, el cual, cuando está presente, debe seguir a los except. Es útil para aquel código que debe ejecutarse si el bloque try no genera una excepción. Por ejemplo:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'no pude abrir', arg
    else:
        print arg, 'tiene', len(f.readlines()), 'lineas'
        f.close()

El uso de else es mejor que agregar código adicional en el try porque evita capturar accidentalmente una excepción que no fue generada por el código que está protegido por la sentencia try … except.

Cuando ocurre una excepción, puede tener un valor asociado, también conocido como el argumento de la excepción. La presencia y el tipo de argumento depende del tipo de excepción.

El except puede especificar una variable luego del nombre (o tupla) de excepción(es). La variable se vincula a una instancia de excepción con los argumentos almacenados en instance.args. Por conveniencia, la instancia de excepción define __str__() para que se pueda mostrar los argumentos directamente, sin necesidad de hacer referencia a .args.

Uno también puede instanciar una excepción antes de generarla, y agregarle cualquier atributo que se desee:

>>> try:
...    raise Exception('carne', 'huevos')
... except Exception as inst:
...    print type(inst)     # la instancia de excepción
...    print inst.args      # argumentos guardados en .args
...    print inst           # __str__ permite imprimir args directamente
...    x, y = inst          # __getitem__ permite usar args directamente
...    print 'x =', x
...    print 'y =', y
...
<type 'exceptions.Exception'>
('carne', 'huevos')
('carne', 'huevos')
x = carne
y = huevos

Si una excepción tiene un argumento, este se imprime como la última parte (el “detalle”) del mensaje para las excepciones que no están manejadas.

Los manejadores de excepciones no manejan solamente las excepciones que ocurren en el bloque try, también manejan las excepciones que ocurren dentro de las funciones que se llaman (inclusive indirectamente) dentro del bloque try. Por ejemplo:

>>> def esto_falla():
...     x = 1/0
...
>>> try:
...     esto_falla()
... except ZeroDivisionError as detail:
...     print 'Manejando error en tiempo de ejecución:', detail
...
Manejando error en tiempo de ejecución: integer division or modulo by zero

Levantando excepciones

La sentencia raise permite al programador forzar a que ocurra una excepción específica. Por ejemplo:

>>> raise NameError('Hola')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: Hola

El único argumento a raise indica la excepción a generarse. Tiene que ser o una instancia de excepción, o una clase de excepción (una clase que hereda de Exception).

Si necesitás determinar cuando una excepción fue lanzada pero no querés manejarla, una forma simplificada de la sentencia raise te permite relanzarla:

>>> try:
...     raise NameError('Hola')
... except NameError:
...     print u'Ha sucedido una excepción!'
...     raise
...
Ha sucedido una excepción!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: Hola

Sentencia assert

La sentencia assert es una vía conveniente para insertar afirmaciones de depuración dentro de un programa:

La forma simple, «assert expression», es equivalente a:

if __debug__:
    if not expression: raise AssertionError

La forma extendida, «assert expression1, expression2», es equivalente a:

if __debug__:
    if not expression1: raise AssertionError(expression2)

Estas equivalencias suponen que __debug__ y la excepción «AssertionError» se refieren a las variables incorporadas con esos nombres. En la corriente implementación, la variable incorporada __debug__ es True en circunstancias normales, False cuando se solicita la optimización (opción del línea de comando -O). El generador de código actual no emite ningún código para una sentencia assert cuando se solicita la optimización en tiempo de compilación. Nota que no es necesario incluir el código fuente de la expresión que falló en el mensaje de error; se mostrará como parte del stack trace.

Asignaciones a __debug__ son ilegales. El valor para la variable integrada es determinada cuando el interprete inicia.

Excepciones definidas por el usuario

Los programas pueden nombrar sus propias excepciones creando una nueva clase excepción (mirá el apartado de Clases para más información sobre las clases de Python). Las excepciones, típicamente, deberán derivar de la clase Exception, directa o indirectamente. Por ejemplo:

>>> class MiError(Exception):
...     def __init__(self, valor):
...         self.valor = valor
...     def __str__(self):
...         return repr(self.valor)
...
>>> try:
...     raise MiError(2*2)
... except MiError as e:
...     print u'Ha ocurrido mi excepción, valor:', e.valor
...
Ocurrió mi excepción, valor: 4
>>> raise MiError('oops!')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MiError: 'oops!'

En este ejemplo, el método __init__() de Exception fue sobrescrito. El nuevo comportamiento simplemente crea el atributo valor.

Esto reemplaza el comportamiento por defecto de crear el atributo args.

Las clases de Excepciones pueden ser definidas de la misma forma que cualquier otra clase, pero usualmente se mantienen simples, a menudo solo ofreciendo un número de atributos con información sobre el error que leerán los manejadores de la excepción. Al crear un módulo que puede lanzar varios errores distintos, una práctica común es crear una clase base para excepciones definidas en ese módulo y extenderla para crear clases excepciones específicas para distintas condiciones de error:

class Error(Exception):
    """Clase base para excepciones en el módulo."""
    pass

class EntradaError(Error):
    """Exception lanzada por errores en las entradas.

    Atributos:
        expresion -- expresión de entrada en la que ocurre el error
        mensaje -- explicación del error
    """

    def __init__(self, expresion, mensaje):
        self.expresion = expresion
        self.mensaje = mensaje

class TransicionError(Error):
    """Lanzada cuando una operación intenta una
       transición de estado no permitida.

    Atributos:
        previo -- estado al principio de la transición
        siguiente -- nuevo estado intentado
        mensaje -- explicación de porque la transición no esta permitida
    """
    def __init__(self, previo, siguiente, mensaje):
        self.previo = previo
        self.siguiente = siguiente
        self.mensaje = mensaje

La mayoría de las excepciones son definidas con nombres que terminan en «Error», similares a los nombres de las excepciones estándar.

Muchos módulos estándar definen sus propias excepciones para reportar errores que pueden ocurrir en funciones propias. Se puede encontrar más información sobre clases en el capítulo Clases.

Definiendo acciones de limpieza

La sentencia try tiene otra sentencia opcional que intenta definir acciones de limpieza que deben ser ejecutadas bajo ciertas circunstancias. Por ejemplo:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print 'Adiós, Mundo!'
...
Chau, Mundo!
KeyboardInterrupt
Traceback (most recent call last):
  File "<stdin>", line 2, in ?

Una sentencia finally siempre es ejecutada antes de salir de la sentencia try, ya sea que una excepción haya ocurrido o no. Cuando ocurre una excepción en la sentencia try y no fue manejada por una sentencia except (o ocurrió en una sentencia except o else), es relanzada luego de que se ejecuta la sentencia finally. La sentencia finally es también ejecutada «a la salida» cuando cualquier otra sentencia de la sentencia try es dejada vía breakcontinue or return. Un ejemplo más complicado (sentencias except y finally en la misma sentencia try):

>>> def dividir(x, y):
...     try:
...         resultado = x / y
...     except ZeroDivisionError:
...         print "¡división por cero!"
...     else:
...         print "el resultado es", resultado
...     finally:
...         print "ejecutando la clausula finally"
...
>>> dividir(2, 1)
el resultado es 2
ejecutando la clausula finally
>>> dividir(2, 0)
¡división por cero!
ejecutando la clausula finally
>>> divide("2", "1")
ejecutando la clausula finally
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Como puedes ver, la sentencia finally es ejecutada siempre. La excepción TypeError lanzada al dividir dos cadenas de caracteres no es manejado por la sentencia except y por lo tanto es relanzada luego de que se ejecuta la sentencia finally.

En aplicaciones reales, la sentencia finally es útil para liberar recursos externos (como archivos o conexiones de red), sin importar si el uso del recurso fue exitoso.

Acciones predefinidas de limpieza

Algunos objetos definen acciones de limpieza estándar que llevar a cabo cuando el objeto no es más necesitado, independientemente de que las operaciones sobre el objeto hayan sido exitosas o no. Mirá el siguiente ejemplo, que intenta abrir un archivo e imprimir su contenido en la pantalla.

for linea in open("numeros.txt"):
    print linea

El problema con este código es que deja el archivo abierto por un periodo de tiempo indeterminado luego de que termine de ejecutarse. Esto no es un problema en scripts simples, pero puede ser un problema en aplicaciones más grandes.

Sentencia with

La sentencia with permite que objetos como archivos sean usados de una forma que asegure que siempre se los libera rápido y en forma correcta.

with open("numeros.txt") as f:
    for linea in f:
        print linea

Luego de que la sentencia sea ejecutada, el archivo f siempre es cerrado, incluso si se encuentra un problema al procesar las líneas. Otros objetos que provean acciones de limpieza predefinidas lo indicarán en su documentación.

Traceback

El Traceback o trazado inverso, es un listado de las funciones en curso de ejecución, presentadas cuando sucede un error en tiempo de ejecución. Es común que al trazado inverso también se le conozca como trazado de pila, porque lista las funciones en el orden en el cual son almacenadas en la pila de llamadas.

El módulo integrado traceback incorpora el comportamiento de Traceback o trazado inverso ya que extrae, formatea e imprime información acerca de trazado del stack de los errores y excepciones en Python.

>>> import traceback
>>> traceback.__doc__
'Extract, format and print information about Python stack traces.'
>>> help(traceback)

Distribución de Software | Python

Distribución de Software

La distribución de código Python, le permite hacer portable de forma amigable usando herramienta de gestión de paquetes Python como la herramienta pip. Esta labor se hace mediante el módulo distutils, y más reciente incorporando el módulo setuptools.

 

Módulo distutils

Permite «empacar» el código de un proyecto de software para ser redistribuido en otros proyectos Python.

Cada paquete empaquetado se puede distribuir en su propia pagina de proyecto y al mismo tiempo puede optar a publicar su proyecto en el Python Package Index (PyPI), con el cual si lo publica allí su proyecto estará a su alcance y sino de muchos mas programadores, ya que es un repositorio de software publico, solo con ejecutar el comando pip install <paquete> lo convierte en una herramienta tremendamente útil y probablemente sea una de las razones del éxito de Python entre los que empiezan a programar.

 

Módulo setuptools

El módulo setuptools, incorpora varias extensiones al módulo distutils para distribuciones de software grandes o complejas.

 

Estructura de proyecto

Para poder empaquetar un proyecto necesita como mínimo la estructura de archivos siguiente:

DIRECTORIO-DEL-PROYECTO
├── LICENSE
├── MANIFEST.in
├── README.txt
├── setup.py
└── NOMBRE-DEL-PAQUETE
     ├── __init__.py
     ├── ARCHIVO1.py
     ├── ARCHIVO2.py
     └── MODULO (OPCIONAL)
           ├── __init__.py
           └── MAS_ARCHIVOS.py

A continuación se detallan el significado y uso de la estructura de directorio anterior:

  • DIRECTORIO-DEL-PROYECTO puede ser cualquiera, no afecta en absoluto, lo que cuenta es lo que hay dentro.

  • NOMBRE-DEL-PAQUETE tiene que ser el nombre del paquete, si el nombre es tostadas_pipo, este directorio tiene que llamarse también tostadas_pipo. Y esto es así. Dentro estarán todos los archivos que forman la librería.

  • LICENSE: es el archivo donde se define los términos de licencia usado en su proyecto. Es muy importate que cada paquete cargado a PyPI incluirle una copia de los términos de licencia. Esto le dice a los usuario quien instala el paquete los términos bajos los cuales pueden usarlo en su paquete. Para ayuda a seleccionar una licencia, consulte https://choosealicense.com/. Una vez tenga seleccionado una licencia abra el archivo LICENSE e ingrese el texto de la licencia. Por ejemplo, si usted elije la licencia GPL:

    License
    =======
    
    PACKAGE-NAME Copyright YEAR, PACKAGE-AUTHOR
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
    MA 02111-1307 USA.
    
  • MANIFEST.in: es el archivo donde se define los criterios de inclusión y exclusión de archivos a su distribución de código fuente de su proyecto. Este archivo incluye la configuración del paquete como se indica a continuación:

include LICENSE
include *.txt *.in
include *.py
recursive-include tostadas_pipo *
global-exclude *.pyc *.pyo *~
prune build
prune dist
  • README.txt: es el archivo donde se define la documentación general del paquete, este archivo es importante debido a que no solo es usado localmente en un copia descargada, sino como información usada el en sitio de PyPI. Entonces abra el archivo README.txt e ingrese el siguiente contenido. Usted puede personalizarlo como quiera:

    ==================
    NOMBRE-DEL-PAQUETE
    ==================
    
    Este es un ejemplo simple de un paquete Python.
    
    Usted puede usar para escribir este contenido la guía
    `Restructured Text (reST) and Sphinx CheatSheet <http://openalea.gforge.inria.fr/doc/openalea/doc/_build/html/source/sphinx/rest_syntax.html>`_.
    
  • setup.py: es el archivo donde se define el paquete, el formato es el mismo para el módulo setuptools y para el módulo distutils. Lo puede ver a continuación. Este archivo incluye la configuración del paquete como se indica a continuación:

"""Instalador para el paquete "tostadas_pipo"."""

from setuptools import setup

long_description = (
    open('README.txt').read()
    + '\n' +
    open('LICENSE').read()
    + '\n')

setup(
    name="tostadas_pipo",
    version="0.1",
    description="Sistema Administrativo de Tostadas Pipo C.A.",
    long_description=long_description,
    # Get more https://pypi.org/pypi?%3Aaction=list_classifiers
    classifiers=[
        # ¿Cuan maduro esta este proyecto? Valores comunes son
        #   3 - Alpha
        #   4 - Beta
        #   5 - Production/Stable
        "Development Status :: 3 - Alpha",
        # Indique a quien va dirigido su proyecto
        "Environment :: Console",
        "Intended Audience :: Developers",
        "Topic :: Software Development :: Libraries",
        # Indique licencia usada (debe coincidir con el "license")
        "License :: OSI Approved :: GNU General Public License",
        # Indique versiones soportas, Python 2, Python 3 o ambos.
        "Programming Language :: Python",
        "Programming Language :: Python :: 2.7",
        "Operating System :: OS Independent",
    ],
    keywords="ejemplo instalador paquete tostadas_pipo",
    author="Leonardo J. Caballero G.",
    author_email="leonardocaballero@gmail.com",
    url="https://twitter/macagua",
    download_url="https://github.com/macagua/tostadas_pipo",
    license="GPL",
    platforms="Unix",
    packages=["tostadas_pipo", "tostadas_pipo/utilidades/"],
    include_package_data=True,
)

Entonces debe cree la siguiente estructura de directorios, ya hecha para seguir adelante:

distribucion/
├── LICENSE
├── MANIFEST.in
├── README.txt
├── setup.py
└── tostadas_pipo
    ├── __init__.py
    ├── principal.py
    └── utilidades
        ├── calculos.py
        ├── impuestos.py
        └── __init__.py

 

Construir dependencias

Para construir cualquier cosas requeridas para instalar el paquete, ejecutando el siguiente comando:

python ./setup.py -v build
running build
running build_py
creating build
creating build/lib.linux-x86_64-2.7
creating build/lib.linux-x86_64-2.7/tostadas_pipo
copying tostadas_pipo/__init__.py -> build/lib.linux-x86_64-2.7/tostadas_pipo
copying tostadas_pipo/principal.py -> build/lib.linux-x86_64-2.7/tostadas_pipo
creating build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/__init__.py -> build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/calculos.py -> build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/impuestos.py -> build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades
not copying tostadas_pipo/principal.py (output up-to-date)
not copying tostadas_pipo/__init__.py (output up-to-date)

De esta forma al terminar la ejecución del comando previo debe tener creado un directorio llamado build e incluyendo el paquete tostadas_pipo construido con todo lo necesario para crear su distribución, como se muestra a continuación:

build/
└── lib.linux-x86_64-2.7
    └── tostadas_pipo
        ├── __init__.py
        ├── principal.py
        └── utilidades
            ├── calculos.py
            ├── impuestos.py
            └── __init__.py

De esta forma ya construyo el paquete tostadas_pipo y todas las cosas necesarias para crear su distribución de código fuente o binaria para su proyecto.

 

Crear paquete

Usted puede crear diversos tipos de formatos de instalación y distribución de sus paquetes Python, a continuación se describen los mas usados:

 

Distribución código fuente

Tanto el módulo setuptools y distutils le permiten crear una distribución de código fuente o source distribution (sdist) de su paquete en formatos como tarball, archivo zip, etc. Para crear una paquete sdist, ejecute el siguiente comando:

python ./setup.py -v sdist
running sdist
running egg_info
creating tostadas_pipo.egg-info
writing tostadas_pipo.egg-info/PKG-INFO
writing top-level names to tostadas_pipo.egg-info/top_level.txt
writing dependency_links to tostadas_pipo.egg-info/dependency_links.txt
writing entry points to tostadas_pipo.egg-info/entry_points.txt
writing manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
reading manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.pyc' found anywhere in distribution
warning: no previously-included files matching '*.pyo' found anywhere in distribution
warning: no previously-included files matching '*~' found anywhere in distribution
no previously-included directories found matching 'build'
no previously-included directories found matching 'dist'
writing manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
running check
creating tostadas_pipo-0.1
creating tostadas_pipo-0.1/tostadas_pipo
creating tostadas_pipo-0.1/tostadas_pipo.egg-info
creating tostadas_pipo-0.1/tostadas_pipo/utilidades
copying files to tostadas_pipo-0.1...
copying LICENSE -> tostadas_pipo-0.1
copying MANIFEST.in -> tostadas_pipo-0.1
copying README.txt -> tostadas_pipo-0.1
copying setup.py -> tostadas_pipo-0.1
copying tostadas_pipo/__init__.py -> tostadas_pipo-0.1/tostadas_pipo
copying tostadas_pipo/principal.py -> tostadas_pipo-0.1/tostadas_pipo
copying tostadas_pipo.egg-info/PKG-INFO -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/SOURCES.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/dependency_links.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/entry_points.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/top_level.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo/utilidades/__init__.py -> tostadas_pipo-0.1/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/calculos.py -> tostadas_pipo-0.1/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/impuestos.py -> tostadas_pipo-0.1/tostadas_pipo/utilidades
not copying tostadas_pipo.egg-info/SOURCES.txt (output up-to-date)
Reading configuration from tostadas_pipo-0.1/setup.cfg
Adding new section [egg_info] to tostadas_pipo-0.1/setup.cfg
Setting egg_info.tag_build to '' in tostadas_pipo-0.1/setup.cfg
Setting egg_info.tag_date to 0 in tostadas_pipo-0.1/setup.cfg
Writing tostadas_pipo-0.1/setup.cfg
creating dist
Creating tar archive
removing 'tostadas_pipo-0.1' (and everything under it)

De esta forma al terminar la ejecución del comando previo debe tener creado un directorio llamado dist e incluyendo el paquete en formato de archivo tarball comprimido en gztar, como se muestra a continuación:

dist/
└── tostadas_pipo-0.1.tar.gz

Por defecto, tanto el módulo setuptools y distutils creá el paquete en formato de archivo tarball comprimido usando gztar.).

Usted puede cambiar el formato de paquete a crear de su distribución de código fuente de su paquete (en formato archivo tarball, archivo zip, etc.), ejecute el siguiente comando:

python ./setup.py sdist --formats=zip,gztar,bztar
running sdist
running egg_info
writing tostadas_pipo.egg-info/PKG-INFO
writing top-level names to tostadas_pipo.egg-info/top_level.txt
writing dependency_links to tostadas_pipo.egg-info/dependency_links.txt
writing entry points to tostadas_pipo.egg-info/entry_points.txt
reading manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.pyc' found anywhere in distribution
warning: no previously-included files matching '*.pyo' found anywhere in distribution
warning: no previously-included files matching '*~' found anywhere in distribution
no previously-included directories found matching 'build'
no previously-included directories found matching 'dist'
writing manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
running check
creating tostadas_pipo-0.1
creating tostadas_pipo-0.1/tostadas_pipo
creating tostadas_pipo-0.1/tostadas_pipo.egg-info
creating tostadas_pipo-0.1/tostadas_pipo/utilidades
copying files to tostadas_pipo-0.1...
copying LICENSE -> tostadas_pipo-0.1
copying MANIFEST.in -> tostadas_pipo-0.1
copying README.txt -> tostadas_pipo-0.1
copying setup.py -> tostadas_pipo-0.1
copying tostadas_pipo/__init__.py -> tostadas_pipo-0.1/tostadas_pipo
copying tostadas_pipo/principal.py -> tostadas_pipo-0.1/tostadas_pipo
copying tostadas_pipo.egg-info/PKG-INFO -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/SOURCES.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/dependency_links.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/entry_points.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo.egg-info/top_level.txt -> tostadas_pipo-0.1/tostadas_pipo.egg-info
copying tostadas_pipo/utilidades/__init__.py -> tostadas_pipo-0.1/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/calculos.py -> tostadas_pipo-0.1/tostadas_pipo/utilidades
copying tostadas_pipo/utilidades/impuestos.py -> tostadas_pipo-0.1/tostadas_pipo/utilidades
Writing tostadas_pipo-0.1/setup.cfg
creating 'dist/tostadas_pipo-0.1.zip' and adding 'tostadas_pipo-0.1' to it
adding 'tostadas_pipo-0.1/MANIFEST.in'
adding 'tostadas_pipo-0.1/setup.cfg'
adding 'tostadas_pipo-0.1/PKG-INFO'
adding 'tostadas_pipo-0.1/LICENSE'
adding 'tostadas_pipo-0.1/README.txt'
adding 'tostadas_pipo-0.1/setup.py'
adding 'tostadas_pipo-0.1/tostadas_pipo/principal.py'
adding 'tostadas_pipo-0.1/tostadas_pipo/__init__.py'
adding 'tostadas_pipo-0.1/tostadas_pipo/utilidades/impuestos.py'
adding 'tostadas_pipo-0.1/tostadas_pipo/utilidades/__init__.py'
adding 'tostadas_pipo-0.1/tostadas_pipo/utilidades/calculos.py'
adding 'tostadas_pipo-0.1/tostadas_pipo.egg-info/dependency_links.txt'
adding 'tostadas_pipo-0.1/tostadas_pipo.egg-info/entry_points.txt'
adding 'tostadas_pipo-0.1/tostadas_pipo.egg-info/PKG-INFO'
adding 'tostadas_pipo-0.1/tostadas_pipo.egg-info/SOURCES.txt'
adding 'tostadas_pipo-0.1/tostadas_pipo.egg-info/top_level.txt'
Creating tar archive
Creating tar archive
removing 'tostadas_pipo-0.1' (and everything under it)

De esta forma al terminar la ejecución del comando previo debe tener creado un directorio llamado dist e incluyendo los tres paquetes en formatos de archivos tarball comprimido en gzip/bzip2 y archivo comprimido en zip.

dist/
├── tostadas_pipo-0.1.tar.bz2
├── tostadas_pipo-0.1.tar.gz
└── tostadas_pipo-0.1.zip

De esta forma ya creo el(los) paquete(s) en diversos formato de distribución de código fuente para su proyecto.

 

Distribución binaria

El módulo setuptools y distutils le permiten crear una distribución binaria construida o built «binary» distribution (bdist) de su paquete en formato eggwheelrpm, etc. A continuación se describen los mas usados:

../_images/python_eggs.jpg

Distribución binaria Egg.

Para crear una distribución bdist de su paquete en formato egg, ejecute el siguiente comando:

python ./setup.py bdist_egg
running bdist_egg
running egg_info
writing tostadas_pipo.egg-info/PKG-INFO
writing top-level names to tostadas_pipo.egg-info/top_level.txt
writing dependency_links to tostadas_pipo.egg-info/dependency_links.txt
writing entry points to tostadas_pipo.egg-info/entry_points.txt
reading manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.pyc' found anywhere in distribution
warning: no previously-included files matching '*.pyo' found anywhere in distribution
warning: no previously-included files matching '*~' found anywhere in distribution
no previously-included directories found matching 'build'
no previously-included directories found matching 'dist'
writing manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/tostadas_pipo
copying build/lib.linux-x86_64-2.7/tostadas_pipo/principal.py -> build/bdist.linux-x86_64/egg/tostadas_pipo
copying build/lib.linux-x86_64-2.7/tostadas_pipo/__init__.py -> build/bdist.linux-x86_64/egg/tostadas_pipo
creating build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/impuestos.py -> build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/__init__.py -> build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/calculos.py -> build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/principal.py to principal.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades/impuestos.py to impuestos.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades/calculos.py to calculos.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/entry_points.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating 'dist/tostadas_pipo-0.1-py2.7.egg' and adding 'build/bdist.linux-x86_64/egg' to it
removing 'build/bdist.linux-x86_64/egg' (and everything under it)

De esta forma al terminar la ejecución del comando previo debe tener creado un directorio llamado dist e incluyendo la distribución bdist en formato egg.

dist/
└── tostadas_pipo-0.1-py2.7.egg

De esta forma ya creo la distribución bdist del paquete en formato egg para su proyecto.

Wheel

Para crear una distribución bdist de su paquete en formato wheel, ejecute el siguiente comando:

python ./setup.py bdist_wheel
running bdist_wheel
running build
running build_py
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64/wheel
creating build/bdist.linux-x86_64/wheel/tostadas_pipo
copying build/lib.linux-x86_64-2.7/tostadas_pipo/principal.py -> build/bdist.linux-x86_64/wheel/tostadas_pipo
copying build/lib.linux-x86_64-2.7/tostadas_pipo/__init__.py -> build/bdist.linux-x86_64/wheel/tostadas_pipo
creating build/bdist.linux-x86_64/wheel/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/impuestos.py -> build/bdist.linux-x86_64/wheel/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/__init__.py -> build/bdist.linux-x86_64/wheel/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/calculos.py -> build/bdist.linux-x86_64/wheel/tostadas_pipo/utilidades
running install_egg_info
running egg_info
writing tostadas_pipo.egg-info/PKG-INFO
writing top-level names to tostadas_pipo.egg-info/top_level.txt
writing dependency_links to tostadas_pipo.egg-info/dependency_links.txt
writing entry points to tostadas_pipo.egg-info/entry_points.txt
reading manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.pyc' found anywhere in distribution
warning: no previously-included files matching '*.pyo' found anywhere in distribution
warning: no previously-included files matching '*~' found anywhere in distribution
no previously-included directories found matching 'build'
no previously-included directories found matching 'dist'
writing manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
Copying tostadas_pipo.egg-info to build/bdist.linux-x86_64/wheel/tostadas_pipo-0.1-py2.7.egg-info
running install_scripts
adding license file "LICENSE" (matched pattern "LICEN[CS]E*")
creating build/bdist.linux-x86_64/wheel/tostadas_pipo-0.1.dist-info/WHEEL
creating 'dist/tostadas_pipo-0.1-py2-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'tostadas_pipo/__init__.py'
adding 'tostadas_pipo/principal.py'
adding 'tostadas_pipo/utilidades/__init__.py'
adding 'tostadas_pipo/utilidades/calculos.py'
adding 'tostadas_pipo/utilidades/impuestos.py'
adding 'tostadas_pipo-0.1.dist-info/LICENSE'
adding 'tostadas_pipo-0.1.dist-info/METADATA'
adding 'tostadas_pipo-0.1.dist-info/WHEEL'
adding 'tostadas_pipo-0.1.dist-info/entry_points.txt'
adding 'tostadas_pipo-0.1.dist-info/top_level.txt'
adding 'tostadas_pipo-0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel

De esta forma al terminar la ejecución del comando previo debe tener creado un directorio llamado dist e incluyendo la distribución bdist en formato whl.

dist/
├── tostadas_pipo-0.1-py2.7.egg
└── tostadas_pipo-0.1-py2-none-any.whl

De esta forma ya creo la distribución bdist del paquete en formato whl para su proyecto.

 

Instalar paquete

Para instalar el paquete de su proyecto, hay dos formas de instalación disponibles a continuación:

 

Instalar distribución código fuente

Para instalar una distribución código fuente de su paquete previamente creado, se realizar usando la herramienta pip, ejecutando el siguiente comando:

pip install --user dist/tostadas_pipo-0.1.tar.gz

Si al ejecutar el comando anterior muestra el mensaje:

pip
bash: pip: no se encontró la orden

Esto es debido a que no tiene instalado dicha herramienta, así que debe ejecutar el siguiente comando:

sudo apt-get install -y python-pip

De nuevo vuelva a ejecutar en su consola de comando el comando:

pip install --user dist/tostadas_pipo-0.1.tar.gz
Processing ./dist/tostadas_pipo-0.1.tar.gz
Building wheels for collected packages: tostadas-pipo
  Running setup.py bdist_wheel for tostadas-pipo ... done
  Stored in directory: /home/leonardo/.cache/pip/wheels/fd/f9/75/a6965566a3c5a8bff507d7daa30760caca0a7525a3de61eac2
Successfully built tostadas-pipo
Installing collected packages: tostadas-pipo
Successfully installed tostadas-pipo-0.1

De esta forma tiene instalado una distribución código fuente en formato tarball de su paquete en el interprete Python usando la herramienta pip.

 

Instalar distribución binaria

Para instalar una distribución binaria de su paquete previamente creado, se realizar usando la herramienta pip, ejecutando el siguiente comando:

pip install --user ./dist/tostadas_pipo-0.1-py2-none-any.whl
Processing ./dist/tostadas_pipo-0.1-py2-none-any.whl
Installing collected packages: tostadas-pipo
Successfully installed tostadas-pipo-0.1

De esta forma tiene instalado una distribución binaria en formato wheel de su paquete en el interprete Python usando la herramienta pip.


Nota

pip, es una herramienta para instalación y administración de paquetes Python.


 

Instalar de código de proyecto

Para instalar el paquete desde el código de proyecto, ejecute el siguiente comando:

python ./setup.py -v install --user
running install
running bdist_egg
running egg_info
writing tostadas_pipo.egg-info/PKG-INFO
writing top-level names to tostadas_pipo.egg-info/top_level.txt
writing dependency_links to tostadas_pipo.egg-info/dependency_links.txt
reading manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.pyo' found anywhere in distribution
warning: no previously-included files matching '*~' found anywhere in distribution
no previously-included directories found matching 'build'
no previously-included directories found matching 'dist'
writing manifest file 'tostadas_pipo.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
not copying tostadas_pipo/principal.py (output up-to-date)
not copying tostadas_pipo/__init__.py (output up-to-date)
not copying tostadas_pipo/utilidades/impuestos.py (output up-to-date)
not copying tostadas_pipo/utilidades/__init__.py (output up-to-date)
not copying tostadas_pipo/utilidades/calculos.py (output up-to-date)
not copying tostadas_pipo/utilidades/__init__.py (output up-to-date)
not copying tostadas_pipo/utilidades/calculos.py (output up-to-date)
not copying tostadas_pipo/utilidades/impuestos.py (output up-to-date)
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/tostadas_pipo
copying build/lib.linux-x86_64-2.7/tostadas_pipo/principal.py -> build/bdist.linux-x86_64/egg/tostadas_pipo
copying build/lib.linux-x86_64-2.7/tostadas_pipo/__init__.py -> build/bdist.linux-x86_64/egg/tostadas_pipo
creating build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/impuestos.py -> build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/__init__.py -> build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
copying build/lib.linux-x86_64-2.7/tostadas_pipo/utilidades/calculos.py -> build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/principal.py to principal.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades/impuestos.py to impuestos.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-x86_64/egg/tostadas_pipo/utilidades/calculos.py to calculos.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying tostadas_pipo.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/tostadas_pipo-0.1-py2.7.egg' and adding 'build/bdist.linux-x86_64/egg' to it
adding 'tostadas_pipo/principal.py'
adding 'tostadas_pipo/principal.pyc'
adding 'tostadas_pipo/__init__.pyc'
adding 'tostadas_pipo/__init__.py'
adding 'tostadas_pipo/utilidades/impuestos.pyc'
adding 'tostadas_pipo/utilidades/calculos.pyc'
adding 'tostadas_pipo/utilidades/impuestos.py'
adding 'tostadas_pipo/utilidades/__init__.pyc'
adding 'tostadas_pipo/utilidades/__init__.py'
adding 'tostadas_pipo/utilidades/calculos.py'
adding 'EGG-INFO/zip-safe'
adding 'EGG-INFO/dependency_links.txt'
adding 'EGG-INFO/PKG-INFO'
adding 'EGG-INFO/SOURCES.txt'
adding 'EGG-INFO/top_level.txt'
removing 'build/bdist.linux-x86_64/egg' (and everything under it)
Processing tostadas_pipo-0.1-py2.7.egg
Copying tostadas_pipo-0.1-py2.7.egg to /home/leonardo/.local/lib/python2.7/site-packages
Adding tostadas-pipo 0.1 to easy-install.pth file
Saving /home/leonardo/.local/lib/python2.7/site-packages/easy-install.pth

Installed /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1-py2.7.egg
Processing dependencies for tostadas-pipo==0.1
Finished processing dependencies for tostadas-pipo==0.1

De esta forma tiene instalado su paquete en su interprete Python usando el comando install disponible con el script setup.py.


Advertencia

Al instalar el paquete usando el parámetro --user el paquete es instalado en el directorio $HOME/.local/lib/python2.7/site-packages/.


 

Comprobar la instalación

Usted puede comprobar luego de realizar la instalación de la distribución de código fuente o binaria de su paquete, ejecute el siguiente comando:

pip list --user --format=freeze | grep "tostadas"
tostadas-pipo==0.1

De esta forma la herramienta de gestión de paquete indica que el tostadas-pipo en su versión 0.1 esta instalado en su interprete Python.

 

Usar paquete

Usar el paquete tostadas_pipo-0.1, recuerde que debe usarlo como una librería, entonces puede probar el correcto funcionamiento del paquete, importando este, ejecutando el siguiente comando:

python -c 'from tostadas_pipo.utilidades.impuestos import impuesto_iva14; print "Función importada " + impuesto_iva14.__doc__[1:36] + "."'
Función importada Calcula el impuesto del IVA de 14 %.

El comando previo muestra la docstring de la función importada impuesto_iva14 sino muestra ningún mensaje de error, el paquete tostadas_pipo-0.1 se instalo correctamente.

 

Eliminar paquete

Para eliminar paquete usando la herramienta pip, ejecute el siguiente comando:

pip uninstall tostadas_pipo
Uninstalling tostadas-pipo-0.1:
  /home/leonardo/.local/bin/tostadas_pipo
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/DESCRIPTION.rst
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/INSTALLER
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/METADATA
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/RECORD
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/WHEEL
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/metadata.json
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo-0.1.dist-info/top_level.txt
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/__init__.py
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/__init__.pyc
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/principal.py
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/principal.pyc
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/utilidades/__init__.py
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/utilidades/__init__.pyc
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/utilidades/calculos.py
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/utilidades/calculos.pyc
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/utilidades/impuestos.py
  /home/leonardo/.local/lib/python2.7/site-packages/tostadas_pipo/utilidades/impuestos.pyc
Proceed (y/n)? y
  Successfully uninstalled tostadas-pipo-0.1

pip esta habilitado a desinstalar la mayoría de paquetes instalados. Las excepciones conocidas son:

  • Los paquetes basado en solamente en el módulo distutils los cuales fueron instalados sin la herramienta pip usando el comando python setup.py install desde el código del paquete.

    Instalándolo de esta forma, al momento de desintalarlo usando el comando pip uninstall tostadas_pipo este comando removerá solo la metadata, no detrás dejando de la instalación metadata para determinar que archivos fueron instalados.

    Entonces para solventar este problema tiene que ir manualmente al directorio site-packages a eliminar manualmente el paquete que instalo.

    Advertencia

    Esta entrando a la cueva de los Dragones!!!

  • Los scripts wrappers instalados ejecutando el comando python setup.py develop.

De esta forma ya tiene eliminado su paquete de forma manual de su sistema.

Paquetes Python

Los paquetes pueden contener módulos y otros paquetes. Son directorios. El único requisito es que contengan un archivo llamado __init__.py. Este archivo puede estar vacío.

Sentencia from

La sentencia from se utiliza en conjunto a la previa sentencia import para importar un módulo.

>>> from utilidades import suma_total

Por ejemplo, cree un directorio llamado tostadas_pipo, que contiene los archivos llamados __init__.pyprincipal.py (dentro del mismo directorio).

  • Archivo __init__.py, este archivo no tiene ningún contenido.
  • Archivo principal.py incluye el siguiente código:
from utilidades import impuestos
from utilidades import calculos

monto = int(input("Introduzca un monto entero: "))
# Llama función definida en el módulo "impuestos"
print "El impuesto IVA de 12%:", impuestos.impuesto_iva12(monto)

suma = int(input("Introduzca un monto entero a sumar: "))
# Llama función definida en el módulo "calculos"
print "La suma total es:", calculos.suma_total(suma)

Seguidamente dentro del directorio tostadas_pipo, cree otro directorio llamado utilidades, dentro de este, cree los siguientes archivos:

  • Archivo __init__.py, este archivo no tiene ningún contenido.
  • Archivo calculos.py incluye el siguiente código:
""" Módulo para cálculos diversos """

def suma_total(monto=0):
    """ Calcula la suma total """
    calculo_suma = 20
    calculo_suma += monto
    return calculo_suma
  • Archivo impuestos.py incluye el siguiente código:
""" Módulo para cálculos de diversos impuestos """

def impuesto_iva12(monto=0):
    """ Calcula el impuesto del IVA de 12 % """
    total = ((monto * 12)/100)
    return total


def impuesto_iva14(monto=0):
    """ Calcula el impuesto del IVA de 14 % """
    total = ((monto * 14)/100)
    return total

Al final tendrá la siguiente estructura del directorios del paquete Python llamado tostadas_pipo, como se describe a continuación:

tostadas_pipo/
├── __init__.py
├── principal.py
└── utilidades/
    ├── calculos.py
    ├── impuestos.py
    └── __init__.py

Entonces realizar importaciones desde una estructura de directorios mas completa se realiza de las siguientes formas:

  • Importar todos los módulo el sub-paquete utilidades, ejecutando:
import tostadas_pipo.utilidades
from tostadas_pipo import utilidades
from tostadas_pipo.utilidades import *
  • Importar el módulo calculos.py desde el sub-paquete utilidades, ejecutando:
from tostadas_pipo.utilidades import calculos
  • Importar la función impuesto_iva14() desde el módulo impuestos.py en el sub-paquete utilidades, ejecutando:
from tostadas_pipo.utilidades.impuestos import impuesto_iva14

Por ejemplo, cree un módulo llamado calculo_factura_pipo.py, que contiene las importaciones del paquete tostadas_pipo:

  • Archivo calculo_factura_pipo.py incluye el siguiente código:
from tostadas_pipo.utilidades import calculos
from tostadas_pipo.utilidades.impuestos import impuesto_iva14

monto = int(input("Introduzca un monto entero: "))
monto_suma = int(input("Introduzca un monto entero a sumar: "))

suma = impuesto_iva14(monto) + calculos.suma_total(monto_suma)

print "Total a Facturar: {0} BsS, con IVA 14%.".format(suma)

Módulos en Python

Módulos Python

Un módulo le permite a usted organizar lógicamente su código Python. Agrupando código relacionado dentro de un módulo hace el código mas fácil de entender y usar. Un módulo es un objeto de Python con atributos con nombres arbitrarios que puede enlazar y hacer referencia.

Simplemente, un módulo es no es otra cosa sino un archivo con extensión .py. Un módulo puede definir funciones, clases y variables, también puede incluir código ejecutable.

El código Python para un módulo nombrado funciones normalmente reside un archivo llamado utilidades.py. A continuación un ejemplo de un simple módulo llamado utilidades.py:

""" Módulo para cálculos diversos """

def suma_total(monto=0):
    """ Calcula la suma total """
    calculo_suma = 20
    calculo_suma += monto
    return calculo_suma

Sentencia import

La sentencia import se utiliza para importar un módulo. Usted puede usar cualquier archivo de código Python como un módulo ejecutando esta sentencia en otro archivo de código Python. La sentencia import tiene la siguiente sintaxis:

>>> import os
>>> import re, datetime

Cuando el interprete encuentra una sentencia import, este importa el módulo si el mismo esta presente en la ruta de búsqueda. Una ruta de búsqueda es una lista de directorios que el interprete busca antes de importar un módulo.

Por ejemplo, al importar el módulo utilidades.py, usted necesita colocar la siguiente sentencia al tope del otro script Python. A continuación un ejemplo de un simple módulo, calculo_factura_pipo.py.

# Importar el modulo llamado "utilidades"
import utilidades

print "Importo el modulo '{0}'\n".format(
    utilidades.__file__.replace(
    	utilidades.__file__, "utilidades.pyc"))

print u"Función '{0}' del módulo '{1}' llamado y mostró:".format(
    utilidades.suma_total.__name__,
    utilidades.__file__.replace(
    	utilidades.__file__, "utilidades.pyc"))

# Usted puede llamar una función definida dentro del módulo
print "Monto total a facturar: {0} BsS.".format(
	utilidades.suma_total(int(input("Ingrese un monto: ")))) 

Cuando el código anterior es ejecutado, ese produce el siguiente resultado:

Importo el modulo 'utilidades.pyc'

Función 'suma_total' del módulo 'utilidades.pyc' llamado y mostró:
Ingrese un monto: 56987
Monto total a facturar: 57007 BsS.

Un módulo se carga solo una vez, independientemente de la cantidad de veces que se importe. Esto evita que la ejecución del módulo ocurra una y otra vez si se producen múltiples importaciones.

La primera vez que un módulo es importado en un script de Python, se ejecuta su código una vez. Si otro módulo importa el mismo módulo este no se cargará nuevamente; los módulos son inicializados una sola vez.

Esto se debe al código objeto compilado que genera en el mismo directorio del módulo que cargo con la extensión de archivo .pyc, ejecutando las siguientes sentencias:

>>> import funciones, os
>>> archivos = os.listdir(os.path.abspath(
...     funciones.__file__).replace("/utilidades.pyc", "/"))
>>> print filter(lambda x: x.startswith('funciones.'), archivos)
['utilidades.py', 'utilidades.pyc']

De esta forma se comprueba que existe el archivo compilado de Python junto con el mismo módulo Python.

Localizando módulos

Cuando usted importa un módulo, el interprete Python busca por el módulo en la secuencia siguiente:

  1. El directorio actual.
  2. Si el módulo no es encontrado, Python entonces busca en cada directorio en la variable de entorno PYTHONPATH del sistema operativo.
  3. Si todas las anteriores fallan, Python busca la ruta predeterminada. En UNIX, la ruta predeterminada normalmente esta /usr/local/lib/python/.

El ruta de búsqueda de módulo es almacenado en el módulo de system sys como la variable sys.path. La variable sys.path contiene el directorio actual, PYTHONPATH, y las predeterminadas dependencia de instalación.

PYTHONPATH

Es una variable de entorno del sistema operativo, consistiendo de una lista de directorios. La sintaxis de PYTHONPATH es la misma como la del shell de la variable PATH.

Así es una típica definición de PYTHONPATH desde un sistema Windows, ejecutando:

set PYTHONPATH = C:\python20\lib;

Así es una típica definición de PYTHONPATH desde un sistema UNIX, ejecutando:

set PYTHONPATH = /usr/local/lib/python

Espacios de nombres y alcance

Las variables son nombres (identificadores) que se asignan a objetos.

Un espacio de nombres o namespace, es un diccionario de nombres de variables (claves) y sus objetos (valores) correspondientes.

Una sentencia de Python puede acceder a las variables en un espacio de nombres local y en el espacio de nombres global. Si una variable local y una variable global tienen el mismo nombre, la variable local sombrea la variable global.

Cada función tiene su propio espacio de nombres local. Los métodos de Clase siguen la misma regla de alcance que las funciones ordinarias.

Python hace conjeturas educadas sobre si las variables son locales o globales. Se supone que cualquier variable asignada a un valor en una función es local.

Por lo tanto, para asignar un valor a una variable global dentro de una función, primero debe usar la sentencia global.

>>> global nombre
>>> nombre
'Leonardo'

La sintaxis global nombre, le dice al interprete Python que la variable nombre es una variable global. Python deja de buscar la variable en el espacio de nombres local.

Por ejemplo, defina una variable Money en el espacio de nombres global. Dentro de la función Money, asigna un valor a Money, por lo tanto, Python asume que Money es una variable local. Sin embargo, accede al valor de la variable local Money antes de configurarlo, por lo que el resultado es una excepción UnboundLocalError. Si descomenta la sentencia global, se soluciona el problema.

Manipulación de archivos en Python

Manipulación de archivos

Para escribir o leer cadenas de caracteres para/desde archivos (otros tipos deben ser convertidas a cadenas de caracteres). Para esto Python incorpora un tipo integrado llamado file, el cual es manipulado mediante un objeto archivo el cual fue generado a través de una función integrada en Python, a continuación se describen los procesos típicos y sus referencias a funciones propias del lenguaje:

Abrir archivo

La forma preferida para abrir un archivo es usando la función integrada open().

Leer archivo

La forma preferida para leer un archivo es usando algunas de los métodos del tipo objeto file como read()readline() y readlines().

Escribir archivo

La forma preferida para escribir un archivo es usando el método del tipo objeto file llamado write().

Cerrar archivo

La forma preferida para cerrar un archivo es usando el método del tipo objeto file llamado close().

Archivos con modulo os

El módulo os de Python le permite a usted realizar operaciones dependiente del Sistema Operativo como crear una carpeta, listar contenidos de una carpeta, conocer acerca de un proceso, finalizar un proceso, etc. Este módulo tiene métodos para ver variables de entornos del Sistema Operativo con las cuales Python esta trabajando en mucho más. Aquí la documentación Python para el módulo os.

A continuación algunos útiles métodos del módulo os que pueden ayudar a manipular archivos y carpeta en su programa Python:

Crear una nueva carpeta

>>> import os
>>> os.makedirs("Ana_Poleo")

Listar el contenidos de una carpeta

>>> import os
>>> os.listdir("./")
['Ana_Poleo']

Mostrar el actual directorio de trabajo

>>> import os
>>> os.getcwd()
'/home/usuario/python/'

Mostrar el tamaño del archivo en bytes del archivo pasado en parámetro

>>> import os
>>> os.path.getsize("Ana_Poleo")
4096

¿Es un archivo el parámetro pasado?

>>> import os
>>> os.path.isfile("Ana_Poleo")
False

¿Es una carpeta el parámetro pasado?

>>> import os
>>> os.path.isdir("Ana_Poleo")
True

Cambiar directorio/carpeta

>>> import os
>>> os.chdir("Ana_Poleo")
>>> os.getcwd()
'/home/usuario/python/Ana_Poleo'
>>> os.listdir("./")
[]
>>> os.chdir("../")
>>> os.getcwd()
'/home/usuario/python'

Renombrar un archivo

>>> import os
>>> os.rename("Ana_Poleo","Ana_Carolina")
>>> os.listdir("./")
['Ana_Carolina']

Eliminar un archivo

>>> import os
>>> os.chdir("Ana_Carolina")
>>> archivo = open(os.getcwd()+'/datos.txt', 'w')
>>> archivo.write("Se Feliz!")
>>> archivo.close()
>>> os.getcwd()
'/home/usuario/python/Ana_Carolina'
>>> os.listdir("./")
['datos.txt']
>>> os.remove(os.getcwd()+"/datos.txt")
>>> os.listdir("./")
[]

Eliminar una carpeta

>>> os.rmdir("Ana_Carolina")
>>> os.chdir("Ana_Carolina")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 2] No such file or directory: 'Ana_Carolina'

Lanza una excepción OSError cuando intenta acceder al directorio que previamente elimino y este no encuentra.

Ejemplos de archivos

A continuación, se presentan algunos ejemplos del uso del tipo objeto file:

Ejemplo de iterar un archivo para leerlo

Usted puede iterar sobre un archivo como se muestra a continuación:

>>> archivo = open('datos.txt', 'r')
>>> for linea in archivo:
...     print linea
...
Este es una prueba

y otra prueba
>>> archivo.close()

Ejemplo de iterar un archivo con escritura y lectura

Usted puede manipular un archivo con permisos de escritura y lectura, ademas de interactuar de el mismo como se muestra a continuación:

import os

print "\nCrear un archivo"
print "================"

NOMBRE_ARCHIVO = 'datos.txt'

archivo = open(NOMBRE_ARCHIVO, 'w') # abre el archivo datos.txt
archivo.write('Este es una prueba \ny otra prueba.')
archivo.close()

if NOMBRE_ARCHIVO in os.listdir("."):
    print "\nArchivo creado en la ruta: \n\n\t{0}/{1}".format(
        os.getcwd(), NOMBRE_ARCHIVO)
else:
    print "El archivo no fue creado!!!\n"


print "\n\nLeer un archivo"
print "===============\n"

archivo = open(NOMBRE_ARCHIVO, 'r')
contenido = archivo.read()
print contenido
archivo.close()


print "\n\nIterar sobre un archivo"
print "=======================\n"

archivo = open(NOMBRE_ARCHIVO, 'r')
for linea in archivo:
    print linea
print "\n"
archivo.close()


print "\nEliminar un archivo"
print "==================="

os.remove(os.getcwd()+"/"+NOMBRE_ARCHIVO)
print "\nEliminado archivo desde la ruta: \n\n\t{0}/{1}".format(
    os.getcwd(), NOMBRE_ARCHIVO)

Entrada/Salida en Python

Entrada/Salida en Python

Los programas serían de muy poca utilidad si no fueran capaces de interaccionar con el usuario.

 

Entrada estándar

Para pedir información al usuario, debe utilizar las funciones integradas en el interprete del lenguaje, así como los argumentos de línea de comandos.

Ejemplo de la función raw_input:

La función raw_input() siempre devuelve un valor de cadenas de caracteres:

>>> nombre = raw_input('Ana: ¿Cómo se llama usted?: ')
Ana: ¿Cómo se llama usted?: Leonardo
>>> print nombre
Leonardo

Ejemplo de la función input:

La función input() siempre devuelve un valor numérico:

>>> edad = input('Ana: ¿Que edad tiene usted?: ')
Ana: ¿Que edad tiene usted?: 38
>>> print edad
38

 

Entrada por script

En muchas practicas de este entrenamiento usted lo que ha hecho ha sido escribir código en el intérprete, y/o escribir/ejecutar pequeños programas Python, pero los programas informáticos no funcionan así.

Se basan en escribir todas las instrucciones en archivos llamados scripts, que no es mas que guiones de instrucciones. Luego se envía este archivo al intérprete como parámetro desde la terminal de comando (si es un lenguaje interpretado como Python) y éste ejecutará todas las instrucciones en bloque.

A parte de ser la base del funcionamiento de los programas, la característica de los scripts es que pueden recibir datos desde la propia terminal de comando en el momento de la ejecución, algo muy útil para agregar dinamismo los scripts a través de parámetros personalizables.

A continuación, un ejemplo el cual simula a sala de chat del servicio LatinChat.com, validando datos de entradas numérico y tipo cadena de caracteres e interactuá con el usuario y en base a condicionales muestra un mensaje.

print "\nSimulando a LatinChat"
print "====================="

print "\nSala de Chat > De 30 a 40 años"
print "------------------------------\n"

print 'Ana: ¿Cómo se llama usted?: ' 
nombre = raw_input('Yo: ')
print 'Ana: Hola', nombre, ', encantada de conocerte :3'

print 'Ana: ¿Que edad tiene usted?: '
edad = input('Yo: ')
print 'Usted tiene', edad, ', y yo ya no digo mi edad xD'

print 'Ana: ¿Tiene WebCam?, ingrese "si" o "no", por favor!: '
tiene_WebCam = raw_input('Yo: ')

if tiene_WebCam in ('s', 'S', 'si', 'Si', 'SI'):
	print "Ponga la WebCam para verle :-D"
elif tiene_WebCam in ('n', 'no', 'No', 'NO'):
	print "Lastima por usted :'( Adiós"

Truco

LatinChat.com, fue un servicio de Internet que ofrecía diversas salas de chat, muy popular en la década de los 90 en latinoamericana.

 

Scripts con argumentos

Para poder enviar información a un script y manejarla, tenemos que utilizar la librería de sistema sys. En ella encontraremos la lista argv que almacena los argumentos enviados al script.

Usted debe crear un script llamado entrada_argumentos.py con el siguiente contenido:

import sys

print sys.argv

Ejecuta el script llamado entrada_argumentos.py, de la siguiente forma:

python entrada_argumentos.py
['entrada_argumentos.py']

Al ejecutarlo puede ver que devuelve una lista con una cadena que contiene el nombre del script. Entonces, el primer argumento de la lista sys.argv (es decir, sys.argv[0]) es el propio nombre del script.

Ahora si intenta ejecutar el script de nuevo pasando algunos valores como números y cadenas de caracteres entre comillas dobles, todo separado por espacios:

python entrada_argumentos.py 300 43.234 "Hola Plone"
['entrada_argumentos.py', '300', '43.234', 'Hola Plone']

Cada valor que enviamos al script durante la llamada se llama argumento e implica una forma de entrada de datos alternativa sin usar las funciones input() y raw_input().

A continuación, un ejemplo el cual usa un script con la librería sys. El script recibe dos (02) argumentos: una cadena de caracteres y un número entero. Lo que hace es imprimir la cadena de caracteres tantas veces como le indique con el argumento de tipo número:

import sys

# Comprobación de seguridad, ejecutar sólo si se reciben 2 
# argumentos realemente
if len(sys.argv) == 3:
    texto = sys.argv[1]
    repeticiones = int(sys.argv[2])
    for r in range(repeticiones):
        print texto
else:
    print "ERROR: Introdujo uno (1) o más de dos (2) argumentos"
    print "SOLUCIÓN: Introduce los argumentos correctamente"
    print 'Ejemplo: entrada_dos_argumentos.py "Texto" 5'

Si quiere comprobar la validación de cuantos argumentos deben enviarme al script, ejecute el siguiente comando:

python entrada_dos_argumentos.py "Hola Plone"
ERROR: Introdujo uno (1) o más de dos (2) argumentos
SOLUCIÓN: Introduce los argumentos correctamente
Ejemplo: entrada_dos_argumentos.py "Texto" 5

Ahora si intenta ejecutar el script entrada_dos_argumentos.py con solo dos (2) argumentos, ejecutando el siguiente comando:

python entrada_dos_argumentos.py "Hola Plone" 3
Hola Plone
Hola Plone
Hola Plone

 

Salida estándar

La forma general de mostrar información por pantalla es mediante una consola de comando, generalmente podemos mostrar texto y variables separándolos con comas, para este se usa la sentencia print.

 

Sentencia print

Sentencia print evalúa cada expresión, devuelve y escribe el objeto resultado a la salida estándar de la consola de comando. Si un objeto no es un tipo cadena de caracteres, ese es primeramente convertido a un tipo cadena de caracteres usando las reglas para las conversiones del tipo. La cadena de caracteres (resultado o original) es entonces escrito.

Entonces para mostrar mensajes en pantalla, se utiliza el uso de la sentencia print.

Ejemplo del uso de la sentencia print:

>>> print 'Ana: Hola', nombre, ', encantada de conocerte :3'
Ana: Hola Leonardo , encantado de conocerte :3

 

Formato de impresión de cadenas

En la sentencia print se pueden usar el formato de impresión alternando las cadenas de caracteres y variables:

>>> tipo_calculo = "raíz cuadrada de dos"
>>> valor = 2**0.5
>>> print "el resultado de", tipo_calculo, "es:", valor
el resultado de raíz cuadrada de dos es: 1.41421356237