funcion - python yield explicacion
Usando try vs si en python (9)
¿Cuál de los siguientes sería más preferible y por qué?
Look Before You Leap es preferible en este caso. Con el enfoque de excepción, un TypeError podría ocurrir en cualquier parte de tu cuerpo de bucle y sería atrapado y desechado, que no es lo que deseas y hará que la depuración sea difícil.
(Estoy de acuerdo con Brandon Corfman, sin embargo: devolver None para ''no items'' en lugar de una lista vacía está roto. Es un hábito desagradable de los codificadores de Java que no deberían verse en Python.
¿Hay alguna razón para decidir cuál de las try
o if
debe usar para construir, cuando se prueba una variable para tener un valor?
Por ejemplo, hay una función que devuelve una lista o no devuelve un valor. Quiero verificar el resultado antes de procesarlo. ¿Cuál de los siguientes sería más preferible y por qué?
result = function();
if (result):
for r in result:
#process items
o
result = function();
try:
for r in result:
#process items
except TypeError:
pass;
Discusión relacionada:
A menudo escuchas que Python fomenta el estilo EAFP ("es más fácil pedir perdón que permiso") sobre el estilo LBYL ("mira antes de saltar"). Para mí, es una cuestión de eficiencia y legibilidad.
En su ejemplo (supongamos que en lugar de devolver una lista o una cadena vacía, la función fuera devolver una lista o None
), si espera que el resultado del 99% del tiempo contenga algo iterable, usaría el try/except
enfoque. Será más rápido si las excepciones son realmente excepcionales. Si el result
es None
más del 50% del tiempo, entonces usar if
es probablemente mejor.
Para apoyar esto con algunas medidas:
>>> import timeit
>>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
0.06379691968322732
>>> timeit.timeit(setup="a=1;b=1", stmt="try:/n a/b/nexcept ZeroDivisionError:/n pass")
0.0829463709378615
>>> timeit.timeit(setup="a=1;b=0", stmt="try:/n a/b/nexcept ZeroDivisionError:/n pass")
0.5070195056614466
>>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:/n a/b")
0.11940114974277094
>>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:/n a/b")
0.051202772912802175
Entonces, mientras que una sentencia if
siempre te cuesta, es casi gratis configurar un bloque try/except
. Pero cuando ocurre una Exception
, el costo es mucho mayor.
Moral:
- Está perfectamente bien (y "pitónico") usar
try/except
del control de flujo, - pero tiene sentido más cuando
Exception
s son realmente excepcionales.
De los documentos de Python:
EAFP
Es más fácil pedir perdón que permiso. Este estilo de codificación de Python común asume la existencia de claves o atributos válidos y atrapa excepciones si la suposición es falsa. Este estilo limpio y rápido se caracteriza por la presencia de muchas instrucciones
try
yexcept
. La técnica contrasta con el estilo LBYL común en muchos otros lenguajes, como C.
Como regla general, nunca debe usar try / catch o cualquier excepción que maneje cosas para controlar el flujo. Aunque detrás de escena la iteración se controla mediante el aumento de StopIteration
excepciones de StopIteration
, aún así debería preferir su primer fragmento de código al segundo.
En general, la impresión que tengo es que las excepciones deben reservarse para circunstancias excepcionales. Si se espera que el result
nunca esté vacío (pero podría serlo, por ejemplo, si un disco se colgó, etc.), el segundo enfoque tiene sentido. Si, por otro lado, un result
vacío es perfectamente razonable en condiciones normales, probarlo con una instrucción if
tiene más sentido.
Tenía en mente el escenario (más común):
# keep access counts for different files
file_counts={}
...
# got a filename somehow
if filename not in file_counts:
file_counts[filename]=0
file_counts[filename]+=1
en lugar de su equivalente:
...
try:
file_counts[filename]+=1
except KeyError:
file_counts[filename]=1
En lo que respecta al rendimiento, usar try block para el código que normalmente no genera excepciones es más rápido que usar el enunciado if cada vez. Entonces, la decisión depende de la probabilidad de casos excepcionales.
Ignore mi solución si el código que proporciono no es obvio a primera vista y debe leer la explicación después de la muestra del código.
¿Puedo suponer que el "valor sin retorno" significa que el valor de retorno es Ninguno? Si es así, o si el "valor nulo" es falso booleano, puede hacer lo siguiente, ya que su código esencialmente trata "sin valor" como "no iterar":
for r in function() or ():
# process items
Si function()
devuelve algo que no es True, itera sobre la tupla vacía, es decir, no ejecuta ninguna iteración. Esto es esencialmente LBYL.
Su función no debe devolver tipos mixtos (es decir, lista o cadena vacía). Debería devolver una lista de valores o solo una lista vacía. Entonces no necesitarías probar nada, es decir, tu código colapsa a:
for r in function():
# process items
Su segundo ejemplo está roto: el código nunca arrojará una excepción TypeError ya que puede iterar a través de ambas cadenas y listas. La iteración a través de una cadena o lista vacía también es válida: ejecutará el cuerpo del ciclo cero veces.
bobince sabiamente señala que envolver el segundo caso también puede atrapar a TypeErrors en el bucle, que no es lo que quieres. Si realmente quieres probar, puedes probar si es iterable antes del ciclo
result = function();
try:
it = iter(result)
except TypeError:
pass
else:
for r in it:
#process items
Como pueden ver, es bastante feo. No lo sugiero, pero debe mencionarse para completarlo.