2009-03-18 12 views
7

Pregunta originalPython: ¿está bien devolver booleanos y cadenas?

He hecho una función que está esperando que una cadena en particular aparezca en un puerto serie, y devuelve todo el carácter leído hasta que se encuentre la cadena, o falso si no. Esto es bastante conveniente, pero me preguntaba si se considera una mala práctica o no.

Aclaración:

El objetivo principal es que esperar a una determinada cadena dea aparecer en una determinada cantidad de tiempo. Excepto por el error IO, el resultado posible es Verdadero (la cadena apareció) o Falso El objetivo secundario es obtener todo el resultado, porque puede haber información que me gustaría analizar antes de la respuesta real que se busca. Pensé que podría ser que podría combinar el objetivo primario y secundario en un valor de retorno.

def MyFunc(s, timeout) : 
    test = get_some_input(timeout) 
    if test.endswith(s) 
     return test 
    else 
     return False 

Editar: Otra respuesta propuesta es hacer una excepción. No creo que sea una buena idea, porque el tiempo de espera es un comportamiento esperado. Quiero decir, si hay un parámetro para especificar un tiempo de espera, entonces un tiempo de espera es un posible resultado, y no una excepción.

Editar 2: Como necesito almacenar la entrada, tal vez usar una clase es la solución correcta. La función de espera tiene un valor de retorno claro, pero también se puede acceder a toda la cadena que se leyó hasta el tiempo de espera.

class Parser : 
     def __init__(self, sport_name): 
       self.currentMsg = '' 
       self.ser = serial.Serial(sport_name, 115200) 
     def WaitFor(self, s, timeOut=None): 
       self.ser.timeout = timeOut 
       self.currentMsg = '' 
       while self.currentMsg.endswith(s) != True : 
         # should add a try catch here 
         c=self.ser.read() 
         if c != '' : 
           self.currentMsg += c 
         else : 
           print 'timeout waiting for ' + s 
           return False 
       return True 

Respuesta

6

Sería mejor devolver una cadena AND un booleano (como en el título) en lugar de devolver una cadena O un booleano. No debería tener que averiguar qué significa el valor de retorno. Debe ser totalmente explícito y los problemas ortogonales deben separarse en diferentes variables.

(okay,value) = get_some_input(blah); 
if (okay): print value 

Tiendo a no devolver las tuplas mucho, porque se siente gracioso. Pero es perfectamente válido para hacerlo.

Devolución "Ninguno" es una solución válida, ya mencionada aquí.

+0

Esa cosa ortogonal está perfectamente bien. Así que devolver una tupla sería una buena solución, sin embargo, todavía no me siento cómodo con esto, y el uso de una clase me permite incorporar un puerto serie y permite una mayor extensión. – shodanex

+0

Entonces, ¿cuál debería ser el valor si está bien? ¿Falso? – SilentGhost

+0

@SilentGhost: el valor siempre es una lectura de entrada hasta ahora (hasta que se encuentra la cadena o se produce un tiempo de espera). – jfs

25

¿No sería más adecuado para devolver un None en lugar de False?

+0

sí, ninguno sería más adecuado –

+0

pero como se describe en otras respuestas, una excepción es aún más pitónico. –

+0

Una excepción no es Pythonic en este caso. Observe el comportamiento de la biblioteca estándar, en particular select.select y re.match. –

5

Lo conveniente es devolver una cadena vacía en este caso.

Además una cadena vacía en Python evaluará a False de todos modos. Así que se podría llamar así:

if Myfunc(s, timeout): 
    print "success" 

Adición: Como ha señalado S. Lott el verdadero camino Pythonic es volver Ninguno. Aunque elijo devolver cadenas en funcs relacionados con cadenas. Una cuestión de preferencia, de hecho.

También asumo que a la persona que llama de Myfunc solo le preocupa manipular una cadena, vacía o no. Si la persona que llama necesita verificar los tiempos de espera, etc., es mejor usar excepciones o devolver None.

+0

El problema con este diseño es que no distingue entre el tiempo de espera en un caso, y s y la prueba es igual a "". –

+0

-1: No muy pitónico en absoluto. Las cadenas vacías todavía son cadenas. Ninguno es mejor Una excepción es mejor aún. –

+0

Ambos puntos válidos, gents, corrigieron la respuesta. –

3

Tal vez si devuelve una tupla como (False, None) y (True, test) sería mejor, ya que puede evaluarlos por separado y no agregar complejidad innecesaria.

EDITAR: Tal vez la cadena que apareció en el puerto serie es "" (tal vez esperada), por lo que al devolver True puede decir que llegó de esa manera.

+0

Prefiero tomar la forma de @jelovirt: la prueba de None no es ambigua y realmente no agrega complejidad. –

+0

Siempre me han gustado las tuplas de python para devolver varios valores. +1. – paxdiablo

+0

Me gusta esta idea, devolver una bandera de control en el mismo campo de valor no me suena bien. En ese tipo de situaciones, corre el riesgo de que en el futuro los valores válidos cambien (como usted dijo, permitiendo nulo) y la bandera de control se vuelva inválida. – Sam

5

Puede devolver la cadena si llegó a tiempo, o generar una excepción adecuada que indique el tiempo de espera.

+0

Incluso si presentar una excepción en Python es de bajo costo en comparación con otros idiomas, no creo que sea una gran idea proporcionarles la funcionalidad de usarlos. – Martin

+0

+1: Las excepciones tienen más sentido en este caso, tiene una condición de "excepción", un tiempo de espera excedido. –

+0

De hecho, he utilizado este patrón mucho y con mucho éxito en un proyecto que usa comunicación a través de puertos seriales, etc. La excepción de tiempo de espera maneja los reintentos y reenvía las condiciones bastante bien. – Ber

10

Creo que el diseño ortodoxo de Python sería devolver None. El manual dice:

Ninguno

Este tipo tiene un valor único. Hay un solo objeto con este valor.Se accede a este objeto a través del nombre incorporado Ninguno. Se usa para significa la ausencia de un valor en muchas situaciones , por ejemplo, se devuelve desde las funciones que no devuelven explícitamente nada. Su valor de verdad es falso.

2

Para agregar al punto de Ber, es posible que desee tomar algo más en cuenta. Si usa una cadena vacía o None, deja la puerta abierta para errores de la variedad "tonto". Por otro lado, si lanza una excepción, está forzando la ejecución de cualquier operación que se ejecute para que se anule.

Por ejemplo, considere el siguiente código:

result = MyFunc(s, timeout) 
if result[0] == 'a': 
    do_something() 

Esto levantará una excepción si la operación ha agotado y se puso una cadena vacía o Ninguno. Por lo que tendría que cambiar a:

result = MyFunc(s, timeout) 
if result and result[0] == 'a': 
    do_something() 

Este tipo de cambios tienden a sumar y hacer que el código sea más difícil de entender.

Por supuesto, estoy seguro de que su respuesta a esto será algo así como "No haré eso" o "Eso no sucederá" a lo que mi respuesta es "Incluso si no lo haces" toparse con esta función, eventualmente si tienes el hábito de hacer esto ". Este tipo de errores casi siempre son el resultado de casos de esquina en los que generalmente no piensas.

1

Este es un caso de uso clásico para los generadores Python. La palabra clave yield proporciona una forma sencilla para repetir conjuntos discretos sin devolver todo a la vez:

def MyFunc(s, timeout) : 
    test = get_some_input(timeout) 
    while test.endswith(s) 
     yield test 
     test = get_some_input(timeout) 

for input in MyFunc(s, timeout): 
    print input 

La clave aquí es que no hay valor de retorno para especificar el final de la entrada; en su lugar, simplemente llegas al final del iterador. Más información sobre los generadores here.

Cuestiones relacionadas