Preguntas frecuentes de programación
************************************


Preguntas generales
===================


¿Existe un depurador a nivel de código fuente con puntos de interrupción, depuración paso a paso, etc?
------------------------------------------------------------------------------------------------------

Sí.

Debajo se describen algunos depuradores para Python y la función
integrada "breakpoint()" te permite ejecutar alguno de ellos.

El módulo pdb es en depurador en modo consola simple pero conveniente
para Python. Es parte de la biblioteca estándar de Python y está
"documentado en el manual de referencia de la biblioteca". Puedes
escribir tu propio depurador usando el código de pdb como ejemplo.

El entorno interactivo de desarrollo IDLE, el cual es parte de la
distribución Python estándar (disponible, generalmente,  como
Tools/scripts/idle), incluye un depurador gráfico.

PythonWin is a Python IDE that includes a GUI debugger based on pdb.
The PythonWin debugger colors breakpoints and has quite a few cool
features such as debugging non-PythonWin programs.  PythonWin is
available as part of pywin32 project and as a part of the ActivePython
distribution.

Eric es un IDE creado usando PyQt y el componente de edición
Scintilla.

trepan3k is a gdb-like debugger.

Visual Studio Code is an IDE with debugging tools that integrates with
version-control software.

Existen varios IDEs comerciales para Python que incluyen depuradores
gráficos. Entre ellos tenemos:

* Wing IDE

* Komodo IDE

* PyCharm


Are there tools to help find bugs or perform static analysis?
-------------------------------------------------------------

Sí.

Pylint and Pyflakes do basic checking that will help you catch bugs
sooner.

Inspectores estáticos de tipos como Mypy, Pyre, y Pytype pueden hacer
comprobaciones de las anotaciones de tipos en código fuente Python.


¿Cómo puedo crear un binario independiente a partir de un programa Python?
--------------------------------------------------------------------------

No necesitas tener la habilidad de compilar Python a código C si lo
único que necesitas es un programa independiente que los usuarios
puedan descargar y ejecutar sin necesidad de instalar primero una
distribución Python. Existe una serie de herramientas que determinan
el conjunto de módulos que necesita un programa y une estos módulos
conjuntamente con un binario Python para generar un único ejecutable.

Una forma es usando la herramienta *freeze*, la cual viene incluida
con el árbol de código Python como "Tools/freeze". Convierte el byte
code Python a arrays C; un compilador C permite incrustar todos tus
módulos en un nuevo programa que, posteriormente se puede enlazar con
los módulos estándar de Python.

Funciona escaneando su fuente de forma recursiva en busca de
declaraciones de importación (en ambas formas) y buscando los módulos
en la ruta estándar de Python, así como en el directorio de la fuente
(para los módulos incorporados).  Luego convierte el *bytecode* de los
módulos escritos en Python en código C (inicializadores de arrays que
pueden ser convertidos en objetos de código usando el módulo marshal)
y crea un archivo de configuración a medida que sólo contiene aquellos
módulos incorporados que se usan realmente en el programa.  A
continuación, compila el código C generado y lo enlaza con el resto
del intérprete de Python para formar un binario autónomo que actúa
exactamente igual que su script.

Obviously, freeze requires a C compiler.  There are several other
utilities which don't:

* py2exe for Windows binaries

* py2app for Mac OS X binaries

* cx_Freeze for cross-platform binaries


¿Existen estándares de código o una guía de estilo para programas Python?
-------------------------------------------------------------------------

Sí. El estilo de código requerido para los módulos de la biblioteca
estándar se encuentra documentado como **PEP 8**.


Núcleo del lenguaje
===================


¿Por qué obtengo un *UnboundLocalError* cuando la variable tiene un valor?
--------------------------------------------------------------------------

Puede ser una sorpresa el hecho de obtener un UnboundLocalError en
código que había estado funcionando previamente cuando se modifica
mediante el añadido de una declaración de asignación en alguna parte
del cuerpo de una función.

Este código:

>>> x = 10
>>> def bar():
...     print(x)
>>> bar()
10

funciona, pero este código:

>>> x = 10
>>> def foo():
...     print(x)
...     x += 1

resulta en un UnboundLocalError:

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment

Esto es debido a que cuando realizas una asignación a una variable en
un  ámbito de aplicación, esa variable se convierte en local y
enmascara cualquier variable llamada de forma similar en un ámbito de
aplicación exterior. Desde la última declaración en foo asigna un
nuevo valor a "x", el compilador la reconoce como una variable local.
Consecuentemente, cuando el "print(x)" más próximo intenta mostrar la
variable local no inicializada se muestra un error.

En el ejemplo anterior puedes acceder al ámbito de aplicación exterior
a la variable declarándola como global:

>>> x = 10
>>> def foobar():
...     global x
...     print(x)
...     x += 1
>>> foobar()
10

Esta declaración explícita es necesaria de cara a recordarte que (a
diferencia de la situación superficialmente análoga con las variables
de clase e instancia) estás modificando el valor de la variable en un
ámbito de aplicación más externo:

>>> print(x)
11

Puedes hacer algo similar en un ámbito de aplicación anidado usando la
palabra clave "nonlocal":

>>> def foo():
...    x = 10
...    def bar():
...        nonlocal x
...        print(x)
...        x += 1
...    bar()
...    print(x)
>>> foo()
10
11


¿Cuáles son las reglas para las variables locales y globales en Python?
-----------------------------------------------------------------------

En Python, las variables que solo se encuentran referenciadas dentro
de una función son globales implícitamente.  Si a una variable se le
asigna un valor en cualquier lugar dentro del cuerpo de una función,
se asumirá que es local a no ser que explícitamente se la declare como
global.

Aunque, inicialmente, puede parecer sorprendente, un momento de
consideración permite explicar esto.  Por una parte, requerir "global"
para variables asignadas proporciona una barrera frente a efectos
secundarios indeseados.  Por otra parte, si "global" es requerido para
todas las referencias globales, deberás usar "global" en todo momento.
Deberías declarar como global cualquier referencia a una función
integrada o a un componente de un módulo importado. Este embrollo
arruinaría la utilidad de la declaración "global" para identificar los
efectos secundarios.


¿Por qué las funciones lambda definidas en un bucle con diferentes valores devuelven todas el mismo resultado?
--------------------------------------------------------------------------------------------------------------

Considera que usas un bucle *for* para crear unas pocas funciones
lambda (o, incluso, funciones normales), por ejemplo.:

   >>> squares = []
   >>> for x in range(5):
   ...     squares.append(lambda: x**2)

Lo siguiente proporciona una lista que contiene 5 funciones lambda que
calculan "x**2". Esperarías que, cuando se les invoca, retornaran,
respectivamente, "0", "1", "4", "9``y ``16". Sin embargo, cuando lo
ejecutes verás que todas devuelven "16":

   >>> squares[2]()
   16
   >>> squares[4]()
   16

Esto sucede porque "x" no es una función lambda local pero se
encuentra definida en un ámbito de aplicación externo y se accede
cuando la lambda es invocada --- no cuando ha sido definida.  Al final
del bucle, el valor de "x" es "4", por tanto, ahora todas las
funciones devuelven "4**2", i.e. "16". También puedes verificar esto
mediante el cambio del valor de "x" y ver como los resultados de las
lambdas cambian:

   >>> x = 8
   >>> squares[2]()
   64

De cara a evitar esto necesitas guardar los valores en variables
locales a las funciones lambda de tal forma que no dependan del valor
de la  "x" global:

   >>> squares = []
   >>> for x in range(5):
   ...     squares.append(lambda n=x: n**2)

Aquí, "n=x" crea una nueva variable "n" local a la función lambda y
ejecutada cuando la función lambda se define de tal forma que tiene el
mismo valor que tenía "x" en ese punto en el bucle.  Esto significa
que el valor de "n" será "0" en la primera función lambda, "1" en la
segunda, "2" en la tercera y así sucesivamente. Por tanto, ahora cada
lambda retornará el resultado correcto:

   >>> squares[2]()
   4
   >>> squares[4]()
   16

Es de destacar que este comportamiento no es peculiar de las funciones
lambda sino que aplica también a las funciones regulares.


¿Cómo puedo compartir variables globales entre módulos?
-------------------------------------------------------

La forma canónica de compartir información entre módulos dentro de un
mismo programa sería creando un módulo especial (a menudo llamado
config o cfg).  Simplemente importa el módulo config  en todos los
módulos de tu aplicación; el módulo estará disponible como un nombre
global.  Debido a que solo hay una instancia de cada módulo, cualquier
cambio hecho en el objeto módulo se reflejará en todos los sitios.
Por ejemplo:

config.py:

   x = 0   # Default value of the 'x' configuration setting

mod.py:

   import config
   config.x = 1

main.py:

   import config
   import mod
   print(config.x)

Ten en cuenta que usar un módulo es también la base para la
implementación del patrón de diseño Singleton, por la misma razón.


¿Cuáles son las "buenas prácticas" para usar import en un módulo?
-----------------------------------------------------------------

En general, no uses "from modulename import *".  Haciendo eso
embarulla el espacio de nombres del importador y hace que sea más
difícil para los *linters* el detectar los nombres sin definir.

Importar los módulos en la parte inicial del fichero. Haciéndolo así
deja claro los módulos que son necesarios para tu código y evita
preguntas sobre si el nombre del módulo se encuentra en el ámbito de
la aplicación. Usar una importación por línea hace que sea sencillo
añadir y eliminar módulos importados pero usar múltiples importaciones
por línea usa menos espacio de pantalla.

Es una buena práctica si importas los módulos en el orden siguiente:

1. módulos de la biblioteca estándar -- por ejemplo, "sys", "os",
   "getopt", "re"

2. módulos de bibliotecas de terceros (cualquier cosa instalada en el
   directorio *site-packages* de Python) -- por ejemplo, mx.DateTime,
   ZODB, PIL.Image, etc.

3. módulos desarrollados localmente

Hay veces en que es necesario mover las importaciones a una función o
clase para evitar problemas de importaciones circulares.  Gordon
McMillan dice:

   No hay problema con las importaciones circulares cuando ambos
   módulos usan la forma de importación "import <module>".  Fallará
   cuando el segundo módulo quiera coger un nombre del primer módulo
   ("from module import name") y la importación se encuentre en el
   nivel superior.  Esto sucede porque los nombres en el primero
   todavía no se encuentran disponibles debido a que el primer módulo
   se encuentra ocupado importando al segundo.

En este caso, si el segundo módulo se usa solamente desde una función,
la importación se puede mover de forma sencilla dentro de la función.
En el momento en que se invoca a la importación el primer módulo habrá
terminado de inicializarse y el segundo módulo podrá hacer la
importación.

También podría ser necesario mover importaciones fuera del nivel
superior del código si alguno de loa módulos son específicos a la
plataforma. En ese caso podría, incluso, no ser posible importar todos
los módulos en la parte superior del fichero. Para esos casos, la
importación correcta de los módulos en el código correspondiente
específico de la plataforma es una buena opción.

Solo debes mover importaciones a un ámbito de aplicación local, como
dentro de la definición de una función, si es necesario resolver
problemas como una importación circular o al intentar reducir el
tiempo de inicialización de un módulo. Esta técnica es especialmente
útil si muchas de las importaciones no son necesarias dependiendo de
cómo se ejecute el programa. También podrías mover importaciones a una
función si los módulos solo se usan dentro de esa función. Nótese que
la primera carga de un módulo puede ser costosa debido al tiempo
necesario para la inicialización del módulo,pero la carga de un módulo
múltiples veces está prácticamente libre de coste ya que solo es
necesario hacer búsquedas en un diccionario. Incluso si el nombre del
módulo ha salido del ámbito de aplicación el módulo se encuentre,
probablemente, en "sys.modules".


¿Por qué los valores por defecto se comparten entre objetos?
------------------------------------------------------------

Este tipo de error golpea a menudo a programadores novatos. Considera
esta función:

   def foo(mydict={}):  # Danger: shared reference to one dict for all calls
       ... compute something ...
       mydict[key] = value
       return mydict

La primera vez que llamas a esta función, "mydict" solamente contiene
un único elemento. La segunda vez, "mydict" contiene dos elementos
debido a que cuando comienza la ejecución de "foo()", "mydict"
comienza conteniendo un elemento de partida.

A menudo se esperaría que una invocación a una función cree nuevos
objetos para valores por defecto. Eso no es lo que realmente sucede.
Los valores por defecto se crean exactamente una sola vez, cuando se
define la función. Se se cambia el objeto, como el diccionario en este
ejemplo, posteriores invocaciones a la función estarán referidas al
objeto cambiado.

Por definición, los objetos inmutables como números, cadenas, tuplas y
"None" están asegurados frente al cambio. Cambios en objetos mutables
como diccionarios, listas e instancias de clase pueden llevar a
confusión.

Debido a esta característica es una buena práctica de programación el
no usar valores mutables como valores por defecto. En su lugar usa
"None" como valor por defecto dentro de la función, comprueba si el
parámetro es "None" y crea una nueva lista/un nuevo
diccionario/cualquier otras cosa que necesites.  Por ejemplo, no
escribas:

   def foo(mydict={}):
       ...

pero:

   def foo(mydict=None):
       if mydict is None:
           mydict = {}  # create a new dict for local namespace

Esta característica puede ser útil. Cuando tienes una función que es
muy costosa de ejecutar, una técnica común es *cachear* sus parámetros
y el valor resultante de cada invocación a la función y retornar el
valor *cacheado* si se solicita nuevamente el mismo valor.  A esto se
le llama "memoizing" y se puede implementar de la siguiente forma:

   # Callers can only provide two parameters and optionally pass _cache by keyword
   def expensive(arg1, arg2, *, _cache={}):
       if (arg1, arg2) in _cache:
           return _cache[(arg1, arg2)]

       # Calculate the value
       result = ... expensive computation ...
       _cache[(arg1, arg2)] = result           # Store result in the cache
       return result

Podrías usar una variable global conteniendo un diccionario en lugar
de un valor por defecto; es una cuestión de gustos.


¿Cómo puedo pasar parámetros por palabra clave u opcionales de una función a otra?
----------------------------------------------------------------------------------

Recopila los argumentos usando los especificadores "*" y "**" en la
lista de parámetros de la función; esto te proporciona los argumentos
posicionales como una tupla y los argumentos con palabras clave como
un diccionario.  Puedes, entonces, pasar estos argumentos cuando
invoques a otra función usando "*" y "**":

   def f(x, *args, **kwargs):
       ...
       kwargs['width'] = '14.3c'
       ...
       g(x, *args, **kwargs)


¿Cuál es la diferencia entre argumentos y parámetros?
-----------------------------------------------------

*Parámetros* se definen mediante los nombres que aparecen en la
definición de una función mientras que *argumentos* son los valores
que se pasan a la función cuando la invocamos.  Los Parámetros definen
qué tipos de argumentos puede aceptar una función.  por ejemplo, dada
la definición de la función:

   def func(foo, bar=None, **kwargs):
       pass

*foo*, *bar* y *kwargs* son parámetros de "func".  Sin embargo, cuando
invocamos a "func", por ejemplo:

   func(42, bar=314, extra=somevar)

los valores "42", "314" y "somevar" son argumentos.


¿Por qué cambiando la lista 'y' cambia, también, la lista 'x'?
--------------------------------------------------------------

Si escribes código como:

   >>> x = []
   >>> y = x
   >>> y.append(10)
   >>> y
   [10]
   >>> x
   [10]

te estarás preguntando porque añadir un elemento a "y" ha cambiado
también a "x".

Hay dos factores que provocan este resultado:

1. Las variables son simplemente nombres que referencian a objetos.
   Haciendo "y = x" no crea una copia de la lista -- crea una nueva
   variable "y" que referencia al mismo objeto al que referencia "x" .
   Esto significa que solo existe un objeto (la lista) y tanto "x"
   como "y" hacen referencia al mismo.

2. Las listas son *mutable*, lo que significa que puedes cambiar su
   contenido.

Después de la invocación a "append()", el contenido del objeto mutable
ha cambiado de "[]" a``[10]``.  Ya que ambas variables referencian al
mismo objeto, el usar cualquiera de los nombres accederá al valor
modificado "[10]".

Si, por otra parte, asignamos un objeto inmutable a "x":

   >>> x = 5  # ints are immutable
   >>> y = x
   >>> x = x + 1  # 5 can't be mutated, we are creating a new object here
   >>> x
   6
   >>> y
   5

podemos ver que "x" e "y" ya no son iguales.  Esto es debido a que los
enteros son *immutable*, y cuando hacemos "x = x + 1" no estamos
mutando el entero "5" incrementando su valor; en su lugar, estamos
creando un nuevo objeto (el entero "6") y se lo asignamos a "x" (esto
es, cambiando el objeto al cual referencia "x").  Después de esta
asignación tenemos dos objetos (los enteros "6" y "5") y dos variables
que referencian a ellos ("x" ahora referencia a  "6" pero "y" todavía
referencia a "5").

Algunas operaciones (por ejemplo "y.append(10)" y "y.sort()") mutan al
objeto mientras que operaciones que podrían parecer similares (por
ejemplo "y = y + [10]" y "sorted(y)") crean un nuevo objeto.  En
general, en Python (y en todo momento en la biblioteca estándar) un
método que muta un objeto retornará "None" para evitar tener dos tipos
de operaciones que puedan ser confusas.  Por tanto, si escribes
accidentalmente "y.sort()" pensando que te retornará una copia
ordenada de "y", obtendrás, en su lugar, "None", lo cual ayudará a que
tu programa genera un error que pueda ser diagnosticado fácilmente.

Sin embargo, existe una clase de operaciones en las cuales la misma
operación tiene, a veces, distintos comportamientos con diferentes
tipos:  los operadores de asignación aumentada.  Por ejemplo, "+="
muta listas pero no tuplas o enteros ("a_list += [1, 2, 3]" es
equivalente a "a_list.extend([1, 2, 3])" y muta "a_list", mientras que
"some_tuple += (1, 2, 3)" y "some_int += 1" crea nuevos objetos).

En otras palabras:

* Si tenemos un objeto mutable ("list", "dict", "set", etc.), podemos
  usar algunas operaciones específicas para mutarlo y todas las
  variables que referencian al mismo verán el cambio reflejado.

* Si tenemos un objeto inmutable ("str", "int", "tuple", etc.), todas
  las variables que referencian al mismo verán siempre el mismo valor
  pero las operaciones que transforman ese valor en un nuevo valor
  siempre retornan un nuevo objeto.

Si deseas saber si dos variables referencian o no al mismo objeto
puedes usar el operador "is" o la función incorporada "id()".


¿Cómo puedo escribir una función sin parámetros (invocación mediante referencia)?
---------------------------------------------------------------------------------

Recuerda que los argumentos son pasados mediante asignación en Python.
Ya que las asignaciones simplemente crean referencias a objetos, no
hay alias entre el nombre de un argumento en el invocador y el
invocado y, por tanto, no hay invocación por referencia per se.
Puedes obtener el mismo efecto deseado de formas distintas.

1. Mediante el retorno de una tupla de resultados:

      >>> def func1(a, b):
      ...     a = 'new-value'        # a and b are local names
      ...     b = b + 1              # assigned to new objects
      ...     return a, b            # return new values
      ...
      >>> x, y = 'old-value', 99
      >>> func1(x, y)
      ('new-value', 100)

   Esta es, casi siempre, la solución más clara.

2. Mediante el uso de variables globales.  No es thread-safe y no se
   recomienda.

3. Pasando un objeto mutable (intercambiable en el mismo sitio):

      >>> def func2(a):
      ...     a[0] = 'new-value'     # 'a' references a mutable list
      ...     a[1] = a[1] + 1        # changes a shared object
      ...
      >>> args = ['old-value', 99]
      >>> func2(args)
      >>> args
      ['new-value', 100]

4. Pasando un diccionario que muta:

      >>> def func3(args):
      ...     args['a'] = 'new-value'     # args is a mutable dictionary
      ...     args['b'] = args['b'] + 1   # change it in-place
      ...
      >>> args = {'a': 'old-value', 'b': 99}
      >>> func3(args)
      >>> args
      {'a': 'new-value', 'b': 100}

5. O empaquetar valores en una instancia de clase:

      >>> class Namespace:
      ...     def __init__(self, /, **args):
      ...         for key, value in args.items():
      ...             setattr(self, key, value)
      ...
      >>> def func4(args):
      ...     args.a = 'new-value'        # args is a mutable Namespace
      ...     args.b = args.b + 1         # change object in-place
      ...
      >>> args = Namespace(a='old-value', b=99)
      >>> func4(args)
      >>> vars(args)
      {'a': 'new-value', 'b': 100}

   Casi nunca existe una buena razón para hacer esto tan complicado.

Tu mejor opción es retornar una tupla que contenga los múltiples
resultados.


¿Cómo se puede hacer una función de orden superior en Python?
-------------------------------------------------------------

Tienes dos opciones: puedes usar ámbitos de aplicación anidados o
puedes usar objetos invocables. Por ejemplo, supón que querías definir
"linear(a,b)" que devuelve una función "f(x)" que calcula el valor
"a*x+b".  Usar ámbitos de aplicación anidados:

   def linear(a, b):
       def result(x):
           return a * x + b
       return result

O usar un objeto invocable:

   class linear:

       def __init__(self, a, b):
           self.a, self.b = a, b

       def __call__(self, x):
           return self.a * x + self.b

En ambos casos,

   taxes = linear(0.3, 2)

nos da un objeto invocable donde "taxes(10e6) == 0.3 * 10e6 + 2".

El enfoque del objeto invocable tiene la desventaja que es un
ligeramente más lento y el resultado es un código levemente más largo.
Sin embargo, destacar que una colección de invocables pueden compartir
su firma vía herencia:

   class exponential(linear):
       # __init__ inherited
       def __call__(self, x):
           return self.a * (x ** self.b)

Los objetos pueden encapsular el estado de varios métodos:

   class counter:

       value = 0

       def set(self, x):
           self.value = x

       def up(self):
           self.value = self.value + 1

       def down(self):
           self.value = self.value - 1

   count = counter()
   inc, dec, reset = count.up, count.down, count.set

Aquí "inc()", "dec()" y "reset()" se comportan como funciones las
cuales comparten la misma variable de conteo.


¿Cómo copio un objeto en Python?
--------------------------------

En general, prueba "copy.copy()" o "copy.deepcopy()" para el caso
general. No todos los objetos se pueden copiar pero la mayoría sí que
pueden copiarse.

Algunas objetos se pueden copiar de forma más sencilla. Los
diccionarios disponen de un método "copy()":

   newdict = olddict.copy()

Las secuencias se pueden copiar usando un rebanado:

   new_l = l[:]


¿Cómo puedo encontrar los métodos o atributos de un objeto?
-----------------------------------------------------------

Para la instancia x de una clase definida por el usuario, "dir(x)"
devuelve una lista de nombres ordenados alfabéticamente que contiene
los atributos y métodos de la instancia y los atributos definidos
mediante su clase.


¿Cómo puede mi código descubrir el nombre de un objeto?
-------------------------------------------------------

Hablando de forma general no podrían puesto que los objetos no
disponen, realmente, de un nombre. Esencialmente, las asignaciones
relacionan un nombre con su valor; Lo mismo se cumple con las
declaraciones "def" y "class" pero, en este caso, el valor es un
invocable. Considera el siguiente código:

   >>> class A:
   ...     pass
   ...
   >>> B = A
   >>> a = B()
   >>> b = a
   >>> print(b)
   <__main__.A object at 0x16D07CC>
   >>> print(a)
   <__main__.A object at 0x16D07CC>

Podría decirse que la clase tiene un nombre: aunque está ligada a dos
nombres y se invoca a través del nombre B, la instancia creada se
sigue reportando como una instancia de la clase A. Sin embargo, es
imposible decir si el nombre de la instancia es a o b, ya que ambos
nombres están ligados al mismo valor.

En términos generales, no debería ser necesario que tu código "conozca
los nombres" de determinados valores. A menos que estés escribiendo
deliberadamente programas introspectivos, esto suele ser una
indicación de que un cambio de enfoque podría ser beneficioso.

En comp.lang.python, Fredrik Lundh proporcionó una vez una excelente
analogía en respuesta a esta pregunta:

   De la misma forma que obtienes el nombre de ese gato que te has
   encontrado en tu porche el propio gato (objeto) no te puede indicar
   su nombre y, realmente, no importa -- por tanto, la única forma de
   encontrar cómo se llama sería preguntando a todos los vecinos
   (espacios de nombres) si es su gato (objeto)...

   ...y no te sorprendas si encuentras que se le conoce mediante
   diferentes nombres o ¡nadie conoce su nombre!


¿Qué ocurre con la precedencia del operador coma?
-------------------------------------------------

La coma no es un operador en Python. Considera la sesión:

   >>> "a" in "b", "a"
   (False, 'a')

Debido a que la coma no es un operador sino un  separador entre
expresiones lo anterior se evalúe como se ha introducido:

   ("a" in "b"), "a"

no:

   "a" in ("b", "a")

Lo mismo sucede con varios operadores de asignación ("=", "+=", etc).
No son realmente operadores sino delimitadores sintácticos en
declaraciones de asignación.


¿Existe un equivalente al operador ternario de C "?:"?
------------------------------------------------------

Sí, existe. La sintaxis es como sigue:

   [on_true] if [expression] else [on_false]

   x, y = 50, 25
   small = x if x < y else y

Antes de que esta sintaxis se introdujera en Python 2.5 una expresión
común fue el uso de operadores lógicos:

   [expression] and [on_true] or [on_false]

Sin embargo, esa expresión no es segura ya que puede retornar valores
erróneos cuando *on_true* tiene un valor booleano falso.  Por tanto,
siempre es mejor usar la forma "... if ... else ...".


¿Es posible escribir expresiones en una línea de forma ofuscada en Python?
--------------------------------------------------------------------------

Sí.  Normalmente se puede hacer anidando "lambda" dentro de "lambda".
Examina los siguientes tres ejemplos, creados por Ulf Bartelt:

   from functools import reduce

   # Primes < 1000
   print(list(filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,
   map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000)))))

   # First 10 Fibonacci numbers
   print(list(map(lambda x,f=lambda x,f:(f(x-1,f)+f(x-2,f)) if x>1 else 1:
   f(x,f), range(10))))

   # Mandelbrot set
   print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,
   Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,
   Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,
   i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y
   >=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(
   64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy
   ))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24))
   #    \___ ___/  \___ ___/  |   |   |__ lines on screen
   #        V          V      |   |______ columns on screen
   #        |          |      |__________ maximum of "iterations"
   #        |          |_________________ range on y axis
   #        |____________________________ range on x axis

¡No probéis esto en casa, personitas!


¿Qué hace la barra (/) en medio de la lista de parámetros de una función?
-------------------------------------------------------------------------

Un *slash* en la lista de argumentos de una función denota que los
parámetros previos al mismo son únicamente posicionales.  Parámetros
únicamente posicionales son aquellos cuyos nombres no son usables
internamente.  Mediante la llamada a una función que acepta parámetros
únicamente posicionales, los argumentos se mapean a parámetros basados
únicamente en su posición. Por ejemplo, "pow()" es una función que
acepta parámetros únicamente posicionales. Su documentación es de la
siguiente forma:

   >>> help(divmod)
   Help on built-in function divmod in module builtins:

   divmod(x, y, /)
       Return the tuple (x//y, x%y).  Invariant: div*y + mod == x.

El *slash* al final de la lista de parámetros indica que los tres
parámetros son únicamente posicionales. Por tanto, invocar a  "pow()"
con argumentos con palabra clave podría derivar en un error:

   >>> divmod(x=3, y=4)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: divmod() takes no keyword arguments


Números y cadenas
=================


¿Cómo puedo especificar enteros hexadecimales y octales?
--------------------------------------------------------

Para especificar un dígito octal, prefija el valor octal con un cero y
una "o" en minúscula o mayúscula.  Por ejemplo, para definir la
variable "a" con el valor octal "10" (8 en decimal), escribe:

   >>> a = 0o10
   >>> a
   8

Un hexadecimal es igual de simple.  Simplemente añade un cero y una
"x", en minúscula o mayúscula, antes del número hexadecimal . Los
dígitos hexadecimales se pueden especificar en minúsculas o
mayúsculas.  Por ejemplo, en el intérprete de Python:

   >>> a = 0xa5
   >>> a
   165
   >>> b = 0XB2
   >>> b
   178


¿Por qué -22 // 10 devuelve -3?
-------------------------------

Es debido, principalmente al deseo que "i % j" tenga el mismo signo
que "j". Si quieres eso y, además, quieres:

   i == (i // j) * j + (i % j)

entonces la división entera debe retornar el valor base más bajo.  C
también requiere que esa esa identidad se mantenga de tal forma que
cuando los compiladores truncan "i // j" necesitan que "i % j" tenga
el mismo signo que "i".

Existen unos pocos casos para "i % j" cuando "j" es negativo.  Cuando
"j" es positivo, existen muchos casos y, virtualmente, en todos ellos
es más útil para "i % j" que sea ">= 0".  Si el reloj dice que ahora
son las 10, ¿qué dijo hace 200 horas?  "-190 % 12 == 2" es útil; "-190
% 12 == -10" es un error listo para morderte.


¿Cómo convierto una cadena a un número?
---------------------------------------

Para enteros puedes usar la función incorporada constructor de tipos
"int()", por ejemplo "int('144') == 144".  De forma similar, "float()"
convierte a un número de coma flotante, por ejemplo "float('144') ==
144.0".

Por defecto, estas interpretan el número como decimal de tal forma que
"int('0144') == 144" y "int('0x144')" lanzará "ValueError".
"int(string, base)" toma la base para convertirlo desde un segundo
parámetro opcional, por tanto "int('0x144', 16) == 324".  Si la base
se especifica como  0, el número se interpreta usando las reglas de
Python's rules: un prefijo '0o' indica octal y un prefijo '0x' indica
un número hexadecimal.

No uses la función incorporada "eval()" si todo lo que necesitas es
convertir cadenas a números.  "eval()" será considerablemente más
lento y presenta riesgos de seguridad: cualquiera podría introducir
una expresión Python que presentara efectos indeseados.  Por ejemplo,
alguien podría pasar``__import__('os').system("rm -rf $HOME")`` lo
cual borraría el directorio home al completo.

"eval()" también tiene el efecto de interpretar números como
expresiones Python , de tal forma que, por ejemplo, "eval('09')" dará
un error de sintaxis porque Python no permite un '0' inicial en un
número decimal (excepto '0').


¿Cómo puedo convertir un número a una cadena?
---------------------------------------------

Para convertir, por ejemplo, el número 144 a la cadena '144', usa el
constructor de tipos incorporado "str()".  Si deseas una
representación hexadecimal o octal usa la función incorporada "hex()"
o "oct()".  Para un formateado elaborado puedes ver las secciones de
Literales de cadena formateados y Sintaxis de formateo de cadena, por
ejemplo ""{:04d}".format(144)" produce "'0144'" y
""{:.3f}".format(1.0/3.0)" produce "'0.333'".


¿Cómo puedo modificar una cadena in situ?
-----------------------------------------

No puedes debido a que las cadenas son inmutables.  En la mayoría de
situaciones solo deberías crear una nueva cadena a partir de varias
partes que quieras usar para crearla.  Sin embargo, si necesitas un
objeto con la habilidad de modificar en el mismo lugar datos unicode
prueba usando el objeto "io.StringIO" o el módulo "array":

   >>> import io
   >>> s = "Hello, world"
   >>> sio = io.StringIO(s)
   >>> sio.getvalue()
   'Hello, world'
   >>> sio.seek(7)
   7
   >>> sio.write("there!")
   6
   >>> sio.getvalue()
   'Hello, there!'

   >>> import array
   >>> a = array.array('u', s)
   >>> print(a)
   array('u', 'Hello, world')
   >>> a[0] = 'y'
   >>> print(a)
   array('u', 'yello, world')
   >>> a.tounicode()
   'yello, world'


¿Cómo puedo usar cadenas para invocar funciones/métodos?
--------------------------------------------------------

Existen varias técnicas.

* Lo mejor sería usar un diccionario que mapee cadenas a funciones.
  La principal ventaja de esta técnica es que las cadenas no necesitan
  ser iguales que los nombres de las funciones.  Esta es también la
  principal técnica que se usa para emular un constructo *case*:

     def a():
         pass

     def b():
         pass

     dispatch = {'go': a, 'stop': b}  # Note lack of parens for funcs

     dispatch[get_input()]()  # Note trailing parens to call function

* Usa la función incorporada "getattr()":

     import foo
     getattr(foo, 'bar')()

  Nótese que "getattr()" funciona en cualquier objeto, incluido
  clases, instancias de clases, módulos, etc.

  Esto se usa en varios lugares de la biblioteca estándar, como esto:

     class Foo:
         def do_foo(self):
             ...

         def do_bar(self):
             ...

     f = getattr(foo_instance, 'do_' + opname)
     f()

* Usa "locals()" o "eval()" para resolver el nombre de la función:

     def myFunc():
         print("hello")

     fname = "myFunc"

     f = locals()[fname]
     f()

     f = eval(fname)
     f()

  Nota: Usar "eval()" es lento y peligroso.  Si no tienes el control
  absoluto del contenido de la cadena cualquiera podría introducir una
  cadena que resulte en la ejecución de código arbitrario.


¿Existe un equivalente a chomp() en Perl para eliminar nuevas líneas al final de las cadenas?
---------------------------------------------------------------------------------------------

Puedes usar "S.rstrip("\r\n")" para eliminar todas las ocurrencias de
cualquier terminación de línea desde el final de la cadena "S" sin
eliminar el resto de espacios en blanco que le siguen. Si la cadena
"S" representa más de una línea con varias líneas vacías al final, las
terminaciones de línea para todas las líneas vacías se eliminarán:

   >>> lines = ("line 1 \r\n"
   ...          "\r\n"
   ...          "\r\n")
   >>> lines.rstrip("\n\r")
   'line 1 '

Ya que esto solo sería deseable, típicamente, cuando lees texto línea
a línea, usar "S.rstrip()" de esta forma funcionaría bien.


¿Existe un equivalente a scanf() o a sscanf() ?
-----------------------------------------------

No de la misma forma.

Para análisis sintáctico simple de la entrada, el método más sencillo
es, usualmente, el separar la línea en palabras delimitadas por
espacios usando el método "split()" de los objetos *string* y,
posteriormente, convertir cadenas decimales a valores usando "int()" o
"float()".  "split()" permite un parámetro opcional "sep" que es útil
si la línea usa algo diferente a espacios en blanco como separador.

Para análisis sintáctico de la entrada más complejo, las expresiones
regulares son más poderosas que "sscanf()" de C  y se ajustan mejor a
esta tarea.


¿Qué significa 'UnicodeDecodeError' o 'UnicodeEncodeError'?
-----------------------------------------------------------

Ver CÓMO (HOWTO) Unicode.


Rendimiento
===========


Mi programa es muy lento. ¿Cómo puedo acelerarlo?
-------------------------------------------------

Esa es una pregunta difícil, en general.  Primero, aquí tienes una
lista de cosas a recordar antes de ir más allá:

* Las características del rendimiento varían entre las distintas
  implementaciones de Python.  Estas preguntas frecuentes se enfocan
  en *CPython*.

* El comportamiento puede variar entre distintos sistemas operativos,
  especialmente cuando se habla de tareas I/O o multi-tarea.

* Siempre deberías encontrar las partes importantes en tu programa
  *antes* de intentar optimizar el código (ver el módulo "profile").

* Escribir programas de comparación del rendimiento te permitirá
  iterar rápidamente cuando te encuentres buscando mejoras (ver el
  módulo "timeit").

* Es altamente recomendable disponer de una buena cobertura de código
  (a partir de pruebas unitarias o cualquier otra técnica) antes de
  introducir potenciales regresiones ocultas en sofisticadas
  optimizaciones.

Dicho lo anterior, existen muchos trucos para acelerar código Python.
Aquí tienes algunos principios generales que te permitirán llegar a
alcanzar niveles de rendimiento aceptables:

* El hacer más rápido tu algoritmo (o cambiarlo por alguno más rápido)
  puede provocar mayores beneficios que intentar unos pocos trucos de
  micro-optimización a través de todo tu código.

* Utiliza las estructuras de datos correctas.  Estudia la
  documentación para los Tipos Integrados y el módulo "collections".

* Cuando la biblioteca estándar proporciona una primitiva de hacer
  algo, esta supuestamente será (aunque no se garantiza) más rápida
  que cualquier otra alternativa que se te ocurra.  Esto es doblemente
  cierto si las primitivas han sido escritas en C, como los *builtins*
  y algunos tipos extendidos.  Por ejemplo, asegúrate de usar el
  método integrado "list.sort()" o la función relacionada "sorted()"
  para ordenar (y ver HOW TO - Ordenar para ver ejemplos de uso
  moderadamente avanzados).

* Las abstracciones tienden a crear rodeos y fuerzan al intérprete a
  trabajar más.  Si el nivel de rodeos sobrepasa el trabajo útil
  realizado tu programa podría ser más lento.  Deberías evitar
  abstracciones excesivas, especialmente, en forma de pequeñas
  funciones o métodos (que también va en detrimento de la
  legibilidad).

Si has alcanzado el límite  de lo que permite el uso de Python puro,
existen otras herramientas que te permiten ir más allá.  Por ejemplo,
Cython puede compilar una versión ligeramente modificada del código
Python en una extensión en C y se podría usar en muchas plataformas
diferentes.  Cython puede obtener ventaja de la compilación (y
anotaciones de tipos opcionales) para hacer que tu código sea
significativamente más rápido cuando se ejecuta.  Si confías en tus
habilidades de programar en C también puedes escribir un módulo de
extensión en C tú mismo.

Ver también: La página de la wiki dedicada a trucos de rendimiento.


¿Cuál es la forma más eficiente de concatenar muchas cadenas conjuntamente?
---------------------------------------------------------------------------

Los objetos "str" y "bytes" son inmutables, por tanto, concatenar
muchas cadenas en una sola es ineficiente debido a que cada
concatenación crea un nuevo objeto. En el caso más general, el coste
total en tiempo de ejecución es cuadrático en relación a la longitud
de la cadena final.

Para acumular muchos objetos "str", la forma recomendada sería
colocarlos en una lista y llamar al método "str.join()" al final:

   chunks = []
   for s in my_strings:
       chunks.append(s)
   result = ''.join(chunks)

(otra forma que sería razonable en términos de eficiencia sería usar
"io.StringIO")

Para acumular muchos objetos "bytes", la forma recomendada sería
extender un objeto "bytearray" usando el operador de concatenación
in situ (el operador "+="):

   result = bytearray()
   for b in my_bytes_objects:
       result += b


Secuencias (Tuplas/Listas)
==========================


¿Cómo convertir entre tuplas y listas?
--------------------------------------

El constructor "tuple(seq)" convierte cualquier secuencia (en
realidad, cualquier iterable) en una tupla con los mismos elementos y
en el mismo orden.

Por ejemplo, "tuple([1, 2, 3])" lo convierte en "(1, 2, 3)" y
"tuple('abc')" lo convierte en "('a', 'b', 'c')".  Si el argumento es
una tupla no creará una nueva copia y retornará el mismo objeto, por
tanto, llamar a "tuple()" no tendrá mucho coste si no estás seguro si
un objeto ya es una tupla.

El constructor "list(seq)" convierte cualquier secuencia o iterable en
una lista con los mismos elementos y en el mismo orden.  Por ejemplo,
"list((1, 2, 3))" lo convierte a "[1, 2, 3]" y "list('abc')" lo
convierte a "['a', 'b', 'c']".  Si el argumento es una lista, hará una
copia como lo haría "seq[:]".


¿Qué es un índice negativo?
---------------------------

Las secuencias en Python están indexadas con números positivos y
negativos.  Para los números positivos el  0 será el primer índice, el
1 el segundo y así en adelante.  Para los índices negativos el -1 el
último índice, el -2 el penúltimo, etc.  Piensa en "seq[-n]" como si
fuera "seq[len(seq)-n]".

El uso de índices negativos puede ser muy conveniente.  Por ejemplo
"S[:-1]" se usa para todo la cadena excepto para su último carácter,
lo cual es útil para eliminar el salto de línea final de una cadena.


¿Cómo puedo iterar sobre una secuencia en orden inverso?
--------------------------------------------------------

Use the "reversed()" built-in function:

   for x in reversed(sequence):
       ...  # do something with x ...

Esto no transformará la secuencia original sino que creará una nueva
copia en orden inverso por la que se puede iterar.


¿Cómo eliminar duplicados de una lista?
---------------------------------------

Puedes echar un vistazo al recetario de Python para ver una gran
discusión mostrando muchas formas de hacer esto:

   https://code.activestate.com/recipes/52560/

Si no te preocupa que la lista se reordene la puedes ordenar y,
después, y después escanearla desde el final borrando duplicados a
medida que avanzas:

   if mylist:
       mylist.sort()
       last = mylist[-1]
       for i in range(len(mylist)-2, -1, -1):
           if last == mylist[i]:
               del mylist[i]
           else:
               last = mylist[i]

Si todos los elementos de la lista pueden ser usados como claves (por
ejemplo son todos *hashable*) esto será, en general, más rápido

   mylist = list(set(mylist))

Esto convierte la lista en un conjunto eliminando, por tanto, los
duplicados y, posteriormente, puedes volver a una lista.


How do you remove multiple items from a list
--------------------------------------------

As with removing duplicates, explicitly iterating in reverse with a
delete condition is one possibility.  However, it is easier and faster
to use slice replacement with an implicit or explicit forward
iteration. Here are three variations.:

   mylist[:] = filter(keep_function, mylist)
   mylist[:] = (x for x in mylist if keep_condition)
   mylist[:] = [x for x in mylist if keep_condition]

The list comprehension may be fastest.


¿Cómo se puede hacer un array en Python?
----------------------------------------

Usa una lista:

   ["this", 1, "is", "an", "array"]

Las listas son equivalentes en complejidad temporal a arrays en C o
Pascal; La principal diferencia es que una lista en Python puede
contener objetos de diferentes tipos.

El módulo "array" proporciona, también, métodos para crear arrays de
tipo fijo con representaciones compactas pero son más lentos de
indexar que las listas.  Además, debes tener en cuenta que las
extensiones Numeric y otras permiten definen estructuras de tipo array
con diversas características.

Para obtener listas enlazadas al estilo de las de Lisp, puedes emular
celdas cons usando tuplas:

   lisp_list = ("like",  ("this",  ("example", None) ) )

Si deseas que haya mutabilidad podrías usar listas en lugar de tuplas.
El análogo a un car de Lisp es "lisp_list[0]" y al análogo a cdr es
"lisp_list[1]".  Haz esto solo si estás seguro que es lo que necesitas
debido a que, normalmente, será bastante más lento que el usar listas
Python.


¿Cómo puedo crear una lista multidimensional?
---------------------------------------------

Seguramente hayas intentado crear un array multidimensional de la
siguiente forma:

   >>> A = [[None] * 2] * 3

Esto parece correcto si lo muestras en pantalla:

   >>> A
   [[None, None], [None, None], [None, None]]

Pero cuando asignas un valor, se muestra en múltiples sitios:

   >>> A[0][0] = 5
   >>> A
   [[5, None], [5, None], [5, None]]

La razón es que replicar una lista con "*" no crea copias, solo crea
referencias a los objetos existentes.  El "*3" crea una lista
conteniendo 3 referencias a la misma lista de longitud dos.  Cambios a
una fila se mostrarán en todas las filas, lo cual, seguramente, no es
lo que deseas.

El enfoque recomendado sería crear, primero, una lista de la longitud
deseada y, después, rellenar cada elemento con una lista creada en ese
momento:

   A = [None] * 3
   for i in range(3):
       A[i] = [None] * 2

Esto genera una lista conteniendo 3 listas distintas de longitud dos.
También puedes usar una comprensión de lista:

   w, h = 2, 3
   A = [[None] * w for i in range(h)]

O puedes usar una extensión que proporcione un tipo de dato para
matrices; NumPy es la más conocida.


¿Cómo puedo aplicar un método a una secuencia de objetos?
---------------------------------------------------------

Usa una comprensión de listas:

   result = [obj.method() for obj in mylist]


¿Por qué hacer lo siguiente, "a_tuple[i] += ['item']", lanza una excepción cuando la suma funciona?
---------------------------------------------------------------------------------------------------

Esto es debido a la combinación del hecho de que un operador de
asignación aumentada es un operador de *asignación* y a la diferencia
entre objetos mutables e inmutable en Python.

Esta discusión aplica, en general, cuando los operadores de asignación
aumentada se aplican a elementos de una tupla que apuntan a objetos
mutables. Pero vamos a usar una "lista" y "+=" para el ejemplo.

Si escribes:

   >>> a_tuple = (1, 2)
   >>> a_tuple[0] += 1
   Traceback (most recent call last):
      ...
   TypeError: 'tuple' object does not support item assignment

La razón por la que se produce la excepción debería ser evidente: "1"
se añade al objeto "a_tuple[0]" que apunta a ("1"), creando el objeto
resultante, "2", pero cuando intentamos asignar el resultado del
cálculo, "2", al elemento "0" de la tupla, obtenemos un error debido a
que no podemos cambiar el elemento al que apunta la tupla.

En realidad, lo que esta declaración de asignación aumentada está
haciendo es, aproximadamente, lo siguiente:

   >>> result = a_tuple[0] + 1
   >>> a_tuple[0] = result
   Traceback (most recent call last):
     ...
   TypeError: 'tuple' object does not support item assignment

Es la parte de asignación de la operación la que provoca el error,
debido a que una tupla es inmutable.

Cuando escribes algo como lo siguiente:

   >>> a_tuple = (['foo'], 'bar')
   >>> a_tuple[0] += ['item']
   Traceback (most recent call last):
     ...
   TypeError: 'tuple' object does not support item assignment

La excepción es un poco más sorprendente e, incluso, más sorprendente
es el hecho que aunque hubo un error, la agregación funcionó:

   >>> a_tuple[0]
   ['foo', 'item']

Para ver lo que sucede necesitas saber que (a) si un objeto implementa
un método mágico "__iadd__" , se le llama cuando se ejecuta la
asignación aumentada "+=" y el valor devuelto es lo que se usa en la
declaración de asignación; y (b) para listas, "__iadd__" es
equivalente a llamar a "extend" en la lista y retornar la lista.  Es
por esto que decimos que para listas, "+=" es un atajo para
"list.extend":

   >>> a_list = []
   >>> a_list += [1]
   >>> a_list
   [1]

Esto es equivalente a

   >>> result = a_list.__iadd__([1])
   >>> a_list = result

El objeto al que apunta a_list ha mutado y el puntero al objeto mutado
es asignado de vuelta a "a_list".  El resultado final de la asignación
no es opción debido a que es un puntero al mismo objeto al que estaba
apuntando "a_list"  pero la asignación sí que ocurre.

Por tanto, en nuestro ejemplo con tupla lo que está pasando es
equivalente a:

   >>> result = a_tuple[0].__iadd__(['item'])
   >>> a_tuple[0] = result
   Traceback (most recent call last):
     ...
   TypeError: 'tuple' object does not support item assignment

El "__iadd__" se realiza con éxito y la lista se extiende pero,
incluso aunque "result" apunta al mismo objeto al que ya está
apuntando "a_tuple[0]" la asignación final sigue resultando en un
error, debido a que las tuplas son inmutables.


Quiero hacer una ordenación compleja: ¿Puedes hacer una transformada Schwartziana (Schwartzian Transform) en Python?
--------------------------------------------------------------------------------------------------------------------

La técnica, atribuida a Randal Schwartz, miembro de la comunidad Perl,
ordena los elementos de una lista mediante una métrica que mapea cada
elemento a su "valor orden". En Python, usa el argumento "key" par el
método "list.sort()":

   Isorted = L[:]
   Isorted.sort(key=lambda s: int(s[10:15]))


¿Cómo puedo ordenar una lista a partir de valores de otra lista?
----------------------------------------------------------------

Las puedes unir en un iterador de tuplas, ordena la lista resultando y
después extrae el elemento que deseas.

   >>> list1 = ["what", "I'm", "sorting", "by"]
   >>> list2 = ["something", "else", "to", "sort"]
   >>> pairs = zip(list1, list2)
   >>> pairs = sorted(pairs)
   >>> pairs
   [("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
   >>> result = [x[1] for x in pairs]
   >>> result
   ['else', 'sort', 'to', 'something']

Una alternativa para el último paso es:

   >>> result = []
   >>> for p in pairs: result.append(p[1])

Si encuentras esto más legible, podrías preferir usar esto en lugar de
una comprensión de lista final.  Sin embargo, es casi el doble de
lento para listas largas.  ¿Por qué? Primero, la operación "append()"
necesita reasignar memoria y, aunque usa algunos trucos para evitar
hacerlo en todo momento, sigue teniéndolo que hacer ocasionalmente y
eso tiene un poco de coste.  Segundo, la expresión "result.append"
requiere una búsqueda adicional de atributos y, tercero, hay una
reducción de velocidad de tener que hacer todas esas llamadas a
funciones.


Objetos
=======


¿Qué es una clase?
------------------

Una clase es un tipo de objeto particular creado mediante la ejecución
de la declaración class. Los objetos class se usan como plantillas
para crear instancias de objetos que son tanto los datos (atributos)
como el código (métodos) específicos para un tipo de dato.

Una clase puede estar basada en una o más clases diferentes, llamadas
su(s) clase(s). Hereda los atributos y métodos de sus clases base.
Esto permite que se pueda refinar un objeto modelo de forma sucesiva
mediante herencia.  Puedes tener una clase genérica "Mailbox"  que
proporciona métodos de acceso básico para un buzón de correo y
subclases como "MboxMailbox", "MaildirMailbox", "OutlookMailbox" que
gestionan distintos formatos específicos de buzón de correos.


¿Qué es un método?
------------------

Un método es una función de un objeto "x" que puedes llamar,
normalmente, de la forma "x.name(arguments...)".  Los métodos  se
definen como funciones dentro de la definición de la clase:

   class C:
       def meth(self, arg):
           return arg * 2 + self.attribute


¿Qué es self?
-------------

Self es, básicamente, un nombre que se usa de forma convencional como
primer argumento de un método.  Un método definido como "meth(self, a,
b, c)" se le llama como "x.meth(a, b, c)" para una instancia "x" de la
clase es que se definió; el método invocado pensará que se le ha
invocado como "meth(x, a, b, c)".

Ver también ¿Por qué debe usarse 'self' explícitamente en las
definiciones y llamadas de métodos?.


¿Cómo puedo comprobar si un objeto es una instancia de una clase dada o de una subclase de la misma?
----------------------------------------------------------------------------------------------------

Usa la función incorporada "isinstance(obj, cls)".  Puedes comprobar
si un objeto es una instancia de cualquier número de clases
proporcionando una tupla en lugar de una sola clase, por ejemplo
"isinstance(obj, (class1, class2, ...))" y, también, puedes comprobar
si un objeto es uno de los tipos incorporados por ejemplo
"isinstance(obj, str)" o "isinstance(obj, (int, float, complex))".

Destacar que muchos programas no necesitan usar "isinstance()" de
forma frecuente en clases definidas por el usuario.  Si estás
desarrollando clases un mejor estilo orientado a objetos sería el de
definir los métodos en las clases que encapsulan un comportamiento en
particular en lugar de ir comprobando la clase del objeto e ir
haciendo cosas en base a la clase que es.  Por ejemplo, si tienes una
función que hace lo siguiente:

   def search(obj):
       if isinstance(obj, Mailbox):
           ...  # code to search a mailbox
       elif isinstance(obj, Document):
           ...  # code to search a document
       elif ...

Un enfoque más adecuado sería definir un método "search()" en todas
las clases e invocarlo:

   class Mailbox:
       def search(self):
           ...  # code to search a mailbox

   class Document:
       def search(self):
           ...  # code to search a document

   obj.search()


¿Qué es la delegación?
----------------------

La delegación es una técnica orientada a objetos (también llamado un
patrón de diseño). Digamos que tienes un objeto "x" y deseas cambiar
el comportamiento de solo uno de sus métodos.  Puedes crear una nueva
clase que proporciona una nueva implementación del método que te
interesa cambiar y delega el resto de métodos al método
correspondiente de "x".

Los programadores Python pueden implementar la delegación de forma muy
sencilla.  Por ejemplo, la siguiente clase implementa una clase que se
comporta como un fichero pero convierte todos los datos escritos a
mayúsculas:

   class UpperOut:

       def __init__(self, outfile):
           self._outfile = outfile

       def write(self, s):
           self._outfile.write(s.upper())

       def __getattr__(self, name):
           return getattr(self._outfile, name)

Aquí, la clase "UpperOut"  redefine el método "write()" para convertir
la cadena del argumento a mayúscula antes de invocar al método
"self._outfile.write()".  El resto de métodos han sido delegados al
objeto "self._outfile".  La delegación se consigue mediante el método
"__getattr__"; consulta la referencia del lenguaje para obtener más
información sobre cómo controlar el acceso a atributos.

Ten en cuenta que para casos más generales la delegación puede ser
algo más complicada. Cuando los atributos se deben colocar y recuperar
la clase debe definir, también, un método "__setattr__()"  y hay que
hacerlo con cuidado.  La implementación básica de "__setattr__()" es,
aproximadamente, equivalente a lo siguiente:

   class X:
       ...
       def __setattr__(self, name, value):
           self.__dict__[name] = value
       ...

Muchas implementaciones de "__setattr__()" deben modificar
"self.__dict__" para almacenar el estado local para self sin provocar
una recursión infinita.


¿Cómo invoco a un método definido en una clase base desde una clase derivada que lo ha sobreescrito?
----------------------------------------------------------------------------------------------------

Usa la función incorporada "super()":

   class Derived(Base):
       def meth(self):
           super(Derived, self).meth()

Para versiones anteriores a la 3.0, puedes usar clases clásicas: Para
la definición de una clase como "class Derived(Base): ..." puedes
invocar el método "meth()" definido en "Base" (o una de las clases
base de "Base") como "Base.meth(self, arguments...)".  Aquí,
"Base.meth" es un método no ligado y, por tanto, debes proporcionar el
argumento "self".


¿Cómo puedo organizar mi código para hacer que sea más sencillo modificar la clase base?
----------------------------------------------------------------------------------------

Puedes definir un alias para la clase base, asignar la clase base real
al alias antes de la definición de tu clase y usar el alias a lo largo
de toda la clase.  Entonces, lo único que tienes que cambiar es el
valor asignado al alias.  Sin pretenderlo, este truco también es útil
si desear decidir de forma dinámica (por ejemplo dependiendo de la
disponibilidad de recursos) qué clase base usar.  Ejemplo:

   BaseAlias = <real base class>

   class Derived(BaseAlias):
       def meth(self):
           BaseAlias.meth(self)
           ...


¿Cómo puedo crear datos estáticos de clase y métodos estáticos de clase?
------------------------------------------------------------------------

Tanto los datos estáticos como los métodos estáticos (en el sentido de
C++ o Java) están permitidos en Python.

Para datos estáticos simplemente define un atributo de clase.  Para
asignar un nuevo valor al atributo debes usar de forma explícita el
nombre de la clase en la asignación:

   class C:
       count = 0   # number of times C.__init__ called

       def __init__(self):
           C.count = C.count + 1

       def getcount(self):
           return C.count  # or return self.count

"c.count" también se refiere a "C.count" para cualquier "c" de tal
forma que se cumpla "isinstance(c, C)", a no ser que "c" sea
sobreescrita por si misma o por alguna clase contenida en la búsqueda
de clases base desde``c.__class__`` hasta "C".

Debes tener cuidado: dentro de un método de C, una asignación como
"self.count = 42" creará una nueva instancia sin relación con la
original que se llamará "count" en el propio diccionario de "self".
El reunificar el nombre de datos estáticos de una clase debería
llevar, siempre, a especificar la clase tanto si se produce desde
dentro de un método como si no:

   C.count = 314

Los métodos estáticos son posibles:

   class C:
       @staticmethod
       def static(arg1, arg2, arg3):
           # No 'self' parameter!
           ...

Sin embargo, una forma más directa de obtener el efecto de un método
estático sería mediante una simple función a nivel de módulo:

   def getcount():
       return C.count

Si has estructurado tu código para definir una clase única (o una
jerarquía de clases altamente relacionadas) por módulo, esto
proporcionará la encapsulación deseada.


¿Como puedo sobrecargar constructores (o métodos) en Python?
------------------------------------------------------------

Esta respuesta es aplicable, en realidad, a todos los métodos pero la
pregunta suele surgir primero en el contexto de los constructores.

En C++ deberías escribir

   class C {
       C() { cout << "No arguments\n"; }
       C(int i) { cout << "Argument is " << i << "\n"; }
   }

En Python solo debes escribir un único constructor que tenga en cuenta
todos los casos usando los argumentos por defecto.  Por ejemplo:

   class C:
       def __init__(self, i=None):
           if i is None:
               print("No arguments")
           else:
               print("Argument is", i)

Esto no es totalmente equivalente pero, en la práctica, es muy
similar.

Podrías intentar, también una lista de argumentos de longitud
variable, por ejemplo

   def __init__(self, *args):
       ...

El mismo enfoque funciona para todas las definiciones de métodos.


Intento usar __spam y obtengo un error sobre _SomeClassName__spam.
------------------------------------------------------------------

Nombres de variable con doble guión prefijado se convierten, con una
modificación de nombres, para proporcionar una forma simple pero
efectiva de definir variables de clase privadas. Cualquier
identificador de la forma "__spam" (como mínimo dos guiones bajos como
prefijo, como máximo un guión bajo como sufijo) se reemplaza con
"_classname__spam", donde "classname" es el nombre de la clase
eliminando cualquier guión bajo prefijado.

Esto no garantiza la privacidad: un usuario externo puede acceder, de
forma deliberada y si así lo desea, al atributo "_classname__spam",  y
los valores privados son visibles en el "__dict__" del objeto.  Muchos
programadores Python no se suelen molestar en usar nombres privados de
variables.


Mi clase define __del__ pero no se le invoca cuando borro el objeto.
--------------------------------------------------------------------

Existen varias razones posibles para que suceda así.

La declaración del no invoca, necesariamente, al método "__del__()" --
simplemente reduce el conteo de referencias del objeto y, si se reduce
a cero entonces es cuando se invoca a "__del__()".

Si tus estructuras de datos contienen enlaces circulares (por ejemplo
un árbol en el cual cada hijo tiene una referencia al padre y cada
padre tiene una lista de hijos) el conteo de referencias no alcanzará
nunca el valor de cero.  De vez en cuando, Python ejecuta un algoritmo
para detectar esos ciclos pero el recolector de basura debe ejecutarse
un rato después de que se desvanezca la última referencia a tu
estructura de datos, de tal forma que tu método "__del__()" se pueda
invocar en un momento aleatorio que no resulte inconveniente. Esto no
es conveniente si estás intentando reproducir un problema. Peor aún,
el orden en el que se ejecutan los métodos "__del__()" del objeto es
arbitrario.  Puedes ejecutar "gc.collect()" para forzar una
recolección pero *existen* casos patológicos en los cuales los objetos
nunca serán recolectados.

A pesar del recolector de ciclos, siempre será buena idea definir un
método "close()" de forma explícita en objetos que debe ser llamado en
el momento que has terminado con ellos.  El método "close()" puede, en
ese momento, eliminar atributos que se refieren a subobjetos.  No
invoques directamente a "__del__()" -- "__del__()" debe invocar a
"close()" y "close()" debe asegurarse que puede ser invocado más de
una vez en el mismo objeto.

Otra forma de evitar referencias cíclicas sería usando el módulo
"weakref", que permite apuntar hacia objetos sin incrementar su conteo
de referencias. Las estructuras de datos en árbol, por ejemplo,
deberían usar referencias débiles para las referencias del padre y
hermanos (¡si es que las necesitan!).

Finalmente, si tu método "__del__()" lanza una excepción, se manda un
mensaje de alerta a "sys.stderr".


¿Cómo puedo obtener una lista de todas las instancias de una clase dada?
------------------------------------------------------------------------

Python no hace seguimiento de todas las instancias de una clase (o de
los tipos incorporados). Puedes programar el constructor de una clase
para que haga seguimiento de todas sus instancias manteniendo una
lista de referencias débiles a cada instancia.


¿Por qué el resultado de "id()" no parece ser único?
----------------------------------------------------

La función incorporada "id()" devuelve un entero que se garantiza que
sea único durante la vida del objeto.  Debido a que en CPython esta es
la dirección en memoria del objeto, sucede que, frecuentemente,
después de que un objeto se elimina de la memoria el siguiente objeto
recién creado se localiza en la misma posición en memoria.  Esto se
puede ver ilustrado en este ejemplo:

>>> id(1000) 
13901272
>>> id(2000) 
13901272

Las dos ids pertenecen a dos objetos 'entero' diferentes que se crean
antes y se eliminan inmediatamente después de la ejecución de la
invocación a "id()".  Para estar seguro que los objetos cuya id
quieres examinar siguen vivos crea otra referencia al objeto:

>>> a = 1000; b = 2000
>>> id(a) 
13901272
>>> id(b) 
13891296


Módulos
=======


¿Cómo creo un fichero .pyc?
---------------------------

Cuando se importa un módulo por primera vez (o cuando el código fuente
ha cambiado desde que el fichero compilado se creó) un fichero ".pyc"
que contiene el código compilado se debería crear en la subcarpeta
"__pycache__" del directorio que contiene al fichero``.py``.  El
fichero ".pyc" tendrá un nombre que empezará con el mismo nombre que
el del fichero  ".py" y terminará con ".pyc", con un componente
intermedio que dependerá del binario "python" en particular que lo
creó.  (Ver **PEP 3147** para detalles.)

Una razón por la que no se cree un fichero ".pyc" podría ser debido a
un problema de permisos del directorio que contiene al fichero fuente,
lo que significa que el subdirectorio "__pycache__" no se puede crear.
Esto puede suceder, por ejemplo, si desarrollas como un usuario pero
lo ejecutas como otro, como si estuvieras probando en un servidor web.

Hasta que no definas la variable de entorno "PYTHONDONTWRITEBYTECODE",
la creación de un fichero .pyc se hará automáticamente si importas un
módulo y Python dispone de la habilidad (permisos, espacio libre,
etc...) para crear un subdirectorio "__pycache__" y escribir un módulo
compilado en ese subdirectorio.

La ejecución de un script principal Python no se considera una
importación y no se crea el fichero ".pyc".  Por ejemplo, Si tienes un
módulo principal "foo.py" que importa a otro módulo "xyz.py", cuando
ejecutas "foo" (mediante un comando de la shell "python foo.py"), se
creará un fichero ".pyc" para "xyz" porque "xyz" ha sido importado,
pero no se creará un fichero ".pyc" para "foo" ya que "foo.py" no ha
sido importado.

Si necesitas crear un fichero ".pyc" también para "foo" -- es decir,
crear un fichero ".pyc" para un módulo que no ha sido importado --
puedes usar los módulos "py_compile" y "compileall".

El módulo "py_compile" puede compilar manualmente cualquier módulo.
Una forma sería usando la función "compile()" de ese módulo de forma
interactiva:

   >>> import py_compile
   >>> py_compile.compile('foo.py')                 

Esto escribirá ".pyc" en el subdirectorio "__pycache__" en la misma
localización en la que se encuentre "foo.py" (o, puedes sobreescribir
ese comportamiento con el parámetro opcional "cfile").

Puedes compilar automáticamente todos los ficheros en un directorio o
directorios usando el módulo "compileall".  Lo puedes hacer desde la
línea de comandos ejecutando "compileall.py" y proporcionando una ruta
al directorio que contiene los ficheros Python a compilar:

   python -m compileall .


¿Cómo puedo encontrar el nombre del módulo en uso?
--------------------------------------------------

Un módulo puede encontrar su propio nombre mirando en la variable
global predeterminada "__name__".  Si tiene el valor "'__main__'", el
programa se está ejecutando como un script.  Muchos módulos que se
usan, generalmente, importados en otro script proporcionan, además,
una interfaz para la línea de comandos o para probarse a si mismos y
solo ejecutan código después de comprobar "__name__":

   def main():
       print('Running test...')
       ...

   if __name__ == '__main__':
       main()


¿Cómo podría tener módulos que se importan mutuamente entre ellos?
------------------------------------------------------------------

Supón que tienes los siguientes módulos:

foo.py:

   from bar import bar_var
   foo_var = 1

bar.py:

   from foo import foo_var
   bar_var = 2

El problema es que el intérprete realizará los siguientes pasos:

* main importa a foo

* Se crea un *globals* vacío para foo

* foo se compila y se comienza a ejecutar

* foo importa a bar

* Se crea un *globals* vacío para bar

* bar se compila y se comienza a ejecutar

* bar importa a foo (lo cual no es una opción ya que ya hay un módulo
  que se llama foo)

* bar.foo_var = foo.foo_var

El último paso falla debido a que Python todavía no ha terminado de
interpretar a "foo" y el diccionario de símbolos global para "foo"
todavía se encuentra vacío.

Lo mismo ocurre cuando usas "import foo" y luego tratas de acceder a
"foo.foo_var" en un código global.

Existen (al menos) tres posibles soluciones para este problema.

Guido van Rossum recomienda evitar todos los usos de "from <module>
import ...", y colocar todo el código dentro de funciones.  La
inicialización de variables globales y variables de clase debería usar
únicamente constantes o funciones incorporadas .  Esto significa que
todo se referenciará como "<module>.<name>" desde un módulo importado.

Jim Roskind sugiere realizar los siguientes pasos en el siguiente
orden en cada módulo:

* exportar (*globals*, funciones y clases que no necesitan clases
  bases importadas)

* "import" declaraciones

* código activo (incluyendo *globals* que han sido inicializados desde
  valores importados).

este enfoque no le gusta mucho a van Rossum debido a que los import
aparecen en lugares extraños, pero funciona.

Matthias Urlichs recomienda reestructurar tu código de tal forma que
un import recursivo no sea necesario.

Estas soluciones no son mutuamente excluyentes.


__import__('x.y.z') devuelve <module 'x'>; ¿cómo puedo obtener z?
-----------------------------------------------------------------

Considera, en su lugar, usa la función de conveniencia
"import_module()" de "importlib":

   z = importlib.import_module('x.y.z')


Cuando edito un módulo importado y lo reimporto los cambios no tienen efecto. ¿Por qué sucede esto?
---------------------------------------------------------------------------------------------------

Por razones de eficiencia además de por consistencia, Python solo lee
el fichero del módulo la primera vez que el módulo se importa.  Si no
lo hiciera así, un programa escrito en muchos módulos donde cada
módulo importa al mismo módulo básico estaría analizando
sintácticamente el mismo módulo básico muchas veces.  Para forzar una
relectura de un módulo que ha sido modificado haz lo siguiente:

   import importlib
   import modname
   importlib.reload(modname)

Alerta: esta técnica no es 100% segura.  En particular, los módulos
que contienen declaraciones como

   from modname import some_objects

continuarán funcionando con la versión antigua de los objetos
importados.  Si el módulo contiene definiciones de clase, instancias
de clase ya existentes *no* se actualizarán para usar la nueva
definición de la clase.  Esto podría resultar en el comportamiento
paradójico siguiente:

   >>> import importlib
   >>> import cls
   >>> c = cls.C()                # Create an instance of C
   >>> importlib.reload(cls)
   <module 'cls' from 'cls.py'>
   >>> isinstance(c, cls.C)       # isinstance is false?!?
   False

La naturaleza del problema se hace evidente si muestras la "identity"
de los objetos clase:

   >>> hex(id(c.__class__))
   '0x7352a0'
   >>> hex(id(cls.C))
   '0x4198d0'
