Apartados


A. Migrando código a Python 3 con 2to3

Nivel de dificultad:5 sobre 5

``La vida es agradable. La muerte es tranquilidad. Es la transición lo que es problemático.''
--atribuido a Isaac Asimov

A.1 Inmersión

Han cambiado tantas cosas entre Python 2 y Python 3 que son pocos los programas que funcionan con ambos sin modificaciones. ¡Pero no te desalientes! Para ayudar con la transición, Python 3 incorpora una herramienta denominada 2to3, que analiza el código fuente en Python 2 y lo convierte en Python 3 tanto como puede. El capítulo 15, se describe como ejecutar 2to3, y muestra algunas cosas que este no puede resolver de forma automática. Este apéndice documenta aquello que sí puede convertir de forma automática.

A.2 La sentencia print

En Python 2, print era una sentencia. Lo que quisieras imprimir iba detrás de ella. En Python 3, print() es una función. Lo quieras imprimir debe pasarse como parámetro, igual que en cualquier otra función.


Línea Python 2 Python 3
1 print print()
2 print 1 print(1)
3 print 1, 2 print(1, 2)
4 print »sys.stderr, 1, 2, 3 print(1, 2, 3, file=sys.stderr)

  1. Línea 1: imprime una línea vacía.
  2. Línea 2: imprime un único valor.
  3. Línea 3: imprime dos valores separados por espacios.
  4. Línea 4: este es un poco complejo. En Python 2, si finalizabas la sentencia print con una coma se imprimían los valores separados por espacios, luego añadía un espacio al final, y paraba sin imprimir un retorno de carroA.1. En Python 3, la forma de hacer esto es pasar end=' ' como un parámetro de la función print(). El parámetro end tiene como valor por defecto '\n' (el retorno de carro), por lo que su sustitución elimina el retorno de carro después de imprimir los demás parámetros.

  5. Línea 5: en Python 2, puedes redirigir la salida a un flujo diferente de la salida estándar --como sys.stderr-- mediante el uso de la sintaxis >>nombre_de_flujo. En Python 3, la forma de hacer esto es pasar el flujo como parámetro file. El valor por defecto de este parámetro es sys.stdout (la salida estándar), por lo que sustituirlo enviará la salida a otro flujo diferente.

A.3 Cadenas de caracteres Unicode

Python 2 tiene dos tipos de cadenas de caracteres: Unicode y no Unicode. Python 3 solo tiene un tipo: Unicode.


Línea Python 2 Python 3
1 u'PapayaWhip' 'PapayaWhip'
2 ur'PapayaWhip\foo' r'PapayaWhip\foo'

  1. Línea 1: las cadenas de caracteres Unicode se escriben directamente como cadenas de caracteres, que en Python 3 siempre son Unicode.
  2. Línea 2: las cadenas de caracteres Unicode Raw (que en Python permiten evitar los caracteres de escape de la barra inclinada invertida) se convierten en cadenas de caracteres Raw, que en Python 3, siempre son Unicode.

A.4 La función global unicode()

Python 2 dispone de dos funciones globales para convertir objetos a cadenas de caracteres: unicode() para convertirlas en cadenas Unicode, y str() para convertirlas en cadenas no Unicode. Python 3 solamente tiene un tipo de cadenas de caracteres, Unicode, por lo que solamente existe la función str(). La función unicode() no existe.


Línea Python 2 Python 3
  unicode(cualquier_objeto) str(cualquier_objeto)

A.5 El tipo de datos long

Python 2 tiene dos tipos de datos para números enteros: int y long. Un int no puede ser mayor que sys.maxint, que es diferente según la plataforma. El tipo long se define añadiendo una 'L' al final del número, y puede ser, bueno, más grande que un int.

En Python 3, solamente existe un tipo de números enteros, denominado int, que se comporta como el tipo long de Python 2. Puesto que no existen dos tipos, no hay necesidad de una sintaxis especial para distinguirlos.

Para profundizar, se puede leer el documento PEP 237: unificando los enteros long e int.


Línea Python 2 Python 3
1 x = 1000000000000L x = 1000000000000
2 x = 0xFFFFFFFFFFFFL x = 0xFFFFFFFFFFFF
3 long(x) int(x)
4 type(x) is long type(x) is int
5 isinstance(x, long) isinstance(x, int)

  1. Línea 1: los enteros largos en base 10 se transforman en enteros en base 10.
  2. Línea 2: los enteros largos en base 16 se transforman en enteros en base 16.
  3. Línea 3: en Python 3, la función long() desaparece. Para forzar una variable al tipo entero, se utiliza la función int().
  4. Línea 4: la función isinstance() sirve para comprobar el tipo de datos de una variable; de nuevo, se debe utilizar int en lugar de long.

A.6 Comparación <>

Python 2 soporta <> como sinónimo de !=, el operador de comparación de desigualdad. Python 3 tiene este operador, pero <> deja de existir.


Línea Python 2 Python 3
1 if x <> y: if x != y:
2 if x <> y <> z: if x != y != z:

  1. Línea 1: una comparación simple.
  2. Línea 2: una comparación más compleja entre tres valores.

A.7 Método de diccionarios: has_key()

En Python 2, los diccionarios tienen un método has_key() para comprobar si el diccionario contiene una determinada clave. En Python 3, este método no existe. En su lugar, debes utilizar el operador in.


Línea Python 2 Python 3
1 un_diccionario.has_key('PapayaWhip') 'PapayaWhip' in un_diccionario
     
2 10cmun_diccionario.has_key(x) or
un_diccionario.has_key(y)
10cmx in un_diccionario or
y in un_diccionario
     
3 un_diccionario.has_key(x or y) (x or y) in un_diccionario
4 un_diccionario.has_key(x + y) (x + y) in un_diccionario
5 x + un_diccionario.has_key(y) x + (y in un_diccionario)

  1. Línea 1: la forma más simple.
  2. Línea 2: el operador in tiene precedencia sobre el operador or, por lo que no hacen falta paréntesis.
  3. Línea 3: por otra parte, sí necesitas paréntesis alrededor de x or yA.2.
  4. Línea 4: El operador + tiene precedencia sobre el operador in, por lo que esta línea, técnicamente, no necesita paréntesis alrededor de x + y, pero 2to3 los incluye para mayor legibilidad del código.
  5. Línea 5: Esta forma sí que necesita paréntesis alrededor de i in un_diccionario ya que el operador + tiene precedencia sobre in.

A.8 Métodos de diccionario que devuelven listas

En Python 2, muchos métodos de diccionario devuelven listas. Los métodos más usados, como: keys(), items(), y values(). En Python 3, todos estos métodos devuelven vistas dinámicas. En algunos contextos, esto no es problema. Por ejemplo, si el valor de retorno de uno de estos métodos se pasa inmediatamente a otra función que itera a través de la secuencia completa, no hay diferencia visible si el tipo real es una lista o una vista. En otros contextos, sí tiene mucha importancia. Si estabas esperando una lista completa con elementos direccionables individualmente, el código fallará, ya que las vistas no soportan el indexado.


Línea Python 2 Python 3
1 mi_diccionario.keys() list(mi_diccionario.keys())
2 mi_diccionario.items() list(mi_diccionario.items())
3 mi_diccionario.iterkeys() iter(mi_diccionario.keys())
4 [i for i in mi_diccionario.iterkeys()] [i for i in mi_diccionario.keys()]
5 min(mi_diccionario.keys()) no cambia

  1. Línea 1: 2to3 tiene a ir a lo más seguro, convirtiendo el valor de retorno de keys() a una lista estática con la función list(). Esto siempre funciona, pero será menos eficiente que utilizar una vista. Deberías examinar el código convertido para ver si es absolutamente necesaria la conversión, o si sería suficiente con una vista.
  2. Línea 2: se produce otra conversión a lista con la función items(). Y también con la función values().
  3. Línea 3: Python 3 elimina el método iterkeys(). Utiliza keys() y, si es necesario, convierte la vista a un iterador con la función iter().
  4. Línea 4: 2to3 reconoce cuando el método iterkeys() se utiliza dentro de una comprensión de lista, y lo convierte al método keys() (sin la llamada extra a la función iter(). Esto funciona porque las vistas son iterables.
  5. Línea 5: 2to3 reconoce que el método keys() se va a pasar inmediatamente como parámetro de una función que itera a través de la secuencia completa, por eso no hay necesidad de convertir el valor de retorno en una lista. La función min() iterará sin problemas a través de la vista. Esto se aplica a los funciones: min(), max(), sum(), list(), tuple(), set(), sorted(), any() y all().

A.9 Módulos que se han renombrado o reorganizado

Se han renombrado varios módulos de la Librería Estándar de Python. Otros relacionados se han combinado o reorganizado para darles una asociación más lógica.

A.9.1 http

En Python 3, se han combinado en un único paquete, http, varios módulos relacionados con HTTP.


Línea Python 2 Python 3
1 import httplib import http.client
2 import Cookie import http.cookies
3 import cookielib import http.cookiejar
4 10cmimport BaseHTTPServer
import SimpleHTTPServer
import CGIHttpServer
import http.server

  1. Línea 1: el módulo http.client implementa una librería de bajo nivel que puede solicitar recursos HTTP e interpretar respuestas HTTP.
  2. Línea 2: el módulo http.cookies proporciona un interfaz pythonico a las cookies del navegador que se envían en una cabecera Set-Cookie: HTTP.
  3. Línea 3: el módulo http.cookiejar manipula los ficheros en los que se almacenan las cookies de los navegadores más populares.
  4. Línea 4: el módulo http.server proporciona un servidor HTTP básico.

A.9.2 urllib

Python 2 tiene un nido de ratas de módulos solapados para procesar, codificar y recuperar URLs. En Python 3, se han refactorizado y combinado en un único paquete, urllib.


Línea Python 2 Python 3
1 import urllib 10cm import urllib.request, urllib.parse,
urllib.error
2 import urllib2 import urllib.request, urllib.error
3 import urlparse import urllib.parse
4 import robotparse import urllib.robotparse
     
5 10cmfrom urllib import FancyURLopener
from urllib import urlencode
10cmfrom urllib.request
        import FancyURLopener
from urllib.parse urlencode
     
6 10cmfrom urllib2 import Request
from urllib2 import HTTPError
10cmfrom urllib.request import Request
from urllib.error HTTPError

  1. Línea 1: el viejo módulo urllib de Python 2 tenía una gran variedad de funciones, incluida urlopen() para recuperar los datos, y splittype(), splithost(), y splituser() para dividir una URL en sus partes constituyentes. Estas funciones se han reorganizado de forma más lógica en el nuevo paquete urllib. 2to3 también modificará todas las llamadas a estas funciones para que utilicen este nuevo esquema de denominación
  2. Línea 2: el viejo módulo urllib2 de Python 2 se ha incorporado al paquete urllib. Todos los elementos conocidos de este paquete --El metodo build_opener(), los objetos Request, HTTPBasicAuthHandler y otros-- están aún disponibles.
  3. Línea 3: el módulo urllib.parse de Python 3 contiene todas las funciones del antiguo módulo urlparse de Python 2.
  4. Línea 4: el módulo urllib.robotparser procesa ficheros robots.txt.
  5. Línea 5: la clase FancyURLopener, que manera las redirecciones HTTP y otros códigos de estado, está aún disponible en el nuevo módulo http.request. La función urlencode() se ha movido a urllib.parse.
  6. Línea 6: el objeto Request está aún disponible en urllib.request, pero las constantes como HTTPError se han movido a urllib.error.

¿He mencionado que 2to3 reescribirá todas las llamadas a estas funciones? Por ejemplo, si tu código Python 2 importa el módulo urllib y llama a urllib.urlopen() para obtener datos, 2to3 arreglará arreglará ambas: la sentencia de importación y la llamada a la función.


Línea Python 2 Python 3
  import urllib 10cm import urllib.request, urllib.parse,
urllib.error
     
  10cmprint urllib.urlopen(
'http://diveintopython3.org').read()
10cmprint urllib.request.urlopen(
'http://diveintopython3.org').read()

A.9.3 dbm

Los diversos clones de DBM se encuentran ahora en un único paquete, dbm. Si necesitas una variante específica, como GNU DBM, puedes importar el módulo apropiado de este paquete.


Línea Python 2 Python 3
  import dbm import dbm.ndbm
  import gdbm import dbm.gnu
  import dbhash import dbm.bsd
  import dumbdbm import dbm.dumb
     
  10cmimport anydbm
import whichdb
import dbm

A.9.4 xmlrpc

XML-RPC es un método ligero de hacer llamadas RPC sobre HTTP. La librería cliente XML-RPC y varias implementaciones de servidores XML-RPC se han combinado en un único paquete, xmlrpc.


Línea Python 2 Python 3
  import xmlrpclib import xmlrpc.client
     
  10cmimport DocXMLRPCServer
import SimpleXMLRPCServer
import xmlrpc.server

A.9.5 Otros módulos


Línea Python 2 Python 3
1 try: import io
      import cStringIO as StringIO  
  except ImportError:  
      import StringIO  
     
2 try: import pickle
      import cPickle as pickle  
  except ImportError:  
      import pickle  
     
3 import __builtin__ import builtins
4 import copy_reg import copyreg
5 import Queue import queue
6 import SocketServer import socketserver
7 import ConfigParser import configparser
8 import repr import reprlib
9 import commands import subprocess

  1. Línea 1: una construcción idiomática común en Python 2 era intentar importar cStringIO como StringIO, y si fallaba, importar la implementación StringIO. En Python 3 desaparece, el módulo io lo hace por ti. Encontrará la implementación más rápida disponible y la utilizará automáticamente.
  2. Línea 2: una construcción similar se utilizaba para encontrar la implementación más rápida de pickle. En Python 3, el módulo pickle lo hace por ti.
  3. Línea 3: el módulo builtins contiene las funciones globales, clases y constantes que se utilizan en todas partes. La redefinición de una función de este módulo la redefinirá en todas partes. Esto es tan potente y peligroso como parece.
  4. Línea 4: el módulo copyreg añade soporte de pickle a los tipos de datos a medida definidos en C.
  5. Línea 5: el módulo queue proporciona colas multiproductor-multiconsumidor.
  6. Línea 6: el módulo socketserver proporciona una clase base genérica para implementar diferentes clases de servidores de sockets.
  7. Línea 7: el módulo configparser procesa ficheros de configuración de estilo INI.
  8. Línea 8: el módulo reprlib reimplementa la función interna repr(), añadiendo controles adicionales sobre lo larga que pueden ser las representaciones antes de ser truncadas.
  9. Línea 9: el módulo subprocess te permite lanzar procesos, conectar sus flujos de entrada y de salida, y obtener sus códigos de retorno.

A.10 Importación relativa dentro de un paquete

Un paquete es un grupo de módulos relacionados que funcionan como una única entidad. En Python 2, cuando los módulos de un paquete necesitaban referenciarse entre sí, podías utilizar import foo o from foo import Bar. El interprete de Python 2 primero buscaba en el paquete actual el fichero foo.py, y luego se movía a los demás directorios del camino de búsqueda (sys.path). Python 3 funciona diferente. En lugar de buscar en el paquete actual, va directamente al camino de búsqueda. Si quieres que un módulo de un paquete importe a otro módulo en el mismo paquete, necesitas proporcionar explícitamente el camino relativo entre estos dos módulos.

Supón que tenías el siguiente paquete con múltiples ficheros en el mismo directorio:

chardet/
|
+-__init__.py
|
+-constants.py
|
+-mbcharsetprober.py
|
+-universaldetector.py
Ahora supón que el fichero universaldetector.py necesita importar el contenido completo del fichero constants.py y una clase del fichero mbcharsetprober.py. ¿Cómo lo harías?


Línea Python 2 Python 3
1 import constants from . import constants
     
2 10cmfrom mbcharsetprober import
MultiByteCharSetProber
10cmfrom .mbcharsetprober import
MultiByteCharsetProber

  1. Línea 1: cuando necesitas importar un módulo completo de cualquier otra parte de tu paquete, utiliza la nueva sintaxis from . import. EL punto es el path relativo desde este fichero (universaldetector.py) al fichero que quieres importar (constants.py). En este caso, están en el mismo directorio, por lo que se coloca un único punto. También puedes importar desde el directorio padre (from .. import otro_módulo) o desde un subdirectorio.
  2. Línea 2: Para importar un clase específica, o función, desde otro módulo directamente en el espacio de nombres de tu módulo, introduce el prefijo del módulo objetivo con un camino relativo, sin usar la barra inclinada del final. En este caso, mbcharsetprober.py está en el mismo directorio que universaldetector.py, por lo que el camino es un único punto. También puedes importar desde el directorio padre (from ..otro_modulo import otra_clase o desde un subdirectorio.

A.11 El método iterador next()

En Python 2, los iteradores tienen el método next() que devuelve el siguiente elemento de la secuencia. Esto sigue siendo cierto en Python 3, pero ahora hay también una función global next() que toma un iterador como parámetro.


Línea Python 2 Python 3
1 unIterador.next() next(unIterador)
2 función_q_retorna_iterador().next() next(función_q_retorna_iterador())
     
3 class A: class A:
      def next(self):     def __next__(self):
          pass         pass
     
4 class A:  
      def next(self, x, y): no cambia
          pass  

  1. Línea 1: en el caso más simple, en lugar de llamar al método next() del iterador, ahora puedes pasar el propio iterador a la función global next().
  2. Línea 2: si tienes una función que devuelve un iterador, puedes llamar a la función y pasarle el resultado a la función next().
  3. Línea 3: si defines tu propia clase y quieres usarla como un iterador, debes definir el método especial __next__().
  4. Línea 4: si defines tu clase y resulta que ya tiene un método next() con uno o más parámetros (sin contar self), 2to3 no la modificará. Esta clase no podrá utilizarse como iterador puesto que su método next() tiene parámetros.
  5. Línea 5: esta última es un poco compleja. Si tienes una variable local que se llame next, tiene precedencia sobre la nueva función global next(). En este caso, necesitas llamar al método especial __next__() directamente para poder obtener el siguiente elemento de la secuencia (Alternativamente, podrías refactorizar el código para que la variable local no se llamara next, pero 2to3 no lo hará por ti.

A.12 La función global filter()

En Python 2, la función filter() devolvía una lista, el resultado de filtrar una secuencia a través de una función que devolviera True o False para cada elemento de la secuencia. En Python 3, la función filter() devuelve un iterador, no una lista.


Línea Python 2 Python 3
1 filter(función, secuencia) list(filter(función, secuencia))
2 list(filter(función, secuencia)) no cambia
3 filter(None, secuencia) [i for i in secuencia if i]
4 for i in filter(None, secuencia): no cambia
5 [i for i in filter(función, secuencia)] no cambia

  1. Línea 1: en el caso básico, 2to3 envolverá la llamada a filter() con una llamada a list(), lo que simplemente itera y devuelve una lista verdadera.
  2. Línea 2: sin embargo, si la llamada a filter() ya está envuelta en una llamada a list(), 2to3 no hará nada, ya que no es necesario.
  3. Línea 3: para sintaxis especial filter(None, ...), 2to3 transformará la llamada en una lista por comprensión semánticamente equivalente.
  4. Línea 4: en contextos como el bucle for, que iteran a través de la secuencia completa siempre, no se requieren cambios.
  5. Línea 5: tampoco se requieren cambios, cuando ya se itere de forma completa por encontrarse filter() en una sentencia de lista por comprensión.

A.13 La función global map()

De forma parecida a como se comporta filter(), la función map() ahora devuelve un iterador (En Python 2, devolvía una lista).


Línea Python 2 Python 3
1 map(función, 'PapayaWhip') list(map(función, 'PapayaWhip'))
2 map(None, 'PapayaWhip') list('PapayaWhip')
2 map(lambda x: x+1, range(42)) [x+1 for x in range(42)]
4 for i in map(None, secuencia): no cambia
5 [i for i in map(función, secuencia)] no cambia

  1. Línea 1: como sucede con filter(), en el caso más básico, 2to3 envolverá la llamada a map()con una llamada a list().
  2. Línea 2: para la sintaxis especial map(None, ...), la función identidad, 2to3 la convertirá en una llamada equivalente a list().
  3. Línea 3: si el primer argumento de map() es una función lambda, 2to3 la convertirá en una lista por comprensión equivalente.
  4. Línea 4: en contextos como los bucles for, en los que se itera a través de la secuencia completa, no se requieren cambios.
  5. Línea 4: de nuevo, no hacen falta cambios, ya que la lista por comprensión iterará a través de la secuencia completa, y esto es posible tanto si el valor de retorno de map() es un iterador, como si es una lista.

A.14 La función global reduce()

En Python 3, se ha suprimido la función reduce() del espacio de nombres global, y se ha colocado en el módulo functools.


Línea Python 2 Python 3
  reduce(a, b, c) from functools import reduce
    reduce(a, b, c)

A.15 La función global apply()

Python 2 tiene una función global denominada apply(), que toma una función f y una lista [a, b, c] y devuelve f(a, b, c). Puedes conseguir el mismo resultado mediante la llamada directa a la función pasándole la lista de parámetros precedida de un asterisco. En Python 3, la función apply() ha dejado de existir; debes usar obligatoriamente la notación asterisco.


Línea Python 2 Python 3
1 apply(funcion, listaParametros) funcion(*listaParametros)
2 apply(funcion, listaParametros, funcion(*listaParametros,
      dicParamNombre)     **dicParamNombre)
3 apply(funcion, listaParametros + z) funcion(listaParametros + z)
4 apply(modulo.funcion, listaParam) modulo.funcion(listaParam)

  1. Línea 1: en su forma más simple, puedes llamar a una función con una lista de parámetros añadiendo un asterisco delante de la lista. Esto es equivalente a la anterior función apply() de Python 2.
  2. Línea 2: en Python 2, la función apply() podría tomar hasta tres parámetros: una función, la lista de parámetros, y un diccionario con los parámetros que se pasan por nombre. En Python 3, puedes conseguir el mismo efecto añadiendo un asterisco a la lista de parámetros y dos asteriscos al diccionario con los parámetros que se pasan por nombre.
  3. Línea 3: El operador +, utilizado aquí para la concatenación de listas, tiene prioridad sobre el operador * (asterisco), por eso no hay necesidad unos paréntesis extras alrededor de listaParametros + z.
  4. Línea 4: 2to3 es lo suficientemente inteligente como para convertir llamadas apply() complejas, incluidas las llamadas a función de módulos importados.

A.16 La función global intern()

En Python 2, se puede llamar a la función intern() sobre una cadena de caracteres para internalizarla con el fin de optimizar su rendimiento. En Python 3, la función intern() se ha movido al módulo sys.


Línea Python 2 Python 3
  intern(a, b, c) sys.intern(a, b, c)

A.17 La sentencia exec

Igual que se ha convertido la sentencia print en función en Python 3, se ha hecho lo mismo con la sentencia exec. La función exec() toma una cadena de caracteres que contiene cualquier código Python y lo ejecuta como si fuese una sentencia o expresión. exec() es como eval(), pero aún más poderoso y maligno. La función eval() solo puede evaluar una única expresión, pero exec() puede ejecutar múltiples sentencias, importar paquetes, declarar funciones --esencialmente, ejecutar un programa Python entero, escrito en una cadena de caracteres.


Línea Python 2 Python 3
1 exec cadenaEjecutable exec(cadenaEjecutable)
2 exec cadenaEjec in espNombresGlobal exec(cadenaEjec, espNombresGlobal)
3 exec cadenaEjec in espNombresGlobal, exec(cadenaEjec, espNombresGlobal,
      espacioNombresLocal     espacioNombresLocal)

  1. Línea 1: en su forma más simple, 2to3 simplemente envuelve entre paréntesis a la cadena de caracteres que contiene el código fuente.
  2. Línea 2: la antigua sentencia exec podía tomar un espacio de nombres, un espacio privado de elementos globales en el que el código fuente se ejecutaría. En Python 3, esto se hace pasando el espacio de nombres como segundo parámetro de la función exec().
  3. Línea 3: la anterior sentencia exec podía tomar como parámetro un espacio de nombres local (como las variables definidas dentro de una función). En Python 3, aún se puede hacer con la función exec().

A.18 La sentencia execfile

Como la anterior sentencia exec, la sentencia execfile sirve ejecuta el código escrito, esta vez, en un fichero. En Python 3 esta sentencia se ha eliminado. Si realmente necesitas leer un fichero Python y ejecutarlo (pero no deseas, simplemente, importarlo), puedes conseguirlo abriendo el fichero, leyendo su contenido, llamando a la función global compile() para forzar al interprete de Python a compilar el código, y luego llamar a la nueva función exec().


Línea Python 2 Python 3
  execfile('nombre_fichero') exec(compile(open('nombre_fichero').read(),
        'nombre_fichero2', 'exec'))

A.19 Literales repr (comilla invertida)

En Python 2, existe una sintaxis especial para envolver cualquier objeto en comillas invertidas (como `x`) para obtener una representación del objeto. En Python 3, no puedes utilizarlas; en su lugar, debes usar la función global repr().


Línea Python 2 Python 3
1 `x` repr(x)
2 `'PapayaWhip' + `2`` repr('PapayaWhip' + repr(2))

  1. Línea 1: x puede ser cualquier cosa: una clase, una función, un módulo, un tipo de datos primitivo, etc. La función repr() funciona sobre cualquier cosa.
  2. Línea 2: En Python 2, las comillas invertidas se pueden anidar, 2to3 las detectar y convierte en llamadas anidadas a repr().

A.20 La sentencia try ...except

La sintaxis para capturar excepciones ha cambiado algo entre Python 2 y 3.


Línea Python 2 Python 3
1 try: try:
      import mymodule     import mymodule
  except ImportError, e except ImportError as e:
      pass     pass
2 try: try:
      import mymodule     import mymodule
  except (RuntimeError, except (RuntimeError
          ImportError), e         ImportError) as e:
      pass     pass
3 try:
      import mymodule no cambia
  except ImportError:
      pass     
4 try:
      import mymodule no cambia
  except:
      pass     

  1. Línea 1: en lugar de una coma después del tipo de la excepción, Python 3 utiliza una palabra reservada nueva as.
  2. Línea 2: la palabra reservada as también se usa en la captura de múltiples tipos de excepción de una vez.
  3. Línea 3: si capturas una excepción pero te da igual acceder al objeto de la excepción, la sintaxis es idéntica en Python 2 y 3.
  4. Línea 4: igualmente, si utilizas la sintaxis para capturar todos los tipos de excepción posibles, la sintaxis es idéntica en Python 2 y 3.

Nunca deberías utilizar la captura por defecto de todos los tipos de excepción cuando importes módulos (o en la mayoría de otras ocasiones). Al hacer esto, capturarás cosas como KeyboardInterrupt (que se dispara si el usuario pulsa Ctrl-C para interrumpir el programa) y que puede hacer más difícil depurar los errores.

A.21 La sentencia raise

La sintaxis para elevar tus propias excepciones ha cambiado también ligeramente.


Línea Python 2 Python 3
1 raise miExcepcion no cambia
2 raise miExcepcion, 'mensaje de error' raise miExcepcion('mensaje de error')
3 raise miExcepcion, 'mensaje de error', raise miExcepcion('mensaje de error').
  traza) with_traceback(traza)
4 raise 'mensaje de error' no soportado

  1. Línea 1: en su forma más simple --cuando se eleva una excepción sin incluir mensaje de error-- la sintaxis no ha cambiado.
  2. Línea 2: la modificación de sintaxis se deja notar cuando quieres elevar una excepción con un mensaje de error personalizado. Python 2 separaba el tipo de excepción y el mensaje con una coma; Python 3 lo pasa como parámetro de la excepción.
  3. Línea 3: Python 2 soportaba una sintaxis más compleja para elevar una excepción con una pila de trazabilidad a medida. En Python 3, para hacer esto, la sintaxis es diferente.
  4. Línea 4: en Python 2, podías elevar una excepción sin introducir el tipo de la misma, solo el mensaje de error. En Python 3, esto no es posible. 2to3 te avisará de que no ha sido capaz de arreglarlo automáticamente.

A.22 El método throw en generadores

En Python 2, los generadores disponen de un método throw(): al llamar a este método se eleva una excepción en el punto en el que el generador estuviera pausado, luego se devuelve el siguiente valor disponible (en yield). En Python 3, esta funcionalidad sigue existiendo con pequeños cambios.


Línea Python 2 Python 3
1 unGenerador.throw(miExcepcion) no cambia
2 unGenerador.throw(miExcepcion, unGenerador.throw(
      'mensaje de error')     miExcepcion('mensaje de error'))
3 unGenerador.throw('mensaje de error') no soportado

  1. Línea 1: en su forma más simple, un generador lanza una excepción sin un mensaje de error. En este caso, la sintaxis no cambia.
  2. Línea 2: si el generador lanza una excepción con un mensaje de error, necesitas pasar la cadena de caracteres del mensaje como parámetro de la excepción que estás creando.
  3. Línea 3: Python 2 también soportaba el lanzar una excepción solamente con el mensaje de error, sin indicar su tipo. En Python 3 esto no está permitido, 2to3 mostrará un aviso indicando que necesitas arreglar esto manualmente.

A.23 La función global xrange()

En Python 2, había dos formas de obtener un rango de números: range(), que retornaba una lista; y xrange(), que retornaba un iterador. En Python 3, range() retorna un iterador, y xrange() no existe.


Línea Python 2 Python 3
1 xrange(10) range(10)
2 lista = range(10) lista = list(range(10))
3 [i for i in xrange(10)] [i for i in range(10)]
4 for i in range(10): no cambia
5 sum(range(10)) no cambia

  1. Línea 1: en su forma más simple, 2to3 convierte xrange() en range().
  2. Línea 2: Si tu código Python 2 usa range(), 2to3 no sabe si necesitas una lista o un iterador; por ello, opta por lo seguro, que es envolver la función en una lista llamando a la función list().
  3. Línea 3: si la función xrange() se encontraba en una lista por comprensión, 2to3 es suficientemente inteligente como para no envolverla en una lista.
  4. Línea 4: De forma similar, un bucle for funcionará igual de bien con un iterador. Por lo que no hay necesidad de cambiar nada en e este caso.
  5. Línea 5: la función sum() también funciona con un iterador, por eso 2to3 no hace ningún cambio. Esto se aplica a min(), max(), sum(), list(), tuple(), set(), sorted(), any(), y all().

A.24 Las funciones globales raw_input() e input()

Python 2 tiene dos funciones globales para pedir al usuario que introduzca datos en la línea de comandos. La primera se denomina input(), y espera que el usuario introduzca una expresión Python (y devuelve el resultado). La segunda, denominada raw_input(), simplemente retorna lo que el usuario ha tecleado. Esto es confuso para principiantes. Python 3 redenomina raw_input() a input(), para que funcione como todo el mundo imagina que debería.


Línea Python 2 Python 3
1 raw_input() input()
2 raw_input('prompt:') input('prompt:')
3 input() eval(input())

  1. Línea 1: en su forma más simple, raw_input() pasa a ser input().
  2. Línea 2: en Python 2, la función raw_input() podía pedir al usuario (con una cadena de caracteres) la entrada de datos mediante un ``prompt'' que se pasaba como parámetro. Esto se mantiene en Python 3 en la nueva función input().
  3. Línea 3: si realmente necesitas pedirle al usuario una expresión que luego debes evaluar, utiliza input() y pasa el resultado a la función eval().

A.25 Los atributos de función func_*

En Python 2, el código dentro de las funcionas puede acceder a atributos de la propia función. En Python 3, estos atributos especiales se han redenominado por consistencia con otros atributos.


Línea Python 2 Python 3
1 unaFuncion.func_name unaFuncion.__name__
2 unaFuncion.func_doc unaFuncion.__doc__
3 unaFuncion.func_defaults unaFuncion.__defaults__
4 unaFuncion.func_dict unaFuncion.__dict__
5 unaFuncion.func_closure unaFuncion.__closure__
6 unaFuncion.func_globals unaFuncion.__globals__
7 unaFuncion.func_code unaFuncion.__code__

  1. Línea 1: el nombre de la función.
  2. Línea 2: contiene el docstring definido para la función en el código fuente.
  3. Línea 3: una tupla que contiene los valores por defecto de los parámetros, para aquellos que lo tengan.
  4. Línea 4: es el espacio de nombres que soporta los atributos de la función.
  5. Línea 5: es una tupla de celdas que contienen los enlaces (bindings) de las variables libres de la función.
  6. Línea 6: es una referencia al espacio de nombres global del módulo en el que la función fue definida.
  7. Línea 7: es un objeto de código que representa al cuerpo compilado de la función.

A.26 El método de E/S xreadlines()

En Python 2, los objetos fichero tenían un método xreadlines() que devolvía un iterador que leía una línea cada vez. Era útil en bucles for, entre otros lugares. En realidad, era tan útil, que posteriores versiones añadieron esta capacidad de forma nativa a los propios objetos fichero.

En Python 3, la función xreadlines() ya no existe. 2to3 puede arreglar los casos más simples, pero otros requieren de intervención manual.


Línea Python 2 Python 3
1 for linea in fich.xreadlines(): for linea in fich:
2 for linea in fich.xreadlines(5): no cambia (roto)

  1. Línea 1: si llamabas a xreadlines() sin parámetros, 2to3 lo convertirá al objeto fichero (sin necesidad de llamada extra).
  2. Línea 2: si llamabas a xreadlines() pasándole el número de línea a leer cada vez, 2to3 no lo arreglará, y el código fallará con una excepción AttributeError: '_io.TextIOWrapper' object has no attribute 'xreadlines'. Puedes cambiar manualmente xreadlines() a readlines() para que funcione en Python 3 (El método readlines() ahora devuelve un iterador, por lo que es tan eficiente como era xreadlines() en Python 2).

A.27 Funciones lambda con parámetros tupla

En Python 2, podías definir una función lambda anónima que tomara varios parámetros definiendo la función para que recibiera una tupla con un número específico de elementos. Python 2 ``desempaqueta'' la tupla en los parámetros con nombre, que luego puedes referenciar (por nombre) en la propia función lambda. En Python 3, aún puedes pasar una tupla a una función lambda, pero el interprete de Python no desempaquetará los parámetros. En su lugar, necesitas referenciar cada uno de ellos mediante su posición en la tupla.


Línea Python 2 Python 3
1 lambda (x,): x + f(x) lambda x1: x1[0] + f(x1[0])
2 lambda (x, y): x + f(y) lambda x_y: x_y[0] + f(x_y[1])
3 lambda (x, (y,z)): x + y + z lambda x_y_z: x_y_z[0] +
        x_y_z[1][0] + x_y_z[1][1]
4 lambda x, y, z: x + y + z no cambia

  1. Línea 1: si habías definido una función lambda que toma como parámetro una tupla con un único elemento, en Python 3, 2to3 lo convertirá en referencias al primer elemento x1[0]. El nombre x1 es autogenerado, y se basa en el nombre del parámetro de la tupla original.
  2. Línea 2: una función lambda con una tupla de dos elementos (x, y) se convierte en x_y con los parámetros en las posiciones x_y[0] x_y[1]
  3. Línea 3: 2to3 puede manejar funciones lambda con tuplas anidadas como parámetros. El código resultante en Python 3 es poco legible.
  4. Línea 4: puedes definir funciones lambda con varios parámetros que no sean tuplas; en este caso la sintaxis de Python 3 no cambia.

A.28 Atributos especiales de métodos

En Python 2, los métodos de las clases pueden referenciar al objeto clase en el que están definidos, así como el objeto método en sí mismo. im_self es el objeto instancia de la clase; im_func es el objeto función (el propio método); im_class es la clase de im_self. En Python 3, estos atributos especiales de método se han redenominado para seguir las convenciones de otros atributos.


Python 2 Python 3
instDeClase.MétDeClase.im_func instDeClase.MétDeClase.__func__
instDeClase.MétDeClase.im_self instDeClase.MétDeClase.__self__
instDeClase.MétDeClase.im_class instDeClase.MétDeClase.__self__.__class__

A.29 El método especial __nonzero__

En Python 2, podrías construir tus propias clases que podían utilizarse en contextos booleanos. Por ejemplo, podrías instanciar un objeto de la clase y luego usar esta instancia como condición en una sentencia if. Para hacer esto debías definir un método especial __nonzero__() que debía devolver True o False, y que Python llamaba siempre que un objeto se utilizara en un contexto booleano. En Python 3, se ha cambiado el nombre del método a __bool__().


Línea Python 2 Python 3
1 class A: class A:
      def __nonzero__(self):     def __bool__(self):
          pass         pass
     
2 class A:
      def __nonzero__(self, x, y): no cambia
          pass

  1. Línea 1: en lugar de __nonzero__(), Python 3 llama al método __bool__() cuando evalúa una objeto en un contexto booleano.
  2. Línea 2: sin embargo, si tu código tiene un objeto __nonzero__() con argumentos adicionales a self, 2to3 asumirá que lo estabas usando para otro propósito y no hará ningún cambio en este método.

A.30 Literales octales

Ha cambiado ligeramente:


Línea Python 2 Python 3
  x = 0755 x = 0o755

A.31 sys.maxint

Debido a que los tipos long e int se integran en uno solo, la constante sys.maxint deja de ser precisa. Por ello se ha redenominado a sys.maxsize.


Línea Python 2 Python 3
1 from sys import maxint from sys import maxsize
2 unaFunción(sys.maxint) unaFunción(sys.maxsize)

  1. Línea 1: maxint se convierte en maxsize.
  2. Línea 2: Cualquier uso de sys.maxint se convierte en sys.maxsize.

A.32 La función global callable()

En Python 2, podías comprobar con la función callable() si un objeto se podía ``llamar'' (como una función). En Python 3, esta función global se ha eliminado. Para comprobar si un objeto se puede llamar hay que comprobar la existencia del método especial __call__().


Línea Python 2 Python 3
1 callable(cualquierObjeto) hasattr(cualquierObjeto, '__call__')

A.33 La función global zip()

En Python 2, la función global zip() tomaba cualquier número de secuencias y devolvía una lista de tuplas. La primera tupla contenía el primer elemento de cada una de las secuencias que había recibido como parámetros; la segunda tupla, los segundos elementos de cada secuencia, y así sucesivamente. En Python 3, zip() devuelve un iterador en lugar de una lista.


Línea Python 2 Python 3
1 zip(a, b, c) list(zip(a, b, c))
2 d.join(zip(a, b, c)) no cambia

  1. Línea 1: En su forma más simple, se conserva el anterior comportamiento con una llamada a list() que envuelva a zip().
  2. Línea 2: En contextos en los que ya se itera a través de los elementos de una secuencia (como la llamada a join()), 2to3 es lo suficientemente inteligente para detectar estos casos y no hacer cambios en el código.

A.34 La excepción StandardError

En Python 2, StandardError era la clase base de todas las excepciones internas salvo StopIteration, GeneratorExit, KeyboardInterrupt, y SystemExit. En Python 3, StandardError ha sido eliminada; en su lugar se debe usar Exception.


Línea Python 2 Python 3
  x = StandardError() x = Exception()
  x = StandardError(a, b, c) x = Exception(a, b, c)

A.35 Constantes del módulo types

El módulo types contiene una variedad de constantes destinadas a ayudar a encontrar el tipo de un objeto. En Python 2, contenía constantes para todos los tipos primitivos como dict e int. En Python 3, se han eliminado estas constantes; basta con usar el propio nombre de los tipos.


Línea Python 2 Python 3
  types.UnicodeType str
  types.StringType bytes
  types.DictType dict
  types.IntType int
  types.LongType int
  types.ListType list
  types.NoneType type(None)
  types.BooleanType bool
  types.BufferType memoryview
  types.ClassType type
  types.ComplexType complex
  types.EllipsisType type(Ellipsis)
  types.FloatType float
  types.ObjectType object
  types.NotImplementedType type(NotImplemented)
  types.SliceType slice
  types.TupleType tuple
  types.TypeType type
  types.XRangeType range

types.StringType se mapea a bytes en lugar de a str porque en Python 2 ``string'' (no Unicode) realmente es una secuencia de bytes en una codificación de caracteres determinada.

A.36 La función global isinstance()

La función insinstance() comprueba si un objeto es una instancia de una clase o tipo particular. En Python 2, puedes pasar una tupla de tipos: isinstance() devolverá True si el objeto es de alguno de los tipos de la tupla. En Python 3, aún puedes hacerlo, pero pasar el mismo tipo dos veces está deprecado.


Línea Python 2 Python 3
  isinstance(x (int, float, int)) isinstance(x, (int, float))

A.37 El tipo de datos basestring

Python 2 tenía dos tipos de cadenas de caracteres: Unicode y no Unicode. Pero había también otro tipo: basestring. Era un tipo de datos abstracto, una superclase para ambos tipos: str y unicode. No se podía instanciar directamente, pero sí se podía pasar a la función global isinstance(). En Python 3, solamente existe un tipo de datos de cadenas de caracteres, por lo que basestring no tiene razón de existir.


Línea Python 2 Python 3
  isinstance(x, basestring) isinstance(x, str)

A.38 El módulo itertools

Python 2.3 introdujo el módulo itertools, que definía variantes de las funciones zip(), map(), y filter() que devolvían iteradores en lugar de listas. En Python 3, las funciones originales ya devuelven iteradores, por lo que estas funciones se han eliminado del módulo.


Línea Python 2 Python 3
1 itertools.izip(a, b) zip(a, b)
2 itertools.imap(a, b) map(a, b)
3 itertools.ifilter(a, b) filter(a, b)
4 from itertools import imap, izip, foo from itertools import foo

  1. Línea 1: En lugar de itertools.izip(), utiliza zip().
  2. Línea 2: En lugar de itertools.imap(), utiliza map().
  3. Línea 3: En lugar de itertools.ifilter(), utiliza filter().
  4. Línea 4: El módulo itertools aún existe en Python 3, solamente le faltan las funciones que han migrado al espacio global de nombres. 2to3 es suficientemente inteligente para eliminar los import específicos que han dejado de existir, conservando los demás.

A.39 sys.exc_type, sys.exc_value, sys.exc_traceback

Python 2 tiene tres variables en el módulo sys que puedes acceder mientras se está manejando una excepción: sys.exc_type, sys.exc_value, sys.exc_traceback (En realidad, se remontan a Python 1). Estas variables están deprecadas desde Python 1.5, en favor del uso de sys.exc_info(), que es una función que retorna una tupla que contiene los tres valores. En Python 3, estas variables individuales por fin desaparecen; debes usar obligatoriamente la función.


Línea Python 2 Python 3
  sys.exc_type sys.exc_info()[0]
  sys.exc_value sys.exc_info()[1]
  sys.exc_traceback sys.exc_info()[2]

A.40 Listas por comprensión a partir de tuplas

En Python 2, si quieres codificar una lista por comprensión que a partir de una tupla, no necesitas poner paréntesis sobre los valores de la tupla. En Python 3, es necesario explicitarlos.


Línea Python 2 Python 3
  [i for i in 1, 2] [i for i in (1, 2)]

A.41 La función os.getcwdu()

Python 2 tiene una función denominada os.getcwd(), que devuelve el directorio de trabajo actual como una cadena de caracteres (no Unicode). Puesto que los sistemas de ficheros modernos pueden manejar nombres en cualquier codificación de caracteres, Python 2.3 introdujo la función os.getcwdu() que devuelve el directorio de trabajo actual codificado en una cadena de caracteres Unicode. En Python 3, solamente existe un tipo de cadena de caracteres (es Unicode), por eso solamente existe os.getcwd().


Línea Python 2 Python 3
  os.getcwdu() os.getcwd()

A.42 Metaclases

En Python 2, podías crear metaclases, bien definiendo el parámetros metaclass en la declaración de la clase, o bien, definiendo un atributo especial de clase __metaclass__. En Python 3, el atributo de clase ha sido eliminado.


Línea Python 2 Python 3
1 class C(metaclass=PapayaMeta): no cambia
      pass
     
2 class Whip: class Whip(metaclass=PapayaMeta):
      __metaclass__     pass
     
3 class C(Whipper, Beater): class C(Whipper, Beater,
  __metaclass__ = PapayaMeta         metaclass=PapayaMeta):
           pass

  1. Línea 1: Declarar la metaclase en la declaración de clase funciona tanto en Python 2, como en Python 3.
  2. Línea 2: Declarar la metaclase en un atributo de clase funcionaba en Python 2, pero no en Python 3.
  3. Línea 3: 2to3 es lo suficientemente inteligente como para construir una declaración de clase válida, incluso si la clase hereda de otras clases.

A.43 Temas de estilo

El resto de los ``arreglos'' listados aquí, realmente no lo son. Son cosas que 2to3 cambia como mejora de estilo. Funcionan igual ambas soluciones tanto en Python 2 como en Python 3, pero los desarrolladores tienen interés en que el código fuente de Python sea lo más homogéneo posible en cuanto a estilo. Por ello, existe una
hrefhttp://www.python.org/dev/peps/pep-0008/Guía oficial de estilo de Python que describe --con todo lujo de detalles-- toda clase de reglas de escritura que, seguramente, no te interesan.

A.43.1 Literales set()

En Python 2, la única forma de definir un conjunto con literales en el código es llamar a set(unaSecuencia). Esto sigue funcionando en Python 3, pero una forma más clara de hacerlo es utilizar la nueva notación de literal de conjuntos: las llaves. Esto sirve para todos los conjuntos, excepto para los vacíos, ya que los diccionarios también utilizan las llaves, y {} es el conjunto vacío.

2to3, por defecto, no arregla literales set(). Para activar esta modificación, debes especificar -f set_literal en la línea de comando de 2to3.


Línea Python 2 Python 3
  set([1, 2, 3]) {1, 2, 3}
  set((1, 2, 3)) {1, 2, 3}
  [i for i in unaSecuencia] {i for in in unaSecuencia}

A.43.2 La función global buffer()

Los objetos Python implementados en C pueden exportar un ``interfaz de buffer'', que permite al código Python leer y escribir directamente en los bloques de memoria del objeto. En Python 3, buffer() se ha redenominado a memoryview() (Es más complicado que esto, pero puedes ignorar las diferencias).

Para que 2to3 aplique este cambio hay que especificar el parámetro -f buffer en la línea de comando.


Línea Python 2 Python 3
  x = buffer(y) x = memoryview(y)

A.43.3 Espacios en blanco alrededor de comas

A pesar de ser draconiano sobre el uso del espacio en blanco para la indentación, Python es bastante liberal sobre su uso en otras areas. En listas, tuplas, conjuntos y diccionarios, el espacio en blanco puede aparecer antes y después de las comas sin que ello provoque error. Sin embargo, la recomendación de la guía de estilo es que las comas no deben tener espacios en blanco delante de ellas y deben ir seguidas por uno. Aunque se trata de un tema puramente estético, 2to2 puede arreglarlo por ti.

Para habilitar esta opción debes especificar el parámetro -f wscomma en la línea de comando de 2to3


Línea Python 2 Python 3
  a ,b a, b
  {a :b} {a: b}

A.43.4 Idiomatismos habituales

Hay un cierto número de idiomatismos que toda la comunidad Python usa. Algunos, como el bucle while 1:, provienen de Python 1 (Python no tuvo tipo booleano hasta la versión 2.3, así que los desarrolladores usaban 1 y 0 en su lugar). Los programadores actuales de Python deberían entrenar sus cerebros para usar las versiones modernas de estos idiomatismos.

Para habilitar esta opción se debe especificar -f idioms en la línea de comando de 2to3.


Línea Python 2 Python 3
  while 1: while True:
      hacerCosas()     hacerCosas()
     
  type(x) == T isinstance(x, T)
  type(x) is T isinstance(x, T)
     
  unaLista = list(unaSecuencia) unaLista = sorted(unaSecuencia)
  unaLista.sort() hacerCosas(unaLista)
  hacerCosas(unaLista)

José Miguel González Aguilera 2016-08-18