Sobre a digitação de pato :
A tipagem de pato é auxiliada por habitualmente não testar o tipo de argumentos nos corpos de métodos e funções, baseando-se na documentação, código claro e teste para garantir o uso correto.
Sobre a validação de argumentos (EAFP: Mais fácil pedir perdão do que permissão). Um exemplo adaptado daqui :
... é considerado mais pitônico:
def my_method(self, key):
try:
value = self.a_dict[member]
except TypeError:
# do something else
Isso significa que qualquer pessoa que use seu código não precisará usar um dicionário ou subclasse real - eles podem usar qualquer objeto que implemente a interface de mapeamento.
Infelizmente, na prática, não é assim tão simples. E se o membro no exemplo acima puder ser um número inteiro? Os números inteiros são imutáveis - portanto, é perfeitamente razoável usá-los como chaves de dicionário. No entanto, eles também são usados para indexar objetos do tipo sequência. Se um membro for um número inteiro, o exemplo dois poderá liberar listas e strings, além de dicionários.
Sobre programação assertiva :
As asserções são uma maneira sistemática de verificar se o estado interno de um programa é o esperado pelo programador, com o objetivo de detectar bugs. Em particular, eles são bons para capturar suposições falsas feitas ao escrever o código ou abusar de uma interface por outro programador. Além disso, eles podem atuar como documentação in-line até certo ponto, tornando óbvias as suposições do programador. ("Explícito é melhor que implícito.")
Os conceitos mencionados às vezes estão em conflito, por isso conto com os seguintes fatores ao escolher se não faço nenhuma validação de dados, faço uma validação forte ou utilizo declarações:
Validação forte. Por forte validação, quero dizer criar uma exceção personalizada (
ApiError
por exemplo). Se minha função / método fizer parte de uma API pública, é melhor validar o argumento para mostrar uma boa mensagem de erro sobre tipo inesperado. Ao marcar o tipo, não quero dizer apenas o usoisinstance
, mas também se o objeto transmitido suporta a interface necessária (digitação de pato). Enquanto documento a API e especifico o tipo esperado e o usuário pode querer usar minha função de maneira inesperada, me sinto mais seguro quando verifico as suposições. Normalmente, usoisinstance
e, se quiser apoiar outros tipos ou patos, alterarei a lógica de validação.Programação assertiva. Se o meu código é novo, eu uso muito. Quais são os seus conselhos sobre isso? Mais tarde, você remove declarações do código?
Se minha função / método não faz parte de uma API, mas transmite alguns de seus argumentos para outro código não escrito, estudado ou testado por mim, eu faço várias afirmações de acordo com a interface chamada. Minha lógica por trás disso - é melhor falhar no meu código, em algum lugar 10 níveis mais profundo no rastreamento de pilha com erro incompreensível, o que força a depurar muito e depois adicionar a declaração ao meu código de qualquer maneira.
Comentários e conselhos sobre quando usar ou não a validação de tipo / valor, afirma? Desculpe por não ser a melhor formulação da questão.
Por exemplo, considere a seguinte função, onde Customer
está um 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
Portanto, existem várias maneiras de lidar com a validação:
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
ou
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
Quando e por que você usaria cada uma delas no contexto de digitação de patos, verificação de tipo, validação de dados?