lenguaje - Errores comunes en Python
python tutorial (30)
Posible duplicado:
Python 2.x gotcha''s y landmines
Hoy fui mordido nuevamente por argumentos predeterminados mutables después de muchos años. Normalmente no uso argumentos predeterminados mutables a menos que sea necesario, pero creo que con el tiempo me olvidé de eso. Hoy en la aplicación agregué tocElements = [] en una lista de argumentos de la función de generación de PDF y ahora "Tabla de contenido" se hace cada vez más larga después de cada invocación de "generar pdf". :)
¿Qué más debería agregar a mi lista de cosas que DEBE evitar?
Siempre importe los módulos de la misma manera, por ejemplo,
from y import x
y laimport x
se tratan como módulos diferentes .No use rango en lugar de listas porque
range()
se convertirá en un iterador de todos modos, lo siguiente fallará:myIndexList = [0, 1, 3] isListSorted = myIndexList == range(3) # will fail in 3.0 isListSorted = myIndexList == list(range(3)) # will not
Lo mismo se puede hacer por error con xrange:
myIndexList == xrange(3)
Tenga cuidado al capturar múltiples tipos de excepciones:
try: raise KeyError("hmm bug") except KeyError, TypeError: print TypeError
Esto imprime "hmm bug", aunque no es un error; parece que estamos obteniendo excepciones de ambos tipos, pero en su lugar estamos capturando KeyError solo como variable TypeError, use esto en su lugar:
try: raise KeyError("hmm bug") except (KeyError, TypeError): print TypeError
Algo relacionado con el argumento mutable predeterminado, la forma en que uno verifica el caso "perdido" da como resultado diferencias cuando se pasa una lista vacía:
def func1(toc=None):
if not toc:
toc = []
toc.append(''bar'')
def func2(toc=None):
if toc is None:
toc = []
toc.append(''bar'')
def demo(toc, func):
print func.__name__
print '' before:'', toc
func(toc)
print '' after:'', toc
demo([], func1)
demo([], func2)
Aquí está el resultado:
func1
before: []
after: []
func2
before: []
after: [''bar'']
Algunas opiniones personales, pero creo que es mejor NO :
utilizar módulos en desuso (usar advertencias para ellos)
uso excesivo de clases y herencia (típico del legado de idiomas estáticos tal vez)
usar explícitamente algoritmos declarativos (como iteración con
for
vs uso deitertools
)reimplementar las funciones de la lib estándar, "porque no necesito todas esas características"
usando características por el bien de esto (reduciendo la compatibilidad con versiones anteriores de Python)
usar metaclases cuando realmente no es necesario y, en general, hacer las cosas demasiado "mágicas"
evitar el uso de generadores
(más personal) intente micro-optimizar el código de CPython a bajo nivel. Mejor dedique tiempo a los algoritmos y luego optimice creando una pequeña
ctypes
compartida C llamadactypes
(es muy fácil obtenerctypes
dectypes
5x en un ciclo interno)usar listas innecesarias cuando los iteradores serían suficientes
codifique un proyecto directamente para 3.x antes de que las librerías que necesita estén todas disponibles (¡este punto puede ser un poco controvertido ahora!)
Creando un módulo local con el mismo nombre que uno de stdlib. Esto casi siempre se hace por accidente (como se informa en esta pregunta ), pero generalmente da como resultado mensajes de error crípticos.
Cuando necesite una población de matrices, puede sentir la tentación de escribir algo como esto:
>>> a=[[1,2,3,4,5]]*4
Y, por supuesto, te dará lo que esperas cuando lo mires
>>> from pprint import pprint
>>> pprint(a)
[[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
Pero no espere que los elementos de su población sean objetos separados:
>>> a[0][0] = 2
>>> pprint(a)
[[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5]]
A menos que esto sea lo que necesitas ...
Vale la pena mencionar una solución alternativa:
a = [[1,2,3,4,5] for _ in range(4)]
Dejaría de usar métodos en desuso en 2.6, de modo que su aplicación o script estará listo y será más fácil de convertir a Python 3.
El primer error antes de empezar: no tengas miedo de los espacios en blanco .
Cuando le muestras a alguien un fragmento de código Python, quedan impresionados hasta que les dices que tienen que sangrar correctamente. Por alguna razón, la mayoría de las personas siente que un idioma no debe forzar un cierto estilo sobre ellos, mientras que todos ellos sangrarán el código de todos modos.
Evite usar palabras clave como sus propios identificadores.
Además, siempre es bueno no usar from somemodule import *
.
Ha mencionado argumentos predeterminados ... Uno que es casi tan malo como los argumentos predeterminados mutables: valores predeterminados que no son None
.
Considere una función que cocinará algo de comida:
def cook(breakfast="spam"):
arrange_ingredients_for(breakfast)
heat_ingredients_for(breakfast)
serve(breakfast)
Debido a que especifica un valor predeterminado para el breakfast
, es imposible que otra función diga "cocinar su desayuno predeterminado" sin un caso especial:
def order(breakfast=None):
if breakfast is None:
cook()
else:
cook(breakfast)
Sin embargo, esto podría evitarse si cook
usó None
como valor predeterminado:
def cook(breakfast=None):
if breakfast is None:
breakfast = "spam"
def order(breakfast=None):
cook(breakfast)
Un buen ejemplo de esto es Django bug #6988 . El módulo de caché de Django tenía una función de "guardar en caché" que se veía así:
def set(key, value, timeout=0):
if timeout == 0:
timeout = settings.DEFAULT_TIMEOUT
_caching_backend.set(key, value, timeout)
Pero, para el backend de memcached, un tiempo de espera de 0
significa "nunca timeout" ... que, como puede ver, sería imposible de especificar.
Importación de re
y uso del enfoque de expresión regular completa para la coincidencia / transformación de cadenas, cuando existen métodos de cuerda perfectamente buenos para cada operación común (por ejemplo, mayúsculas, búsqueda / coincidencia simple).
La copia normal (asignación) se realiza por referencia, por lo que llenar un contenedor adaptando el mismo objeto e insertando, termina con un contenedor con referencias al último objeto agregado.
Use copy.deepcopy
en copy.deepcopy
lugar.
Mutando un argumento predeterminado:
def foo(bar=[]):
bar.append(''baz'')
return bar
El valor predeterminado se evalúa solo una vez, y no cada vez que se llama a la función. Las llamadas repetidas a foo()
devolverían [''baz'']
, [''baz'', ''baz'']
, [''baz'', ''baz'', ''baz'']
, ...
Si quieres cambiar la barra, haz algo como esto:
def foo(bar=None):
if bar is None:
bar = []
bar.append(''baz'')
return bar
O, si le gustan los argumentos para ser final:
def foo(bar=[]):
not_bar = bar[:]
not_bar.append(''baz'')
return not_bar
No modifique una lista mientras la itera.
odd = lambda x : bool(x % 2)
numbers = range(10)
for i in range(len(numbers)):
if odd(numbers[i]):
del numbers[i]
Una sugerencia común para evitar este problema es iterar sobre la lista al revés:
for i in range(len(numbers)-1,0,-1):
if odd(numbers[i]):
del numbers[i]
Pero aún mejor es usar una lista de comprensión para construir una nueva lista para reemplazar la anterior:
numbers[:] = [n for n in numbers if not odd(n)]
No sé si esto es un error común, pero mientras Python no tiene operadores de incremento y decremento, se permiten dobles signos, por lo que
++i
y
--i
es un código sintácticamente correcto, pero no hace nada "útil" o que pueda estar esperando.
No usando herramientas funcionales. Esto no es solo un error desde el punto de vista del estilo, es un error desde el punto de vista de la velocidad porque muchas de las herramientas funcionales están optimizadas en C.
Este es el ejemplo más común:
temporary = []
for item in itemlist:
temporary.append(somefunction(item))
itemlist = temporary
La forma correcta de hacerlo:
itemlist = map(somefunction, itemlist)
La forma más correcta de hacerlo:
itemlist = [somefunction(x) for x in itemlist]
Y si solo necesita los elementos procesados disponibles uno a la vez, en lugar de todos a la vez, puede ahorrar memoria y mejorar la velocidad utilizando los equivalentes iterables.
# itertools-based iterator
itemiter = itertools.imap(somefunction, itemlist)
# generator expression-based iterator
itemiter = (somefunction(x) for x in itemlist)
Nunca suponga que tener una aplicación Python multiproceso y una máquina con capacidad SMP (por ejemplo, una equipada con una CPU multi-core) le dará la ventaja de introducir un verdadero paralelismo en su aplicación. Lo más probable es que no lo haga debido a GIL (Global Interpreter Lock) que sincroniza su aplicación en el nivel de intérprete de código de bytes.
Hay algunas soluciones alternativas como aprovechar el SMP colocando el código concurrente en llamadas a la API C o utilizando procesos múltiples (en lugar de hilos) a través de contenedores (por ejemplo, como el disponible en http://www.parallelpython.org ) pero si uno necesita verdadero multi-threading en Python uno debe mirar cosas como Jython, IronPython etc. (GIL es una característica del intérprete CPython para que otras implementaciones no se vean afectadas).
Según las preguntas frecuentes de Python 3000 (disponible en Artima), lo anterior aún se mantiene incluso para las últimas versiones de Python.
Python Language Gotchas - cosas que fallan de maneras muy oscuras
Usando argumentos por defecto mutables.
Los ceros a la izquierda significan octal.
09
es un error de sintaxis muy oscuro en Python 2.xErrores al escribir nombres de métodos anulados en una superclase o subclase. El error ortográfico de la superclase es peor porque ninguna de las subclases lo anula correctamente.
Python Design Gotchas
Pasar tiempo en la introspección (por ejemplo, tratando de determinar automáticamente los tipos o la identidad de la superclase u otras cosas). Primero, es obvio al leer la fuente. Más importante aún, el tiempo invertido en la introspección de Python rara suele indicar una falla fundamental para captar el polimorfismo. El 80% de las preguntas de introspección de Python sobre SO no logran obtener el polimorfismo.
Pasar tiempo en el golf de código. El hecho de que su modelo mental de su aplicación tenga cuatro palabras clave ("do", "what", "I", "mean"), no significa que deba construir un marco hiperconstruido e introspectivo impulsado por decoradores para hacer eso. Python le permite tomar DRY a un nivel que es tonto. El resto de las preguntas de introspección de Python sobre SO intentan reducir problemas complejos para codificar ejercicios de golf.
Monkeypatching.
No leer realmente la biblioteca estándar y reinventar la rueda.
Confiando el tipo interactivo a medida que vaya Python con un programa adecuado. Mientras escribe de manera interactiva, puede perder el rastro de una variable y debe usar
globals()
. Además, mientras escribe, casi todo es global. En los programas adecuados, nunca "perderás el rastro" de una variable, y nada será global.
Riesgo común: los argumentos predeterminados se evalúan una vez :
def x(a, l=[]):
l.append(a)
return l
print x(1)
print x(2)
huellas dactilares:
[1]
[1, 2]
es decir, siempre obtienes la misma lista.
Rolando su propio código antes de buscar en la biblioteca estándar. Por ejemplo, escribiendo esto:
def repeat_list(items):
while True:
for item in items:
yield item
Cuando podrías usar esto:
from itertools import cycle
Ejemplos de módulos frecuentemente pasados por alto (además de las itertools
) incluyen:
-
optparse
para crear analizadores de línea de comandos -
ConfigParser
para leer archivos de configuración de manera estándar -
tempfile
para crear y administrar archivos temporales -
shelve
para almacenar objetos Python en el disco, útil cuando una base de datos completa es overkill
Si vienes de C ++, date cuenta de que las variables declaradas en una definición de clase son estáticas. Puede inicializar miembros no estáticos en el método init .
Ejemplo:
class MyClass:
static_member = 1
def __init__(self):
self.non_static_member = random()
Similar a los argumentos por defecto mutables es el atributo de clase mutable.
>>> class Classy:
... foo = []
... def add(self, value):
... self.foo.append(value)
...
>>> instance1 = Classy()
>>> instance2 = Classy()
>>> instance1.add("Foo!")
>>> instance2.foo
[''Foo!'']
No es lo que esperas
Sorprendido de que nadie haya dicho esto:
Mezcle la pestaña y los espacios al sangrar.
Realmente, es un asesino. Créame. En particular , si se ejecuta.
También comencé a aprender Python y uno de los errores más grandes que cometí es usar constantemente C ++ / C # indexed "for" loop. Python tiene un ciclo de tipo (i; i <length; i ++) y por una buena razón; la mayoría de las veces hay mejores formas de hacer lo mismo.
Ejemplo: tenía un método que iteraba sobre una lista y devolvía los índices de los elementos seleccionados:
for i in range(len(myList)):
if myList[i].selected:
retVal.append(i)
En cambio, Python tiene una lista de comprensión que resuelve el mismo problema de una manera más elegante y fácil de leer:
retVal = [index for index, item in enumerate(myList) if item.selected]
Una mala costumbre de la que tuve que entrenarme fue usar X and Y or Z
para la lógica en línea.
A menos que pueda garantizar al 100% que Y
sea un valor real, incluso cuando su código cambie dentro de 18 meses, se prepara para un comportamiento inesperado.
Afortunadamente, en versiones posteriores puede usar Y if X else Z
Usando el formateador %s
en los mensajes de error. En casi todas las circunstancias, se debe usar %r
.
Por ejemplo, imagina un código como este:
try:
get_person(person)
except NoSuchPerson:
logger.error("Person %s not found." %(person))
Impreso este error:
ERROR: Person wolever not found.
Es imposible saber si la variable person
es la cadena "wolever"
, la cadena unicode u"wolever"
o una instancia de la clase Person
(que tiene __str__
definido como def __str__(self): return self.name
). Mientras que, si se usara %r
, habría tres mensajes de error diferentes:
...
logger.error("Person %r not found." %(person))
Produciría los errores mucho más útiles:
ERROR: Person ''wolever'' not found. ERROR: Person u''wolever'' not found. ERROR: Person not found.
Otra buena razón para esto es que los caminos son mucho más fáciles de copiar / pegar. Imagina:
try:
stuff = open(path).read()
except IOError:
logger.error("Could not open %s" %(path))
Si la path
es some path/with ''strange'' "characters"
, el mensaje de error será:
ERROR: Could not open some path/with ''strange'' "characters"
Que es difícil de analizar visualmente y difícil de copiar / pegar en un caparazón.
Mientras que, si se usa %r
, el error sería:
ERROR: Could not open ''some path/with /'strange/' "characters"''
Fácil de analizar visualmente, fácil de copiar y pegar, todo mejor.
++n
y - ++n
pueden no funcionar como se espera de personas procedentes de C o Java.
++n
es positivo de un número positivo, que es simplemente n
.
--n
es negativo de un número negativo, que es simplemente n
.
No use el índice para recorrer una secuencia
No hacer:
for i in range(len(tab)) :
print tab[i]
Hacer:
for elem in tab :
print elem
Porque automatizará la mayoría de las operaciones de iteración por usted.
Use enumerate
si realmente necesita el índice y el elemento.
for i, elem in enumerate(tab):
print i, elem
Tenga cuidado al usar "==" para verificar contra verdadero o falso
if (var == True) :
# this will execute if var is True or 1, 1.0, 1L
if (var != True) :
# this will execute if var is neither True nor 1
if (var == False) :
# this will execute if var is False or 0 (or 0.0, 0L, 0j)
if (var == None) :
# only execute if var is None
if var :
# execute if var is a non-empty string/list/dictionary/tuple, non-0, etc
if not var :
# execute if var is "", {}, [], (), 0, None, etc.
if var is True :
# only execute if var is boolean True, not 1
if var is False :
# only execute if var is boolean False, not 0
if var is None :
# same as var == None
No verifique si puede, solo hágalo y maneje el error
Los pitonistas generalmente dicen "es más fácil pedir perdón que permiso".
No hacer:
if os.path.isfile(file_path) :
file = open(file_path)
else :
# do something
Hacer:
try :
file = open(file_path)
except OSError as e:
# do something
O incluso mejor con Python 2.6 / 3:
with open(file_path) as file :
Es mucho mejor porque es mucho más genérico. Puede aplicar "try / except" a casi cualquier cosa. No es necesario que se preocupe por qué hacer para evitarlo, solo sobre el error que está arriesgando.
No verifique contra el tipo
Python se tipea dinámicamente, por lo tanto, la comprobación del tipo te hace perder flexibilidad. En su lugar, use la tipificación de pato comprobando el comportamiento. Por ejemplo, esperas una cadena en una función, luego usa str () para convertir cualquier objeto en una cadena. Esperas una lista, use list () para convertir cualquier iterable en una lista.
No hacer:
def foo(name) :
if isinstance(name, str) :
print name.lower()
def bar(listing) :
if isinstance(listing, list) :
listing.extend((1, 2, 3))
return ", ".join(listing)
Hacer:
def foo(name) :
print str(name).lower()
def bar(listing) :
l = list(listing)
l.extend((1, 2, 3))
return ", ".join(l)
Usando la última manera, foo aceptará cualquier objeto. Bar aceptará cadenas, tuplas, conjuntos, listas y mucho más. DRY barato :-)
No mezcle espacios y pestañas
Simplemente no lo hagas Llorarías
Usar objeto como primer padre
Esto es complicado, pero te morderá a medida que crezca tu programa. Hay clases antiguas y nuevas en Python 2.x. Los viejos son, bueno, viejos. Carecen de algunas características y pueden tener un comportamiento incómodo con la herencia. Para ser utilizable, cualquiera de su clase debe ser del "nuevo estilo". Para hacerlo, hágalo heredar de "objeto":
No hacer:
class Father :
pass
class Child(Father) :
pass
Hacer:
class Father(object) :
pass
class Child(Father) :
pass
En Python 3.x todas las clases son de estilo nuevo para que puedas declarar la class Father:
está bien.
No inicializar atributos de clase fuera del método __init__
Las personas que vienen de otros idiomas lo encuentran tentador porque eso es lo que hace el trabajo en Java o PHP. Usted escribe el nombre de la clase, luego enumera sus atributos y les da un valor predeterminado. Parece que funciona en Python, sin embargo, esto no funciona de la manera que crees.
Hacer eso configurará atributos de clase (atributos estáticos), y luego cuando intente obtener el atributo del objeto, le dará su valor a menos que esté vacío. En ese caso, devolverá los atributos de clase.
Implica dos grandes riesgos:
- Si se cambia el atributo de clase, se cambia el valor inicial.
- Si establece un objeto mutable como valor predeterminado, obtendrá el mismo objeto compartido entre instancias.
No (a menos que quiera estática):
class Car(object):
color = "red"
wheels = [wheel(), Wheel(), Wheel(), Wheel()]
Hacer:
class Car(object):
def __init__(self):
self.color = "red"
self.wheels = [wheel(), Wheel(), Wheel(), Wheel()]
import this
Hermoso es mejor que feo.
Explícito es mejor que implícito.
Simple es mejor que complejo.
Complejo es mejor que complicado.
Flat es mejor que anidado.
Sparse es mejor que denso.
La legibilidad cuenta
Los casos especiales no son lo suficientemente especiales como para romper las reglas.
Aunque la practicidad supera a la pureza.
Los errores nunca deberían pasar silenciosamente.
A menos que esté explícitamente silenciado.
En vista de la ambigüedad, rechace la tentación de adivinar.
Debería haber una, y preferiblemente solo una, forma obvia de hacerlo.
Aunque de esa manera puede no ser obvio al principio a menos que seas holandés.
Ahora es mejor que nunca.
Aunque nunca es a menudo mejor que ahora.
Si la implementación es difícil de explicar, es una mala idea.
Si la implementación es fácil de explicar, puede ser una buena idea.
Los espacios de nombres son una gran idea: ¡hagamos más de eso!
import not_this
Escribe un código feo
Escribe un código implícito.
Escribir código complejo.
Escribir código anidado.
Escribir código denso.
Escribe un código ilegible.
Escribe casos especiales.
Esfuérzate por la pureza.
Ignora errores y excepciones.
Escriba el código óptimo antes de liberarlo.
Cada implementación necesita un diagrama de flujo.
No use espacios de nombres.
my_variable = <something>
...
my_varaible = f(my_variable)
...
use my_variable and thinking it contains the result from f, and not the initial value
Python no le advertirá de ninguna manera que en la segunda asignación escribió mal el nombre de la variable y creó una nueva.
- no escriba grandes mensajes de salida a la salida estándar
- las cadenas son inmutables: compóngalo no usando el operador "+" sino usando la función str.join ().
- lee esos artículos:
El último enlace es el original, esta pregunta SO es un duplicado.