2009-03-02 7 views
11

Python no recomienda verificar los tipos. Pero en muchos casos esto puede ser útil:¿Debo verificar los tipos de argumentos de constructor (y en otros lugares también)?

  1. Comprobación de los argumentos del constructor. p.ej. checking foe Boolean, string, dict, etc. Si no lo hago y establezco los miembros del objeto en los argumentos, causará problemas más adelante.

  2. Comprobando los argumentos de las funciones.

  3. En propiedades. Si alguien establece un valor incorrecto o un tipo diferente, debería responder rápidamente.

+0

Python tipo de falla - no escala; Prefiero usar Mono. –

Respuesta

9

La respuesta simple es Sin, utilice polimorfismo, excepciones, etc.

  1. En el caso de los argumentos del constructor son del tipo incorrecto, una excepción serán lanzados durante la ejecución de código que dependen s en el parámetro siendo de un tipo particular. Si es algo extraño, específico del dominio, crea tu propia Excepción. Bloques envolventes de código que pueden fallar con try-except y manipular errores. Por lo tanto, es mejor usar el manejo de excepciones. (Igual va para los argumentos de funciones)

  2. En propiedades, se aplica el mismo argumento. Si está validando el valor recibido, use una aserción para verificar su rango, etc. Si el valor es del tipo incorrecto, fallará de todos modos. Luego, maneje AssertionError.

En Python, usted trata a los programadores como seres inteligentes !! Solo documente bien su código (haga las cosas obvias), levante Excepciones donde corresponda, escriba código polimórfico, etc. Deje el manejo de Excepción (cuando sea apropiado solamente)/errores en la construcción del código del cliente.

Advertencia
Dejando El manejo de excepciones a los clientes, no significa que usted debe tirar un montón de errores de basura en el usuario sin saberlo. Si es posible, maneje las excepciones que puedan ocurrir debido a una mala construcción o cualquier otra razón en su código. Tu código debe ser robusto. ¡Donde sea imposible para usted manejar el error, informe amablemente al programador de códigos de usuario/cliente!

Nota
En general, los malos argumentos a un constructor no es algo que me preocupa demasiado.

4

Marque todo lo que desea, solo tiene que ser explícito. El siguiente ejemplo es un constructor de a module en la biblioteca estándar - se comprueba el extrasaction arg:

class DictWriter: 
    def __init__(self, f, fieldnames, restval="", extrasaction="raise", 
       dialect="excel", *args, **kwds): 
     self.fieldnames = fieldnames # list of keys for the dict 
     self.restval = restval   # for writing short dicts 
     if extrasaction.lower() not in ("raise", "ignore"): 
      raise ValueError, \ 
       ("extrasaction (%s) must be 'raise' or 'ignore'" % 
       extrasaction) 
     self.extrasaction = extrasaction 
     self.writer = writer(f, dialect, *args, **kwds) 
11

La respuesta es casi siempre "no". La idea general en Python, Ruby y algunos otros idiomas nos llama "Duck Typing". No debería importarte qué es algo, solo cómo funciona. En otras palabras, "si todo lo que quieres es algo que grazna, no necesitas comprobar que en realidad es un pato".

En la vida real, el problema con la instalación de todos esos controles de tipo es la imposibilidad de reemplazar entradas con implementaciones alternativas. Puede verificar si hay dict, pero es posible que desee pasar algo que no sea un dict pero que implemente el dict API.

La comprobación de tipo solo comprueba uno de los muchos posibles errores en el código. Por ejemplo, no incluye la verificación de rango (al menos no en Python). Una respuesta moderna a la afirmación de que debe haber verificación de tipos es que es más efectivo desarrollar pruebas unitarias que aseguren que no solo los tipos sean correctos, sino también que la funcionalidad sea correcta.

Otro punto de vista es que debe tratar a los usuarios de su API como adultos que consienten y confiar en que utilicen la API correctamente. Por supuesto, hay momentos en que la verificación de entrada es útil, pero eso es menos común de lo que piensas. Un ejemplo es la entrada de fuentes que no son de confianza, como desde la web pública.

+0

No confío en que mis usuarios de API acepten adultos. Veo el código que escriben. –

+0

No podría estar más de acuerdo. La comprobación de tipos en una función tiene un gran impacto en la legibilidad. Usa los asertos (ver dangph a continuación) en desarrollo, y elimínalos con 'python -O' para producción. Con buenas pruebas de unidad, eso es todo lo que necesita. –

+0

Si los desarrolladores no pueden ser "adultos con consentimiento", ese es su problema. Mientras existan documentos API para que puedan verificar los tipos de argumentos esperados, está bien. A MENOS QUE desee responder específicamente a cierto tipo. –

0

"Si no lo hago y establezco los miembros del objeto en los argumentos, causará problemas más adelante".

Sea muy claro en la lista exacta de "problemas" que se producirán más adelante.

  • ¿No funcionará en absoluto? Para qué sirven los bloques try/except.

  • ¿Se comportará "extrañamente"? Esto es realmente raro y está limitado a tipos y operadores "near miss". El ejemplo estándar es división. Si espera números enteros, pero tiene punto flotante, entonces la división puede no hacer lo que usted quería. Pero eso está arreglado con los operadores //, vs./division.

  • ¿Será simplemente incorrecto, pero aún parece que se complete? Esto es realmente raro, y requeriría un tipo bastante cuidadosamente elaborado que usara nombres estándar, pero que no sean estándares. Por ejemplo

    class MyMaliciousList(list): 
        def append(self, value): 
         super(MyMaliciousList, self).remove(value) 
    

Aparte de eso, es difícil tener las cosas "causar problemas más adelante". Por favor actualice su pregunta con ejemplos específicos de "problemas".

1

A menudo es algo bueno de hacer. Probablemente, la comprobación de tipos explícitos no sea tan útil en Python (como han dicho otros), pero la comprobación de valores legales puede ser una buena idea. La razón por la cual es una buena idea es que el software fallará más cerca de la fuente del error (sigue el Principio de Fail Fast). Además, los cheques actúan como documentación para otros programadores y usted mismo. Mejor aún, es la "documentación ejecutable", que es buena porque es documentación que no puede mentir.

Una manera rápida y sucia, pero razonable para revisar sus argumentos es utilizar afirman:

def my_sqrt(x): 
    assert x >= 0, "must be greater or equal to zero" 
    # ... 

hacer valer sus argumentos es un tipo de diseño pobre del hombre por contrato. (Es posible que desee buscar Diseño por contrato; es interesante.)

1

AFAIU, desea asegurarse de que algunos objetos se comportan ("seguir una interfaz") en un momento anterior al del uso real. En su ejemplo, desea saber que los objetos son apropiados en el momento de creación de la instancia, no cuando realmente se utilizarán.

Teniendo en cuenta que estamos hablando de Python aquí, no sugeriré assert (¿qué ocurre si python -O o una variable de entorno PYTHONOPTIMIZE está configurada en 1 cuando se ejecuta el programa?) O la comprobación de tipos específicos (debido a que restringe innecesariamente los tipos que puede utilizar), pero voy a sugerir las primeras pruebas funcionalidad, algo a lo largo de las líneas:

def __init__(self, a_number, a_boolean, a_duck, a_sequence): 

    self.a_number= a_number + 0 

    self.a_boolean= not not a_boolean 

    try: 
     a_duck.quack 
    except AttributeError: 
     raise TypeError, "can't use it if it doesn't quack" 
    else: 
     self.a_duck= a_duck 

    try: 
     iter(a_sequence) 
    except TypeError: 
     raise TypeError, "expected an iterable sequence" 
    else: 
     self.a_sequence= a_sequence 

Solía ​​try… except… else en esta sugerencia porque quiero establecer los miembros de instancia única si la prueba tuvo éxito, incluso si el código se cambia o aumenta. No tienes que hacerlo así, obviamente.

Para argumentos de funciones y propiedades de configuración, no haría estas pruebas con antelación, solo usaría los objetos provistos y actuaría en excepciones lanzadas, a menos que los objetos sospechosos vayan a usarse después de un largo proceso.

0

Como dice Dalke, la respuesta es casi siempre "no". En Python, por lo general no se preocupan de que un parámetro es un cierto tipo, sino más bien que se comporta como un cierto tipo. Esto se conoce como "Duck Typing". Hay dos maneras de probar si un parámetro se comporta como un tipo determinado: (1) se puede utilizar como si se comportó como se esperaba y lanzar una excepción cuando/si no lo hace, o (2) se puede definir una interfaz que describe cómo ese tipo debe comportarse y probar la conformidad con esa interfaz.

zope.interface es mi sistema de interfaz preferida para Python, pero hay varios otros. Con cualquiera de ellos, define una interfaz, luego declara que un tipo dado se ajusta a esa interfaz o define un adaptador que convierte su tipo en algo que se ajusta a esa interfaz. A continuación, puede afirmar (o probar como desee) que los parámetros proporcionan (en la terminología zope.interface) esa interfaz.

Cuestiones relacionadas