Cambios de Python 1.5 a 2.4

Cambios de Python 1.5 a 2.4

Nota: Este documento está basado en los distintos documentos que han ido surgiendo
detallando los cambios de cada versión de Python. Tanto la traducción como la adaptación
son bastante libres. Los autores de los textos originales son Moshe Zadka, Andrew Kuchling

Cambios menores

  • Print ahora puede tener su salida redirigida a un objeto de tipo archivo
    con la siguiente síntaxis:

    print >> sys.stderr, "Esto es un error muy chungo"
    

    Esto mandaría esa cadena al error estándar en lugar de a la salida
    estándar.

    print >> miarchivo, "Esto no sale por pantalla; se escribe en un
    archivo"
    

    Escribe esa cadena en el objeto de tipo archivo miarchivo (que debe
    estar abierto para escritura, obviamente).

  • Se añade el tipo de datos Decimal que se declara como d = Decimal(3.343434321) para decimales de precisión arbitraria sin uso de números de coma flotante.
  • Los métodos de función de cadena de ajuste de linea ljust(), rjust() y center() toman un segundo parámetro adicional con el que podemos especificar el caracter de relleno si no queremos que sea el espacio.
  • Las cadenas tienen una función rsplit() que funciona igual que split() pero empezando desde el final. Esto es útil cuando sólo queremos coger un número limitado de tokens.
  • El intérprete gana el parámetro -m que ejecuta el módulo que se le diga y encuentre en sys.path como un script.
  • "None" es una constante y no se le pueden hacer asignaciones.
  • Expresiones condicionales: son similares a las a == 1 ? b = 1 : b = 0; pero con una sintaxis más sencilla: b = 1 if a == 1 else b = 2
  • Pueden importarse varios símbolos de un mismo módulo usando una sintaxis más elegante que la de romper cada linea con \:

    from SimpleXMLRPCServer import (SimpleXMLRPCServer,
                                    SimpleXMLRPCRequestHandler,
                                    CGIXMLRPCRequestHandler,
                                    resolve_dotted_attribute)
    

  • (Nuevo en 2.4) Existe una función reversed() que toma una secuencia y proporciona un interador que la
    recorre invertida (equivalente a invertirlo sin modificarlo) de modo que si queremos recorrer inversamente una lista llamada "miLista" podemos escribir:
    for i in reversed(miLista): print t
    
  • Los módulos pueden ser renombrados al importarse con la síntaxis:

      import string as nomeusesquetoyobsoleto.
  • Se añade el indicador de formato %r que insertará el repr() de su
    argumento. Es simétrico al %s que inserta el str() de su argumento.

  • Puede sobrecargarse el operador 'in' mediante el método de clase
    __contains__ de modo que podemos personalizar el modo en que nuestra
    clase responda a una sentencia del tipo for objeto in secuencia.

  • Las comparaciones pueden lanzar excepciones. Antes las excepciones que
    se hubieran podido lanzar en el método __cmp__ de una clase se
    silenciaban. Ya no.

  • Nuevas excepciones: UnboundLocalError (utilizamos una variable no
    definida), TabError y IndentationError (errores de indentación).

  • Las funciones 'int()' y 'long()' ahora aceptar un argumento opcional
    'base'. Este argumento sólo funciona si el primer parámetro es una
    cadena (numérica, como '1234') y provoca que el número representado
    por la cadena sea convertida a la base indicada:

      int('123',10) -> 123
      int('123',16) -> 291
    

    Además, como veremos más adelante, los ints ahora se convierten
    automaticamente a longs cuando es necesario sin provocar excepciones
    de desbordamiento.

  • Los diccionarios tienen un nuevo método 'setdefault(clave, valor)' que
    se comporta de forma similar el método existente get() pero que en el
    caso de que no exista la clave devuelve el valor indicado por 'valor'.
    Con esto puede reducirse:

      if dict.has_key(key): return dict[key]
      else:
        dict[key] = pordefecto
        return dict[key]
    

    a:

      return dict.setdefault(key, pordefecto)
    
  • El método append() de las listas sólo acepta un parámetro aunque este
    puede ser una tupla u otra estructura de datos compuesta.

  • El método connect() y bind() de los sockets sólo admiten un parámetro,
    que normalmente va a ser una tupla (host, puerto).


    Cambios en el modelo de clases y objetos [Avanzado]

    Este es sin duda el mayor cambio que ha experimentado Python en este
    periodo. Los cambios (de momento) son compatibles hacia atrás asi que la
    mayoría de los programadores no deberían tener problemas en mantener sus
    programas bajo el modelo antiguo, pero sería recomendable cambiar con el
    tiempo (por ejemplo, cuando estemos casi seguros de que la inmensa
    mayoría de la gente esta utilizando, al menos, Python2.2).

    En resumen:

    • Puede heredarse cualquier tipo de dato como listas y enteros, y estas
      clases heredadas deberían funcionar en cualquier sitio que requiera
      una clase del tipo que se hereda.

    • Ahora es posible definir métodos estáticos y de clase, además de los
      métodos de instancia que ya estaban disponibles en versiones
      enteriores de Python.

    • La lista de atributos legales de una instancia puede limitarse a un
      conjunto particular utilizando 'slots'. Esto hace posible protegerse
      contra errores tipográficos (objeto.contadorz += 1) y quizás facilitar
      la tarea de realizar optimizaciones en versiones futuras de Python.

    • Es posible llamar automáticamente a métodos cuando se accedan o
      escriban atributos de instancia utilizando un nuevo mecanismo llamado
      'propiedades' (properties). Muchos usos de __getattr__ pueden
      reescribirse para que en su lugar utilicen propiedades, haciendo que
      el código resultante sea más pequeño y rápido. Derivado de esto, ahora
      los atributos también pueden tener cadenas de documentación.

    Viejas y nuevas clases

    Python 2.2 tiene dos tipos de clases, las de estilo viejo y las de
    estilo nuevo conviviendo. Las de estilo viejo siguen exactamente el
    mismo modelo que cualquier programador de cualquier versión anterior
    debería conocer. Todas las características que se explicarán a
    continuación se aplican sólo a las clases de estilo nuevo. Eventualmente
    se dejarán de soportar las clases de estilo viejo, probablemente en
    Python 3.0.

    ¿Cómo se define una clase de estilo nuevo?

    Se hace heredando de una clase existente. La mayoría de los tipos
    internos de Python, como enteros, listas, diccionarios, e incluso
    archivos son ahora clases de estilo nuevo. Hay además una clase de
    estilo nuevo llamada 'object' que se convierte en la clase base para
    todos los tipos internos, de modo que si no queremos heredar de un nuevo
    tipo interno se puede heredar de este:

    class MiClase(object):
        def __init__(self):
            ...
    

    De esto se deduce que todas las clases que no hereden de ninguna serán
    de estilo viejo.

    Ahora las funciones internas 'int()', 'float()' y 'str()' ya no son
    funciones sino objetos clases que se comportan como fábricas cuando se
    las llama.

    >>> int
    <type 'int'>
    >>> int('123')
    123
    

    Para añadir simetría, se han añadido las 'fábricas' para el resto de
    tipos como 'dict' y 'file'. Por ejemplo, para añadir un método lock a
    una clase heredada de los objetos fichero (file) tán sólo tendríamos que
    hacer:

    class FicheroLockable(file):
        def lock(self, operation, length=0, start=0, whence=0):
            import fcntl
            return fcntl.lockf(self.fileno(), operation, length, start, whence)
    
    

    Descriptores

    En versiones anteriores no existía una forma consistente de descubrir
    que atributos y métodos tenía un objeto. Existían algunas convenciones
    informales como definir __members__ y __methods__ que eran listas de
    nombres, pero en muchas ocasiones los autores de una clase añadida no se
    preocupaban por definirlos. Se podía inspeccionar el __dict__ de un
    objeto pero en algunas ocasiones esto también podía ser poco apropiado.

    Los descriptores especifican el valor de un atributo, empezando por si
    es un método o un campo. Con el API para descriptores, pueden hacerse
    los métodos de clase y los métodos estáticos además de contrucciones más
    exóticas.

    Los descriptores de atributos son objetos que viven dentro de los
    objetos de clase y tienen unos pocos atributos por si mismos:

    • __name__ es el nombre del atributo
    • __doc__ el la cadena de documentación del atributo
    • __get__(objeto) es el método que devuelve el valor del atributo para
    • 'objeto'
    • __set__(objeto,valor) pone el valor del atributo en 'objeto' a 'valor'.
    • __delete__(objeto, valor) borra el valor del atributo de 'objeto'.

    Por ejemplo, cuando se escribe obj.x, los pasos que Python realiza
    realmente son:

    descriptor = obj.__class__.x
    descriptor.__get__(obj)
    

    Para los métodos get devuelve un objeto temporal al que se le puede
    llamar, y agrupa la instancia y el método en ese objeto.

    Métodos estáticos y de clase

    Se definen de forma similar al siguiente ejemplo:

    class Miclase(object):
    
        @staticmethod
        def f(arg1,arg2):
            ...
    
        @classmethod
        def g(cls,arg1,arg2):
            ...
    
    

    A los métodos estáticos no se les pasa la instancia de la clase, por lo
    que son muy parecidos a las funciones normales (aunque se sigue
    accediendo a ellos con la notación Miclase.f()). Son similares a los
    métodos estáticos de C++. A los métodos de clase se les pasa como primer
    argumento la clase, pero no la instancia. Por decirlo de alguna forma,
    son como métodos de 'la fábrica' más que de los objetos fabricados.

    Decoradores

    En el apartado anterior hemos visto que se ha usado una arroba antes de una palabra clave
    para especificar que el método de la siguiente linea era de un tipo especial. Estos símbolos
    antepuestos de una arroba se llaman "Decoradores" y no son más que funciones que toman como parámetro
    una función, realizan operaciones sobre la misma y devuelven otra función (o la misma, modificada).

    Por ejemplo en el apartado anterior el:

    @staticmethod
    def f(arg1, arg2):
      ...
    

    es equivalente a:

    def f(arg1, arg2):
      ...
    
    f = staticmethod(f)
    

    Podemos definir nuestros propios decoradores:

    def miDecorador(funcion_a_decorar):
     func.propiedad = "decorada"
     return func
    
    @deco 
    def funcion_decorable(): pass
    
    # f.attr vale "decorada"
    

    super()

    super() proporciona una forma nueva de acceder a cualquier de las
    superclases de una clase, y es la forma recomendada de acceder a ellas.
    El uso es sencillo:

    super(superclase, self).metodo()
    

    o:

    super(superclase, self).variable
    
    

    Acceso a atributos

    Con versiones anteriores de Python se suele utilizar __getattr__ para
    definir 'ganchos' que se ejecuten cuando se acceda a un atributo. Por
    ejemplo, muchas clases utilizan __getattr__ para definir que método se
    utilizará al acceder a determinada variable (por ejemplo, para convertir
    obj.cosa en una llamada al método get_cosa() de forma transparente al
    usuario de la clase).

    Ahora se han añadido nuevas formas de controlar el acceso a atributos,
    aunque __getattr__ se mantiene intacto.

    __getattribute__: Si __getattr__ se utilizaba cuando se accede a un
    atributo que no existe, __getattribute__ se utiliza siempre que se
    accede a un atributo.

    'Propiedades': Las propiedades (properties) son un nuevo tipo de datos
    interno que empaqueta tres funciones para leer, escribir y borrar un
    atributo, respectivamente, y una cadena de documentación. Es como llevar
    al nivel del lenguaje lo que siempre se ha hecho en programación
    orientada a objetos, es decir, los métodos de acceso 'get' y 'set' para
    las variables de instancia.
    El uso es bien sencillo, y se ve mejor con un ejemplo:

    class C_nueva(object):
        def get_tamanio(self):
            print 'Retornamos el valor de tamanio!'
            return self.tamanio
        def set_tamanio(self,tamanio):
            ... calcular algo en relación con el parámetro tamanio ...
            ... y poner el estado interno
            print 'Escribimos el valor de tamanio!'
        tamanio = properties(get_tamanio, set_tamanio, None, "Cadena de docuemntacion")
    
    

    El equivalente en una clase 'vieja' sería:

    class C_vieja:
        def __init__(self):
            self.tamanio = 0
        def get_tamanio(self):
            print 'Retornamos el valor de tamanio!'
            return self.tamanio
        def set_tamanio(self,tamanio):
            ... bla bla bla..
            print 'Escribimos el valor de tamanio!'
    

    Ahora examinemos la misma secuencia de comandos para las dos clases y su
    resultado:

    cv = C_vieja();   # No imprime nada, pone tamanio a 0
    cv.tamanio = 5    # No imprime nada, pone tamanio a 5
    tmp = cv.tamanio  # No imprime nada, asigna tamanio a tmp
    cv.set_tamanio(5) # Imprime 'Escribimos...' y pone tamanio a 5
    cv.get_tamanio()  # Imprime 'Retornamos...' y devuelve 5
    

    Con la clase nueva sería:

    cv = C_nueva();
    cv.tamanio = 5    # Escribe 'Escribimos...' y pone tamanio a 5
    tmp = cv.tamanio  # Escribe 'Retornamos...' y asigna tamanio a tmp
    cv.set_tamanio(5) # Escribe 'Escribimos...' y pone tamanio a 5
    cv.get_tamanio(5) # Escribe 'Retornames...' y devuelve 5
    

    Como se puede observar, la diferencia en usar properties en lugar de
    atributos 'normales' es que con las properties las funciones registradas
    para leer, escribir y borrar el atributo son llamadas automáticamente
    aunque accedamos directamente al nombre de la variable. Esto permite
    hacer nuestro código mucho más robusto, añadiendo las comprobaciones
    necesarias a la hora de poner o escribir el valor del atributo (o hacer
    lo que queramos, no estamos limitados a leer y escribir el valor, aunque
    será lo que normalmente se haga), y queda un código más claro (menos
    inundado de llamadas a métodos get y set) y legible.


    Otros cambios

    Conjuntos (nuevo en la versión 2.3, ampliado en la 2.4)

    Los conjuntos son un nuevo tipo de datos, mutables e internos al lenguaje (desde la 2.4)sobre el que, además de las operaciones de añadir y eliminar
    elementos, se pueden realizar las operaciones algebraicas de resta, or exclusivo, unión e intersección con otros conjuntos
    (mediante el signo "-", "^", "|","&", respectivamente). También permiten añadir elementos (método add() para uno y update() para varios) o eliminarlos (con remove.)

    Los conjuntos _no_ son tipos secuenciales (de hecho están
    construidos a partir de un diccionario). También soportan las operaciones de comprobación de
    sub (issubset()) o superconjunto (issuperset()).

    Existe un segundo tipo de conjunto declarado como "frozenset" que es inmutable.

    Ejemplo del documento de cambios a 2.4:

    >>> a = set('abracadabra')              # form a set from a string
    >>> 'z' in a                            # fast membership testing
    False
    >>> a                                   # unique letters in a
    set(['a', 'r', 'b', 'c', 'd'])
    >>> ''.join(a)                          # convert back into a string
    'arbcd'
    
    >>> b = set('alacazam')                 # form a second set
    >>> a - b                               # letters in a but not in b
    set(['r', 'd', 'b'])
    >>> a | b                               # letters in either a or b
    set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
    >>> a & b                               # letters in both a and b
    set(['a', 'c'])
    >>> a ^ b                               # letters in a or b but not both
    set(['r', 'd', 'b', 'm', 'z', 'l'])
    
    >>> a.add('z')                          # add a new element
    >>> a.update('wxy')                     # add multiple new elements
    >>> a
    set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'x', 'z'])       
    >>> a.remove('x')                       # take one element out
    >>> a
    set(['a', 'c', 'b', 'd', 'r', 'w', 'y', 'z'])
    

    Iteradores

    Los iteradores son objetos que tienen un método llamado next() que
    devuelve un elemento. Cada vez que se llame a next() un iterador debe
    devolver el siguiente elemento de la estructura de datos o objeto sobre la que
    itera hasta que ya no hay más elementos momento en el cual se lanza una
    excepción de tipo StopIteration. Los objetos iterados se crean mediante
    la nueva función interna iter(objeto) siendo el parámetro objeto el
    objeto sobre el que el iterador devuelto va a iterar.

    Ejemplo:

    L = [1,2,3] # Creamos una sencilla lista
    i = iter(L) # i es un objeto iterador que iterará sobre L
    i.next()    # Devuelve 1
    i.next()    # Devuelve 2
    i.next()    # Devuelve 3
    i.next()    # Lanza la siguiente excepción
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    StopIteration
    

    En general, son iterables cualquier colección (listas, tuplas, cadenas,
    diccionarios) y objetos que tengan el método __iter__ redefinido.

    iter() también puede otra forma con dos parámetros iter(C,centinela)
    siendo el primer parámetro un objeto invocable (una función, un método,
    o un objeto con el método __call__ redefinido) y centinela un valor que
    hará que se lance StopIteration cuando C devuelve ese valor, por
    ejemplo:

    fich = open('fichero', 'r')
    fich_it = iter(fich.readline, '')
    fich_it.next()
    ....
    

    Cada llamada a fich_it.next() leera una nueva línea hasta que se
    llegue al final del archivo (fich.readline devuelve '') momento en el
    que se lanzará la StopIteration.

    Otro ejemplo:

    i = iter( [1,2,3,None,4,5,6] )
    j = iter( i.next, None )
    

    Tendrá como salida:

    1
    2
    3
    

    Los iteradores tienen una serie de consecuencias internas:

    • Los bucles 'for' ya no esperan un objeto de tipo secuencia; ahora
      cualquier objeto iterable sirve (es decir, un objeto al cual
      aplicándole iter() devuelva un iterador). El for simplemente contruirá
      internamente el operador aplicando iter() sobre el objeto y se
      detendrá cuando se lance StopIteration. Por compatibilidad hacia
      atrás se construye un iterador automáticamente para todos los tipos de
      secuencia de modo que 'for i in [1,2,3]' siga funcionando.

      Como también podemos llamar a iter() con un diccionario, podemos
      hacer:

        for clave in dicc: print clave, dicc[clave]
      

      Para escribir todas las claves y valores de un diccionario. Pero
      además si se quiere iterar especificamente sobre algún componente del
      diccionario puede utilizarse:

      iterkeys(dicc): Devuelve un iterador que itera sobre las claves del
      diccionario argumento.

      itervalues(dicc): Devuelve un iterador que itera sobre los valores del
      diccionario argumento.

      iteritems(dicc): Devuelve un iterador que itera sobre los conjuntos
      clave/valor del diccionario argumento.

    • El operador 'in' ahora también funciona en los diccionarios, de modo
      que 'if clave in dict' es equivalente a 'if dict.has_key(clave)'

    • Los ficheros también proporcionan un iterador que llama al método
      readline() hasta que no hay más líneas en el fichero. Esto quiere
      decir que ahora podemos leer cada línea de un fichero haciendo algo
      como:

        for linea in fichero:
          ... hacer algo con cada línea ...
      
    • Los iteradores no se utilizan internamente sólo en los for, sino en
      cualquier expresión que recorra una secuencia, de modo que pueden
      hacer cosas como:

        L = [1,2,3]
        i = iter(L)
        a,b,c = i
        a,b,c
        # Escribirá:1,2,3
        

    Es importante destacar que sólo se puede ir hacia adelante con un
    iterador, no puede ni irse hacía atrás, reiniciar el operador o hacer
    una copia del mismo. Un objeto iterador creado podría proporcionar esas
    capacidades adicionales pero el protocolo iterador sólo requiere un
    método next().

    Generadores

    Los generadores son otra característica nueva, y depende de los
    iteradores. Un generador modifica sustancialmente la forma en la que una
    función o método trabaja.

    Normalmente se llama a una función, y en algún punto la función
    devuelve un valor o ninguno en absoluto. La siguiente vez que se llama a
    la función, se vuelve a ejecutar desde el principio de la misma forma y
    de nuevo devolverá (o no) un valor dependiendo de sus argumentos (o
    falta de ellos). Esto creo que lo sabemos todos.

    Bien, cuando en una función normal llegamos al 'return' las variables
    locales se destruyen y el valor resultante es devuelto al invocador. Una
    llamada posterior a la misma función tendrá un conjunto creado de nuevo
    de variables locales. Pero...

    ¿Qué pasaría si quisiéramos que las variables locales de la función no
    se destruyeran al final de cada llamada? ¿Qué pasaría si en la siguiente
    llamada quisiéramos continuar la ejecución de la función donde se quedo
    la última vez?

    Pues la respuesta a estas preguntas es: Utilizar generadores. Puede
    pensarse en los generadores (o funciones generadores) como en funciones
    continuables.

    Para convertir una función en una función generadora, tan sólo tenemos
    que utilizar la palabra clave yield en lugar de return. yield
    devuelve, al igual que return, el valor de la expresión que tenga a la
    derecha (o ninguno, si no tiene ninguno), pero la próxima vez que
    llamemos a la función la ejecución continuará con las mismas variables
    locales y a partir de donde se salió la última vez (es decir, a partir
    del último yield que provocará el retorno de la función generador).

    El ejemplo más sencillo es:

    def generar_enteros(N):
        for i in range(N):
            yield i
    

    ¿Qué hace exactamente esta función? Veámoslo continuando el ejemplo, y
    si hemos leido el apartado sobre iteradores lo entenderemos a la
    perfección:

    gen = generar_enteros(3)
    gen.next() # Devuelve 0
    gen.next() # Devuelve 1
    gen.next() # Devuelve 2
    gen.next() # Genera la excepción StopIteration
    

    También podría escribirse:

    for i in generar_enteros(3):
        ... etc ...
    

    ó:

    a,b,c,d = generar_enteros(4) # a=0 b=1 c=2 d=3
    

    Dentro de una función generadora, el 'return' sólo puede utilizarse sin
    ninguna expresión a la derecha y marca el final del ciclo de proceso;
    utilizar el return con alguna expresión es un error dentro de una
    función generador. El final del ciclo de proceso también puede marcarse
    lanzando StopIteration manualmente o simplemente dejando que el flujo de
    ejecución del generador llega al final de la función.

    Un ejemplo más elaborado de un generador que implementa el recorrido de
    un arbol en orden in-orden (primero todos los nodos de la izquierda y
    luego los de la derecha) de forma recursiva:

    def inorder(t):
        if t:
            for x in inorder(t,left):
                yield x
            yield x.label
            for x in inorder(t.right):
                yield x
    
    

    Como los generadores introducen una palabra clave nueva, si queremos
    utilizarlos desde Python 2.2 es necesario incluir al principio del archivo .py:

    from __future__ import generator
    

    Desde Python 2.3 no es necesario incluir este comando.

    La función enumerate() (nuevo en Python 2.3)

    enumerate(secuencia), siendo secuencia un tipo de datos secuencia (lista, tupla, cadena, etc) nos
    devolverá un iterador (ver más arriba) que en cada llamada nos irá devolviendo una tupla (posición, elemento)
    en la que 'posición' será la posición ordinal que 'elemento' ocupará dentro de la secuencia. Por ejemplo:

    lista = ['perro', 'gato', 'hamster']
    listaEnumGen = enumerate(lista)
    listaEnumGen.next()
    (0, 'perro')
    listaEnumGen.next()
    (1, 'gato')
    listaEnumGen.next()
    (2, 'hamster')
    

    Por supuesto el iterador creado también podrá utilizarse en un bucle 'for', por ejemplo para sustitúir:

    for posicion in range(len(Lista)):
        elemento = L[posicion]
        #...hacer algo con el elemento...
        L[posicion] = resultado
    

    Por:

    for posicion,elemento in enumerate(Lista):
        #... hacer algo con el elemento...
        L[posicion] = resultado
    

    Tipo Lógico o Booleano (nuevo en Python 2.3)

    Python 2.3 añade un nuevo tipo de datos interno a los ya existentes: El lógico o
    booleano. Este tipo sólo puede tener dos valores, que se incorporan como palabras clave
    al lenguaje, y son True o False (si teníamos código existente utilizando esas palabras para
    nombres de símbolo debemos cambiarlas si queremos que funcione correctamente en Python 2.3). Como
    seguramente ya haya adivinado el lector, True significa 'Cierto, Verdadero' y False 'Falso'. Estos
    tipos tienen un valor numérico que es '1' y '0' respectivamente, por compatibilidad.

    Unificación de enteros y enteros largos (completada en 2.4)

    Python 2.2 (y posteriores) convertirá los enteros en enteros largos
    automáticamente cuando sea necesario, por lo que ya no se necesita el
    sufijo 'L' para indicar el tipo. Muchas operaciones que antes lanzaban
    la excepción OverflowError ahora devolverán un entero largo como su
    resultado, por ejemplo:

    >>> 1234567890123 # Devuelve 1234567890123L
    >>> 2 ** 64       # Devuelve 18446744073709551616L
    
    

    Cambio del operador de división

    Actúalmente el operador de división / de Python (y en versiones
    anteriores) se comporta como el operador de división de C cuando se le
    utiliza con dos valores enteros: devuelve un resultado entero que trunca
    la parte fraccionaria. Por ejemplo 3/2 es 1, no 1.5. Esto tiene la
    consecuencia de que los resultados de una división pueden variar de
    forma inesperada dependiendo del tipo de los dos operandos (enteros o
    reales) y como Python es dinámicamente tipado, puede ser dificil
    determinar los tipos de los operarandos.

    Ahora se ha dividido la operación de división en dos operaciones, por un
    lado la 'división real' proporciona los resultados que la mayoría de los
    no-programadores esperarían, es decir, con parte fraccionaria si es
    necesario aunque los dos operadores sean enteros, y por otro lado está
    la 'división clásica' que es lo que el operador / actúal realiza.
    También está la 'división con redondeo' que devuelve un entero sin
    importar los tipos de los operandos (aunque seán flotantes).

    Python 2.2 introduce el operador // para la 'divisón con redondeo', y
    se cambia el
    operador / que a partir de ahora realizará la división real.

    Simétricamente las clases ahora pueden redefinir los métodos __truediv__
    para la división real y __floordiv__ para la división con redondeo,
    correspondientes a los operadores del mismo nombre.

    Para utilizar el nuevo operador / como división real es necesario
    incluir al principio de los archivos .py la sentencia:

    from __future__ import division
    

    En Python 2.3 no será necesario (el operador / actuará como división
    real por defecto).

    Si queremos comprobar si nuestros programas se pueden ver afectados por
    este cambio, podemos ejecutar el intérprete con el parámetro -Q warn que
    causará que se imprima una advertencia cada vez que se utilice una
    divisón con dos enteros, de modo que podamos arreglar el código afectado
    por este cambio.

    Cadenas Unicode

    Utilizan 16 bits por caracter en lugar de 8. Se crean anteponiendo una 'u'
    a las comillas (u"esto es una cadena unicode en python"). Puede obtenerse
    cualquier carácter unicode con \uHHHH siendo HHHH un número en hexadecimal
    de 0000 a FFFF.

    Tienen un método encode() que devuelve una cadena normal de 8 bits en la
    codificación deseada (como 'ascii', 'utf-8', 'iso-8859-1' u otras).
    Combinar cadenas de 8 bits y unicode fuerza la conversión a unicode.

    Hay funciones para tratarlas:

    • unichr(ch) devuelve una cadena unicode de 1 carácter de largo que
      contiene el carácter ch.

    • ord(u) devuelve un número entero correspondiente al carácter u.

    Todos los comandos internos que aceptaban cadenas normales ahora también
    aceptan cadenas unicode.

    Directivas __future__

    Esa directiva se utiliza cuando en una versión de Python se introducen
    cambios que podrían romper la compatibilidad de programas existentes.
    Por ello se ha añadido esta palabra clave que permite tener
    opcionalmente determinadas características que serán obligatorias en
    versiones siguientes.

    Esta directiva, __future__, se utiliza de la siguiente manera:

    from __future__ import característica
    

    Deben ponerse al principio de los módulos, antes de cualquier otro
    import o código.

    Comparaciones mejoradas

    Hasta ahora las clases que tenían un método __cmp__ para sobrecargar la
    comparación entre dos clases sólo tenían que preocuparse de devolver 0
    si las clases eran iguales o +1 o -1 si no lo eran. No podían lanzarse
    excepciones o devolver cualquier otro valor que no fuera booleano.

    Ahora las comparaciones se ha dividido en varios métodos para cada tipo,
    siendo estos y los nombres de los métodos correspondientes:

    • < __lt__
    • <= __le__
    • > __gt__
    • >= __ge__
    • == __eq__
    • != __ne__

    Además estos métodos no sólo pueden devolver un valor booleano sino
    cualquier cosa (listas, matrices, etc.)

    La función interna cmp() acepta también como tercer argumento opcional
    el tipo de comparación a realizar como una cadena (como '<' '>=', etc.)
    Si le le sigue llamando sin ese tercer argumento el funcionamiento es
    igual que el antiguo, devolviendo un valor booleano.

    Warnings

    Cuando se importa un módulo que va a dejar de desarrollarse por
    cualquier motivo en versiones futuras se producirán advertencias
    ('DeprecationWarning'). Podemos producir advertencias en nuestro código
    utilizando el módulo warning: warning.warning("No uses esto!").

    Referencias débiles

    Se ha incluido un nuevo tipo de dato llamado 'referencia debil'
    (weakref). Este tipo mantiene una referencia hacia un objeto, pero deja
    de funcionar cuando el objeto sólo tiene referencias débiles apuntando
    hacia él. Es símilar al concepto de 'enlace simbólico' en los sistemas de
    arhivos Unix.

    Por ejemplo, si tuviéramos una caché que almacenara los valores de
    retorno de una función para optimizar el código:

    _cache = {}
    def memoize(x):
        if _cache.has_key(x):
            return _cache[x]
    
        retval = f(x)
        _cache[x] = retval
    
        return retval
    

    Esto tiene el problema de que aunque no usemos nunca más el valor de
    retorno, el objeto almacenado en _cache seguirá allí. Esto no es un gran
    problema si el objeto es un entero o otro tipo pequeño, pero para
    objetos muy grandes puede suponer un gasto de memoria innecesario.

    Reescribámoslo con referencias débiles:

    _cache = {}
    def momoize(x):
        if _cache.has_key(x):
            obj = _cache[x]()
            # Si la referencia débil aún existe la devolvemos:
            if obj is not None: return obj
    
        retval = f(x)
    
        #Cacheamos la referencia débil
        _cache[x] = weakref.ref(retval)
    
        return retval
    

    Como puede observarse las referencias débiles se crean llamando a
    weakref.ref, y para objeterlas se llama al nombre creado con la
    referencia como si fuera una función, esto podemos verlo en un ejemplo
    más sencillo:

    obj = funcion_q_crea_objeto()
    refobj = weakref.ref(obj) #Hemos creado la referencia debil 'refobj'
    obj2 = refobj() # obj2 es una referencia (fuerte) a obj.
    del obj
    del obj2 #Eliminamos las referencias fuertes a obj
    print refobj() # Escribirá 'None'
    

    Aquí (y en el ejemplo anterior) también observamos que cuando un objeto
    sólo tiene referencias débiles ya no existe, y la llamada a la
    referencia débil devuelve 'None'.

    También podemos crear un tipo de referencias débiles llamadas 'proxis'
    que pueden actúar como alias del objeto:

    proxy = weakref.proxy(obj)
    proxy.attr #Equivalente a obj.attr
    proxy.meth() # Equivalente a obj.meth()
    del obj     # Borramos el objeto referenciado
    proxy.attr  # Lanzará una excepción weakref.ReferenceError porque el
                # objeto ya no tiene ninguna referencia fuerte apuntando
                # hacia el.
    
    

    Atributos de funciones

    Las funciones ahora pueden tener información de cualquier tipo unida a
    ellas. Anteriormente se utilizaba mucho el atributo __doc__ para incluir
    información diversa sobre una función porque era la única forma de
    hacerlo. Por ejemplo en el servidor de aplicaciones Zope las funciones
    se marcaban como 'seguras para acceso público' mediante una cadena en
    __doc__.

    Ahora para añadir información y atributos de cualquier tipo a una
    función se puede hacer utilizando la síntaxis habitúal de Python:

    def f(): pass
    
    f.publicable = 1
    f.segura = 1
    f.gramatica = "A :: = B (C D) *"
    

    Como se ahora las funciones se parecen un poquito más a los objetos,
    objetos con un sólo método. De hecho esto anteriormente se podría
    similar creando objetos cuyo método a llamar fuera __call__, pero esto
    es mucho más eficiente.

    Expansión de listas

    Sirven para crear listas a partir de los elementos de una secuencia o
    conjunto de secuencias. Tiene la síntaxis:

    [ expresión for expr  in secuencia1
                for expr2 in secuencia2
                for expr3 in secuencia3
                if condicion]
    

    El if es opcional, se ve más claro con un ejemplo:

    sec1 = 'abc'
    sec2 = (1,2,3)
    [ (x,y) for x in sec1 for y in sec2 ]
    

    Generará:

    [('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1),
    ('c', 2), ('c', 3)]
    

    Ampliación de la asignación

    Se añaden los operadores +=, -=, *=, /=, %=, **=, &=, |=, ^=, >>= y <<=.

    Son iguales que los de C, para el que no lo entienda lo verá con un
    ejemplo:

    x = x + 2 equivale a: x+=2
    x = x /3 equivale a: x/=3
    

    Las clases de python pueden sobrecargar estos operadores mediante los
    métodos __iadd__, __isub__, etc.

    NO se han añadido los operadores de post/pre incremento/decremento (a++,
    ++a).

    Alcances anidados

    Anteriormente sólo había tres tipos de alcance en Python, el global, el
    del módulo y en interno. Esto podría producir errores en el caso de
    algunas funciones anidadas y recursivas como:

    def f():
        def g(valor):
            return g(valor-1) + 1
    

    Porque g no existe en ninguno de los tres alcances (ni local, ni módulo
    ni interno).

    Ahora ya no es así; cada agrupación de instrucciones tiene su propio
    alcance anidado que incluye el del nivel superior.

    Si todo es un objeto, las cadenas también

    Como hemos visto antes, ahora todo es un objeto (de tipo clásico o
    nuevo, pero un objeto). Las cadenas no son una excepción.

    La manipulación que antes se hacía desde el módulo string ahora puede
    hacerse como métodos (aunque sigue vigente el módulo string por
    compatibilidad):

    'juanjo'.capitalize()
    'Juanjo'
    
    'windows mola'.replace('windows', 'linux)
    'linux mola'
    
    'juanjo'.find('o')
    6
    

    Sin embargo las cadenas siguen siendo totalmente inmutables, por lo que
    los métodos de cadena devuelven nuevas cadenas sin modificar la cadena
    sobre la que operan.

    Hay dos métodos nuevos, startswith() y endswith() que añaden un carácter
    al principio o al final de la cadena.

    El método join recibe un parámetro que es una secuencia de cadenas y las
    une con la cadena a la que pertenece el método:

    ' '.join(('uno','dos','tres'))
    'uno dos tres'
    

    Además, desde Python 2.3, el operador 'in' puede utilizarse con cadenas para evaluar si una es
    subcadena de otra (antes sólo podía utilizarse con carácteres), por ejemplo:

    cadena = "el veloz murciélago hindú..."
    subcadena = "veloz"
    if subcadena in cadena:
        print "Si!"
    

    Método pop para diccionarios

    Desde Python 2.3 los diccionarios tienen un nuevo método llamado pop(clave) que
    devuelve el valor correspondiente a la clave especificada en el argumento y sacan
    el par clave/valor del diccionario o lanza un KeyError si el par no existe.

    Recolector de basura

    Antes ya lo tenía, pero el tipo de recolector que tenía (contador de
    referencias) podía llegar a situaciones en la que un objeto inaccesible
    nunca seríe borrado, por ejemplo:

    instancia = AlgunaClase()
    instancia.yomismo = instancia
    

    Ahora el objeto tiene dos referencias; una definida por el propio nombre
    'instancia' y otra dentro de si misma. Si ahora hacemos:

    del instancia
    

    Decrementamos el contador en un, pero como tenía 2 se queda en uno y
    tenemos un objeto al que no podemos acceder (porque hemos borrado la
    referencia que era el nombre 'instancia') pero que sigue en la memoria.

    Por eso ahora periódicamente se ejecuta un sencillo recolector de basura
    basado en un algoritmo para detectar estos ciclos, que borrará los
    objetos en caso de hacerlo.


    Cambios en los módulos existentes

    • Los sockets tienen soporte ssl si el intérprete ha sido compilado para
      ello:

      socket.ssl(socket,fich_de_clave,fich_de_certificado) genera un objeto
      socket SSL.

      Otro cambio que se ha introducido es que los sockets ahora tienen
      soporte para sockets 'raw' (si tenemos permisos para ellos en el sistema
      operativo en el que estemos). Por último, desde Python 2.3 podemos especificar
      un timeout que haga que los sockets lancen la excepción socket.error cuando este 'salte'.
      Para poner un tiemout a un socket llamamos a su método settimeout(tiempo) indicando el tiempo
      (en segundos) como argumento.

    • El módulo curses se ha mejorado muchísimo para proporcionar
      características adicionales de ncurses y sysV curses como colores,
      conjuntos de carácteres alternativos, pads, paneles (ventanas
      ncurses), y ratón.

    • Se ha añadido sys.excepthook. La función que le asignemos a ello será
      la que trate las excepciones no capturadas por ningún bloque
      try...except.

    • Varias funciones del módulo time como asctime() y localtime() requiren
      un argumento en coma flotante conteniendo el tiempo en segundos desde
      la 'época 0'. El uso más común de estas funciones es el de trabajar
      con el tiempo actúal de modo que el argumento en coma flotando ahora
      es opcional, y por defecto será el tiempo actúal, de modo que ahora en
      lugar de tener que escribir, por ejemplo:

      
        time.asctime(time.localtime(time.time()))
      
        Ahora basta con time.asctime()
      
    • La ftplib ahora por defecto funciona en modo pasivo, que causa menos
      problemas detrás de un firewall. Si quiere hacerse que trabaje en modo
      activo se puede llamar al método set_pasv(0) de los objetos FTP.

    • Varias funciones que antes devolvían largas tuplas ahora devuelven
      pseudo-secuencias que aún pueden actúar como tuplas pero que tienen
      atributos memnonicos como 'memberst_mtime' o 'tm_year'. Las funciones
      mejoradas incluyen stat(), fstat(), statvfs(), y fstatvfs() en el
      módulo 'os' y localtime(), gmtime() y strptime() en el módulo 'time'.

      Ejemplo: Para obtener el tamaño de un fichero utilizando las viejas
      tuplas había que terminar escribiendo algo como:

        tamanio_fich = os.stat(nombre_fich)[stat.ST_SIZE]
      

      pero ahora pueden ser escritas con más claridad de la forma:

        tamanio_fich = os.stat(nombre_fich).st_size
      
    • El módulo socket puede compilarse para tener soporte IPv6 activando la
      opción --enable-ipv6 en el script configure de Python.

    • El intérprete en modo interactivo tiene ahora una función help() que
      utilizará pydoc para mostrar la ayuda sobre su parámetro.

    • Se ha mejorado mucho el rendimiento del módulo para tratamiento de
      expresiones regulares 're'.

    • La smtplib soporta SMTP sobre TLS de modo que es posible cifrar el
      tráfico entre un programa Python y el agente de transporte de correo
      que está manejando el mensaje. También se soporta autentificación
      SMTP.

    • El módulo imaplib tiene soporte para las extensiones NAMESPACE, SORT,
      GETACL y SETACL.

    • Se han añadido al módulo string las nuevas constantes ascii_letters,
      ascii_lowercase y ascii_upercase para representar el conjunto de
      carácteres ascii, ascii en minúsculas y ascii en mayúsculas.

    • El modulo threading ahora tiene una clase 'Time' que permite que se
      pueda programar una actividad para que se ejecute en algún tiempo
      futuro.


    Nuevos módulos

    • atexit: Este módulo proporciona atexit.register(función) que hará que
      cuando el intérprete vaya a terminar se ejecute 'función'.

    • filecmp: Sustituye a los viejos cmp, dircmp, y cmpcache.
    • textwrap: Tiene (entre otros) cuenta con una función fill(cadena, anchura) que nos
      devuelve una representación de la cadena divida en varias líneas no mayores que el segundo argumento
      e intentando no cortar las palabras. También tiene una función wrap(cadena, anchura) que hace lo mismo
      pero devolviendo una lista de cadenas en lugar de una cadena completa multilínea.

    • gettext: Esto módulo proporciona soporte para la internacionalización
      de los programas escritos en Python.

    • linuxaudiodev: Proporciona acceso al dispositivo de sonido /dev/audio
      en Linux.

    • mmap: Una interfaz para ficheros mapeados en memoria tanto en Unix
      como en Windows. Los contenidos de un fichero pueden mapearse en
      memoria en cuyo caso se comporta exactamente como una cadena mutable
      de modo que sus contenidos pueden leerse y modificarse. Incluso se les
      puede pasar a funciones que esperen cadenas ordinarias.

    • webbrowser: Un módulo que proporciona una forma independiente de
      ejecutar el navegador que se encuentre disponible en cualquier
      plataforma.

    • _winreg: Una interfaz al registro de windows, anteriormente incluida
      sólo en PythonWin.

    • zipfile: Un módulo para leer y escribir en archivos zip (ojo, zip, no
      gzip).

    • El módulo difflib contiene una clase, SequenceMatcher que compara dos
      secuencias y computa los cambios necesarios para transformar una en la
      otra. Este módulo puede usarse para crear herramientas simialares al
      diff de Unix.

    • Se añade el módulo 'email' para tratamiento de mensajes de correo que
      contiene el objeto 'Message' que representa un mensaje rfc822.

    • El módulo subproccess proporciona una forma más sencilla (y elegante) de ejecutar subcomandos
      tanto mediante su sencilla llamada call: codigoSalida = subprocess.call('programa')

    • Nuevo módulo collections que contiene tipos de dato contenedores (de momento sólo contiene
      "deque" que es una cola doblemente finalizada.)

    • cookielib: proporciona funciones para trabajar con cookies HTTP como cliente.