Sobre la escritura de pato :
La tipificación de los patos se ayuda al no probar habitualmente el tipo de argumentos en los cuerpos de métodos y funciones, confiando en la documentación, el código claro y las pruebas para garantizar un uso correcto.
Acerca de la validación de argumentos (EAFP: es más fácil pedir perdón que permiso). Un ejemplo adaptado de aquí :
... se considera más pitónico hacer:
def my_method(self, key):
try:
value = self.a_dict[member]
except TypeError:
# do something else
Esto significa que cualquier otra persona que use su código no tiene que usar un diccionario real o una subclase; puede usar cualquier objeto que implemente la interfaz de mapeo.
Desafortunadamente en la práctica no es tan simple. ¿Qué pasa si el miembro en el ejemplo anterior podría ser un número entero? Los enteros son inmutables, por lo que es perfectamente razonable usarlos como claves de diccionario. Sin embargo, también se utilizan para indexar objetos de tipo secuencia. Si el miembro es un número entero, el ejemplo dos podría dejar pasar listas y cadenas, así como diccionarios.
Sobre programación asertiva :
Las afirmaciones son una forma sistemática de verificar que el estado interno de un programa es el esperado por el programador, con el objetivo de detectar errores. En particular, son buenos para detectar suposiciones falsas que se hicieron al escribir el código, o el abuso de una interfaz por parte de otro programador. Además, pueden actuar como documentación en línea hasta cierto punto, haciendo obvias las suposiciones del programador. ("Explícito es mejor que implícito")
Los conceptos mencionados a veces están en conflicto, por lo que cuento con los siguientes factores al elegir si no hago ninguna validación de datos, si hago una validación fuerte o uso afirmaciones:
Fuerte validación. Por validación fuerte me refiero a generar una excepción personalizada (
ApiError
por ejemplo). Si mi función / método es parte de una API pública, es mejor validar el argumento para mostrar un buen mensaje de error sobre un tipo inesperado. Al marcar el tipo, no me refiero a usar soloisinstance
, sino también si el objeto pasado admite la interfaz necesaria (escritura de pato). Si bien documenté la API y especifiqué el tipo esperado y el usuario podría querer usar mi función de manera inesperada, me siento más seguro cuando verifico los supuestos. Usualmente usoisinstance
y si luego quiero admitir otros tipos o patos, cambio la lógica de validación.Programación asertiva. Si mi código es nuevo, uso afirma mucho. ¿Cuáles son tus consejos sobre esto? ¿Luego eliminas las afirmaciones del código?
Si mi función / método no es parte de una API, pero pasa algunos de sus argumentos a otro código no escrito, estudiado o probado por mí, hago muchas afirmaciones de acuerdo con la interfaz llamada. Mi lógica detrás de esto: mejor falla en mi código, luego en algún lugar 10 niveles más profundos en stacktrace con un error incomprensible que obliga a depurar mucho y luego agregar la afirmación a mi código de todos modos.
¿Comentarios y consejos sobre cuándo usar o no usar la validación de tipo / valor, afirma? Lo siento, no es la mejor formulación de la pregunta.
Por ejemplo, considere la siguiente función, donde Customer
es un modelo declarativo SQLAlchemy:
def add_customer(self, customer):
"""Save new customer into the database.
@param customer: Customer instance, whose id is None
@return: merged into global session customer
"""
# no validation here at all
# let's hope SQLAlchemy session will break if `customer` is not a model instance
customer = self.session.add(customer)
self.session.commit()
return customer
Entonces, hay varias formas de manejar la validación:
def add_customer(self, customer):
# this is an API method, so let's validate the input
if not isinstance(customer, Customer):
raise ApiError('Invalid type')
if customer.id is not None:
raise ApiError('id should be None')
customer = self.session.add(customer)
self.session.commit()
return customer
o
def add_customer(self, customer):
# this is an internal method, but i want to be sure
# that it's a customer model instance
assert isinstance(customer, Customer), 'Achtung!'
assert customer.id is None
customer = self.session.add(customer)
self.session.commit()
return customer
¿Cuándo y por qué usaría cada uno de estos en el contexto de la escritura, verificación de tipos y validación de datos?