¿Debo verificar los tipos de argumentos de constructor (y también en otros lugares)?

StackOverflow https://stackoverflow.com/questions/602046

  •  03-07-2019
  •  | 
  •  

Pregunta

Python desaconseja la comprobación de los tipos. Pero en muchos casos esto puede ser útil:

  1. Comprobando los argumentos del constructor. p.ej. revisando al enemigo Booleano, cadena, dictado, etc. Si no lo hago y configuro los miembros del objeto a los argumentos, esto 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.

¿Fue útil?

Solución

La respuesta simple es No , usa Polimorfismo, Excepciones, etc.

  1. En el caso de que los argumentos del constructor sean del tipo incorrecto, se lanzará una excepción al ejecutar el código que depende de que el parámetro sea de un tipo particular. Si es una cosa rara, específica de dominio, levante su propia excepción. Rodee los bloques de código que pueden fallar con try-except y maneje los errores. Así que es mejor usar el manejo de excepciones. ( Lo mismo ocurre con los argumentos de función )

  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, tratas a los programadores como seres inteligentes! Simplemente documente su código (haga las cosas más obvias), genere excepciones cuando sea apropiado, escriba código polimórfico, etc. Deje el manejo de Excepciones (cuando sea apropiado solamente) / errores en la construcción al código del cliente.

Advertencia
Dejar el manejo de Excepciones a los clientes no significa que deba tirar muchos errores de basura al usuario involuntario. Si es posible, maneje las excepciones que puedan ocurrir debido a una construcción incorrecta o cualquier otra razón en su propio código. Su código debe ser robusto. Cuando sea imposible para usted manejar el error, informe amablemente al programador de código de usuario / cliente.

Nota
En general, los malos argumentos para un constructor no es algo de lo que me preocupe demasiado.

Otros consejos

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

En la vida real, el problema de poner en todas esas verificaciones de tipo es la incapacidad de reemplazar las entradas con implementaciones alternativas. Puede verificar el dictado, pero es posible que desee pasar algo en el que no sea un dict, sino que implemente la API dict.

La comprobación de tipo solo comprueba uno de los muchos errores posibles 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 es necesario que haya una 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 debes tratar a los usuarios de tu API como adultos que consienten y confiar en que usen la API correctamente. Por supuesto, hay ocasiones en que la verificación de entrada es útil, pero eso es menos común de lo que cree. Un ejemplo es la entrada de fuentes no confiables, como de la web pública.

Marque todo lo que quiera, solo tiene que ser explícito. El siguiente ejemplo es un constructor de un módulo en la biblioteca estándar - comprueba el extraaction 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)

A menudo es una buena cosa que hacer. La comprobación de tipos explícitos probablemente no sea tan útil en Python (como han dicho otros), pero verificar los valores legales puede ser una buena idea. La razón por la que es una buena idea es que el software fallará más cerca de la fuente del error (sigue el principio de falla rápida). Además, los controles actúan como documentación para otros programadores y para usted. Aún mejor, es " documentación ejecutable " ;, lo que es bueno porque es documentación que no puede mentir.

Una forma rápida y sucia pero razonable de verificar tus argumentos es usar assert:

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

Afirmar sus argumentos es un tipo de diseño por contrato del pobre. (Es posible que desee consultar Diseño por contrato; es interesante).

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

Teniendo en cuenta que estamos hablando de Python aquí, no sugeriré assert (¿qué pasaría si python -O o una variable de entorno PYTHONOPTIMIZE se establezca en 1 cuando ¿se ejecuta su programa?) o se comprueban tipos específicos (porque eso restringe innecesariamente los tipos que puede usar), pero sugeriré la funcionalidad de , algo similar a lo siguiente:

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

Utilicé intente & # 8230; excepto & # 8230; else en esta sugerencia porque quiero establecer los miembros de la instancia solo si la prueba tuvo éxito, incluso si el código se modifica o aumenta. No tienes que hacerlo así, obviamente.

Para los argumentos de la función y las propiedades de configuración, no haría estas pruebas de antemano, solo usaría los objetos proporcionados y actuaría sobre las excepciones lanzadas, a menos que los objetos sospechosos se vayan a usar después de un largo proceso.

" Si no lo hago y configuro los miembros del objeto a los argumentos, esto causará problemas más adelante. "

Sea muy claro en la lista exacta de " problemas " que será causado más tarde.

  • ¿No funcionará en absoluto? Para eso están los bloques try / except.

  • ¿Se comportará " de forma extraña " ;? Esto es realmente raro, y se limita a " casi-miss " Tipos y operadores. El ejemplo estándar es la división. Si esperabas enteros, pero obtuviste un punto flotante, entonces la división podría no hacer lo que querías. Pero eso se soluciona con los operadores //, vs. / division.

  • ¿Estará simplemente mal, pero aun así parece que se completa? Esto es realmente raro, y requeriría un tipo bastante bien diseñado que usara nombres estándar, pero que hiciera cosas no estándar. Por ejemplo

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

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

Como dice Dalke, la respuesta es casi siempre " no " ;. En Python, generalmente no le importa que un parámetro sea de cierto tipo, sino que se comporte como un cierto tipo. Esto se conoce como " Duck Typing " ;. Hay dos formas de probar si un parámetro se comporta como un tipo dado: (1) puede usarlo como si se comportara como esperaba y lanzar una excepción cuando / si no lo hace o (2 ) puede definir una interfaz que describa cómo ese tipo debe comportarse y probar la conformidad con esa interfaz.

zope.interface es mi interfaz preferida Sistema 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 sí cumple con esa interfaz. Luego puede afirmar (o probar como desee) que los parámetros proporcionan (en la terminología zope.interface) esa interfaz.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top