tutorial lenguaje instalar descargar python

lenguaje - python tutorial



Python 2.x gotchas y minas terrestres (22)

Python 2 tiene un comportamiento sorprendente con comparaciones:

>>> print x 0 >>> print y 1 >>> x < y False

¿Que esta pasando? repr() al rescate:

>>> print "x: %r, y: %r" % (x, y) x: ''0'', y: 1

El propósito de mi pregunta es fortalecer mi base de conocimiento con Python y obtener una mejor idea de ello, que incluye conocer sus fallas y sorpresas. Para mantener las cosas específicas, solo me interesa el intérprete CPython.

Estoy buscando algo similar a lo que aprendí de mi pregunta sobre las minas antipersonal de PHP , donde algunas de las respuestas eran bien conocidas para mí, pero una pareja era terriblemente horrible.

Actualización: aparentemente una o dos personas están molestas por haber hecho una pregunta que ya se respondió parcialmente fuera de Stack Overflow. Como una especie de compromiso aquí está la URL http://www.ferg.org/projects/python_gotchas.html

Tenga en cuenta que una o dos respuestas aquí ya son originales de lo que estaba escrito en el sitio al que se hace referencia anteriormente.


Si asigna una variable dentro de una función, Python supone que la variable está definida dentro de esa función:

>>> x = 1 >>> def increase_x(): ... x += 1 ... >>> increase_x() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in increase_x UnboundLocalError: local variable ''x'' referenced before assignment

Use global x (o nonlocal x en Python 3) para declarar que desea establecer una variable definida fuera de su función.


La única sorpresa con la que he tratado es con GIL de CPython. Si por alguna razón esperas que los hilos de python en CPython se ejecuten al mismo tiempo ... bueno, no lo son, y esto está bastante bien documentado por la multitud de Python e incluso el propio Guido.

Una explicación larga pero exhaustiva del enhebrado de CPython y algunas de las cosas que suceden bajo el capó y por qué no es posible una verdadera concurrencia con CPython. http://jessenoller.com/2009/02/01/python-threads-and-the-global-interpreter-lock/


La vinculación dinámica hace que los errores tipográficos en los nombres de las variables sean sorprendentemente difíciles de encontrar. Es fácil pasar media hora arreglando un error trivial.

EDITAR: un ejemplo ...

for item in some_list: ... # lots of code ... # more code for tiem in some_other_list: process(item) # oops!


Debe tener en cuenta cómo se manejan las variables de clase en Python. Considere la siguiente jerarquía de clases:

class AAA(object): x = 1 class BBB(AAA): pass class CCC(AAA): pass

Ahora, verifique el resultado del siguiente código:

>>> print AAA.x, BBB.x, CCC.x 1 1 1 >>> BBB.x = 2 >>> print AAA.x, BBB.x, CCC.x 1 2 1 >>> AAA.x = 3 >>> print AAA.x, BBB.x, CCC.x 3 2 3

¿Sorprendido? No lo serás si recuerdas que las variables de clase se manejan internamente como diccionarios de un objeto de clase. Si no se encuentra un nombre de variable en el diccionario de la clase actual, se buscan las clases principales. Entonces, el siguiente código nuevamente, pero con explicaciones:

# AAA: {''x'': 1}, BBB: {}, CCC: {} >>> print AAA.x, BBB.x, CCC.x 1 1 1 >>> BBB.x = 2 # AAA: {''x'': 1}, BBB: {''x'': 2}, CCC: {} >>> print AAA.x, BBB.x, CCC.x 1 2 1 >>> AAA.x = 3 # AAA: {''x'': 3}, BBB: {''x'': 2}, CCC: {} >>> print AAA.x, BBB.x, CCC.x 3 2 3

Lo mismo ocurre con el manejo de variables de clase en instancias de clase (trate este ejemplo como una continuación del anterior):

>>> a = AAA() # a: {}, AAA: {''x'': 3} >>> print a.x, AAA.x 3 3 >>> a.x = 4 # a: {''x'': 4}, AAA: {''x'': 3} >>> print a.x, AAA.x 4 3


Las expresiones en los argumentos predeterminados se calculan cuando se define la función, no cuando se llama.

Ejemplo: considere por defecto un argumento para la hora actual:

>>>import time >>> def report(when=time.time()): ... print when ... >>> report() 1210294387.19 >>> time.sleep(5) >>> report() 1210294387.19

El argumento when no cambia Se evalúa cuando defines la función. No cambiará hasta que la aplicación se reinicie.

Estrategia: no se tropezará con esto si tiene argumentos por defecto a None y luego hará algo útil cuando lo vea:

>>> def report(when=None): ... if when is None: ... when = time.time() ... print when ... >>> report() 1210294762.29 >>> time.sleep(5) >>> report() 1210294772.23

Ejercicio: para asegurarse de haber entendido: ¿por qué está sucediendo esto?

>>> def spam(eggs=[]): ... eggs.append("spam") ... return eggs ... >>> spam() [''spam''] >>> spam() [''spam'', ''spam''] >>> spam() [''spam'', ''spam'', ''spam''] >>> spam() [''spam'', ''spam'', ''spam'', ''spam'']


James Dumay me recordó de manera elocuente otro truco de Python:

No todas las "baterías incluidas" de Python son maravillosas .

El ejemplo específico de James fueron las bibliotecas HTTP: httplib , urllib , urllib2 , urlparse , mimetools y ftplib . Parte de la funcionalidad está duplicada, y parte de la funcionalidad que esperaría está completamente ausente, por ejemplo, el manejo de redirección. Francamente, es horrible.

Si alguna vez tengo que tomar algo a través de HTTP estos días, utilizo el módulo urlgrabber bifurcado del proyecto Yum.


Los flotantes no se imprimen con la máxima precisión por defecto (sin repr ):

x = 1.0 / 3 y = 0.333333333333 print x #: 0.333333333333 print y #: 0.333333333333 print x == y #: False

repr imprime demasiados dígitos:

print repr(x) #: 0.33333333333333331 print repr(y) #: 0.33333333333300003 print x == 0.3333333333333333 #: True


Sin incluir un __init__ .py en sus paquetes. Ese todavía me atrapa a veces.


Una de las mayores sorpresas que he tenido con Python es esta:

a = ([42],) a[0] += [43, 44]

Esto funciona como uno podría esperar, ¡excepto para generar un TypeError después de actualizar la primera entrada de la tupla! Entonces a será ([42, 43, 44],) después de ejecutar el enunciado += , pero de todos modos habrá una excepción. Si intentas esto por otro lado

a = ([42],) b = a[0] b += [43, 44]

no obtendrás un error


def f(): x += 1 x = 42 f()

da como resultado un UnboundLocalError , porque los nombres locales se detectan estáticamente. Un ejemplo diferente sería

def f(): print x x = 43 x = 42 f()


try: int("z") except IndexError, ValueError: pass

La razón por la que esto no funciona es porque IndexError es el tipo de excepción que está capturando, y ValueError es el nombre de la variable a la que está asignando la excepción.

El código correcto para detectar múltiples excepciones es:

try: int("z") except (IndexError, ValueError): pass


La división de listas me ha causado mucha pena. Realmente considero el siguiente comportamiento como un error.

Definir una lista x

>>> x = [10, 20, 30, 40, 50]

Índice de acceso 2:

>>> x[2] 30

Como esperabas

Cortar la lista del índice 2 y hasta el final de la lista:

>>> x[2:] [30, 40, 50]

Como esperabas

Índice de acceso 7:

>>> x[7] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range

De nuevo, como esperabas.

Sin embargo , intente dividir la lista del índice 7 hasta el final de la lista:

>>> x[7:] []

???

El remedio es realizar muchas pruebas al usar la división de listas. Me gustaría obtener un error en su lugar. Mucho más fácil de depurar.


No puede usar locals () [''x''] = lo que sea para cambiar los valores de las variables locales como podría esperar.

This works: >>> x = 1 >>> x 1 >>> locals()[''x''] = 2 >>> x 2 BUT: >>> def test(): ... x = 1 ... print x ... locals()[''x''] = 2 ... print x # *** prints 1, not 2 *** ... >>> test() 1 1

Esto realmente me quemó en una respuesta aquí en SO, ya que lo había probado fuera de una función y obtuve el cambio que quería. Luego, lo encontré mencionado y contrastado con el caso de globals () en "Dive Into Python". Ver ejemplo 8.12. (Aunque no nota que el cambio a través de los lugareños () funcionará en el nivel superior como se muestra arriba).


Bucles y lambdas (o cualquier cierre, realmente): las variables están vinculadas por su nombre

funcs = [] for x in range(5): funcs.append(lambda: x) [f() for f in funcs] # output: # 4 4 4 4 4

Una solución alternativa es crear una función separada o pasar los argumentos por nombre:

funcs = [] for x in range(5): funcs.append(lambda x=x: x) [f() for f in funcs] # output: # 0 1 2 3 4


Usar variables de clase cuando desee variables de instancia. La mayoría de las veces esto no causa problemas, pero si se trata de un valor mutable causa sorpresas.

class Foo(object): x = {}

Pero:

>>> f1 = Foo() >>> f2 = Foo() >>> f1.x[''a''] = ''b'' >>> f2.x {''a'': ''b''}

Casi siempre desea variables de instancia, que requieren que asigne dentro de __init__ :

class Foo(object): def __init__(self): self.x = {}


Los valores de range(end_val) no solo son estrictamente más pequeños que end_val , sino estrictamente más pequeños que int(end_val) . Para un argumento float al range , este podría ser un resultado inesperado:

from future.builtins import range list(range(2.89)) [0, 1]


x += [...] no es lo mismo que x = x + [...] cuando x es una lista "

>>> x = y = [1,2,3] >>> x = x + [4] >>> x == y False >>> x = y = [1,2,3] >>> x += [4] >>> x == y True

Uno crea una nueva lista mientras que el otro se modifica en su lugar


Lista de repetición con listas anidadas

Esto me sorprendió hoy y desperdicié una hora de mi depuración de tiempo:

>>> x = [[]]*5 >>> x[0].append(0) # Expect x equals [[0], [], [], [], []] >>> x [[0], [0], [0], [0], [0]] # Oh dear

Explicación: problema de la lista de Python


Hubo un gran debate sobre las características del lenguaje oculto hace un tiempo: hidden-features-of-python . Donde se mencionaron algunos escollos (y algunas de las cosas buenas también).

También es posible que desee verificar Python Warts .

Pero para mí, la división entera es una gotcha:

>>> 5/2 2

Probablemente querías:

>>> 5*1.0/2 2.5

Si realmente quieres este comportamiento (tipo C), debes escribir:

>>> 5//2 2

Como eso también funcionará con flotadores (y funcionará cuando finalmente vaya a Python 3 ):

>>> 5*1.0//2 2.0

GvR explica cómo la división de enteros comenzó a funcionar como lo hace en la historia de Python .


Mezclar sin querer las clases de estilo antiguo y nuevo puede causar errores aparentemente misteriosos.

Supongamos que tiene una jerarquía de clases simple que consiste en la superclase A y la subclase B. Cuando se instancia B, se debe llamar primero al constructor de A. El siguiente código hace esto correctamente:

class A(object): def __init__(self): self.a = 1 class B(A): def __init__(self): super(B, self).__init__() self.b = 1 b = B()

Pero si olvida hacer A una clase de estilo nuevo y definirlo así:

class A: def __init__(self): self.a = 1

obtienes este rastreo:

Traceback (most recent call last): File "AB.py", line 11, in <module> b = B() File "AB.py", line 7, in __init__ super(B, self).__init__() TypeError: super() argument 1 must be type, not classobj

Otras dos preguntas relacionadas con este tema son 489269 y 770134


Debido a la ''verdad'' esto tiene sentido:

>>>bool(1) True

pero puede que no esperes que vaya por el otro lado:

>>>float(True) 1.0

Esto puede ser un problema si está convirtiendo cadenas en numéricas y sus datos tienen valores True / False.