A melhor prática para Python assert
Pergunta
-
Existe um problema de desempenho ou a manutenção do código com o uso
assert
como parte do código padrão em vez de usá-lo apenas para fins de depuração?é
assert x >= 0, 'x is less than zero'
melhor ou pior do que
if x < 0: raise Exception, 'x is less than zero'
-
Além disso, existe alguma maneira para definir uma regra de negócio como
if x < 0 raise error
que é sempre verificada sem atry/except/finally
assim, se a qualquer momento durante todo ox
código é menor que 0 é accionado um erro, como se você conjuntoassert x < 0
em o início de uma função, em qualquer lugar dentro da função ondex
torna-se menos, em seguida, 0 uma exceção?
Solução
Para ser capaz de lançar automaticamente um erro quando x tornar-se menos do que zero em toda a função. Você pode usar href="http://docs.python.org/reference/datamodel.html#implementing-descriptors" descritores de classe . Aqui está um exemplo:
class LessThanZeroException(Exception):
pass
class variable(object):
def __init__(self, value=0):
self.__x = value
def __set__(self, obj, value):
if value < 0:
raise LessThanZeroException('x is less than zero')
self.__x = value
def __get__(self, obj, objType):
return self.__x
class MyClass(object):
x = variable()
>>> m = MyClass()
>>> m.x = 10
>>> m.x -= 20
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "my.py", line 7, in __set__
raise LessThanZeroException('x is less than zero')
LessThanZeroException: x is less than zero
Outras dicas
afirma deve ser usado para condições de teste que nunca deveria acontecer . O objetivo é bater cedo no caso de um estado programa corrupto.
As excepções devem ser usados ??para erros que podem concebivelmente acontecer, e você deve quase sempre criar suas próprias classes de exceção .
Por exemplo, se você está escrevendo uma função para ler a partir de um arquivo de configuração em um dict
, formatação inadequada no arquivo deve levantar uma ConfigurationSyntaxError
, enquanto você pode assert
que você não está prestes a None
retorno.
No seu exemplo, se x
é um valor definido por meio de uma interface de utilizador ou a partir de uma fonte externa, uma exceção é o melhor.
Se x
só é definido por seu próprio código no mesmo programa, ir com uma afirmação.
declarações "assert" são removidos quando a compilação é otimizado . Então, sim, há tanto o desempenho e diferenças funcionais.
O gerador de código atual não emite código para uma instrução assert quando a otimização é solicitado em tempo de compilação. - Python 2.6.4 Docs
Se você usar assert
para implementar a funcionalidade do aplicativo, em seguida, otimizar a implantação de produção, você será atormentado por "mas-it-obras em dev" defeitos.
Os quatro fins de assert
Suponha que você trabalha em 200.000 linhas de código com quatro colegas Alice, Bernd, Carl, e Daphne. Eles chamam seu código, você chamar seu código.
Então assert
tem quatro papéis :
-
Informar Alice, Bernd, Carl, e Daphne o que sua espera de código.
Suponha que você tenha um método que processa uma lista de tuplas e a lógica do programa pode quebrar se essas tuplas não são imutáveis:def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))
Este é mais confiável do que a informação equivalente na documentação e muito mais fácil de manter.
-
Informar o computador que seus espera de código.
assert
impõe um comportamento adequado dos chamadores do seu código. Se seu código chama sua chama o código de Bernd das Alices e, em seguida, sem aassert
, se o programa trava em código Alices, Bernd poderia supor que a culpa era de Alice, Alice investiga e pode-se supor que foi sua culpa, você investigar e dizer Bernd era de fato sua. Muito trabalho perdido.
Com afirma, quem recebe um erro chamada, eles rapidamente ser capaz de ver que era culpa deles, não seu. Alice, Bernd, e você todo o benefício. Salva imensas quantidades de tempo. -
Informar os leitores do seu código (incluindo você) que seu código tem alcançado em algum ponto.
Suponha que você tenha uma lista de entradas e cada um deles pode ser limpo (que é bom) ou ele pode ser smorsh, Trale, gullup, ou cintilado (que não são aceitáveis). Se é smorsh deve ser unsmorshed; se é Trale deve ser baludoed; se é gullup deve ser trotou (e então possivelmente ritmo, também); se for brilharam deve ser brilharam novamente, exceto às quintas-feiras. Você começa a idéia: É uma coisa complicada. Mas o resultado final é (ou deveria ser) que todas as entradas estão limpos. A Coisa Certa (TM) para fazer é resumir o efeito da sua a limpeza do circuito comoassert(all(entry.isClean() for entry in mylist))
Este declarações salva uma dor de cabeça para todos tentando entender o exatamente é que o laço maravilhoso está conseguindo. E o mais frequente dessas pessoas provavelmente vai ser você mesmo.
-
Informar o computador o seu código tem alcançado em algum ponto.
Se você esquecer a andar uma entrada precisar dele depois de trote, oassert
vai salvar o seu dia e evitar que o seu código breaks querido muito mais tarde. Daphne
Em minha mente, assert
é dois fins de documentação (1 e 3) e
salvaguarda (2 e 4) são igualmente valiosas.
Informando as pessoas podem até ser mais valioso do que informando o computador
porque ele pode impedir as erros os objetivos assert
de captura (no caso 1)
e muitos erros subseqüentes, em qualquer caso.
Além das outras respostas, afirma-se lançar exceções, mas apenas AssertionErrors. Do ponto de vista utilitário, afirmações não são adequados para quando você precisa de um controle de grão fino sobre o qual as exceções que você pegar.
A única coisa que está realmente errado com esta abordagem é que é difícil fazer uma exceção muito descritivo, com instruções Assert. Se você está procurando a sintaxe mais simples, lembre-se que você pode também fazer algo como isto:
class XLessThanZeroException(Exception):
pass
def CheckX(x):
if x < 0:
raise XLessThanZeroException()
def foo(x):
CheckX(x)
#do stuff here
Outro problema é que o uso de assert para a condição de verificação normal é que ela torna mais difícil para desativar a depuração afirma usando o sinalizador -o.
Como já foi dito anteriormente, afirmações deve ser usado quando o código não deve nunca chegar a um ponto, o que significa que há um bug lá. Provavelmente a razão mais útil que pode ver de usar uma afirmação é uma invariante / pre / pós-condição. Estes são algo que deve ser verdadeiro no início ou no final de cada iteração de um loop ou uma função.
Por exemplo, uma função recursiva (2 funções separadas de modo 1 alças má entrada ea outras alças código ruim, porque é difícil distinguir com recursão). Isso tornaria óbvio, se eu esqueci de escrever a instrução if, o que tinha de errado embora.
def SumToN(n):
if n <= 0:
raise ValueError, "N must be greater than or equal to 0"
else:
return RecursiveSum(n)
def RecursiveSum(n):
#precondition: n >= 0
assert(n >= 0)
if n == 0:
return 0
return RecursiveSum(n - 1) + n
#postcondition: returned sum of 1 to n
Estes invariantes de laço muitas vezes pode ser representado com uma afirmação.
A palavra do idioma Inglês assert aqui é usado no sentido de jura , afirmam , avow . Isso não significa "verificação" ou "deve ser" . Isso significa que você como um codificador está fazendo um declaração juramentada aqui:
# I solemnly swear that here I will tell the truth, the whole truth,
# and nothing but the truth, under pains and penalties of perjury, so help me FSM
assert answer == 42
Se o código está correto, salvo Single-evento viradas , falhas de hardware e tal, < strong> não assert nunca vai falhar . É por isso que o comportamento do programa para um usuário final não deve ser afectado. Especialmente, uma declaração não pode falhar, mesmo sob condições programáticas excepcionais . Ele simplesmente não acontecer. Se isso acontecer, o programador deve ser eletrocutado por isso.
é há um problema de desempenho?
-
Por favor, lembre-se de "fazê-lo funcionar antes de você fazer o trabalho rápido" .
Muito poucos por cento de qualquer programa são geralmente relevantes para a sua velocidade. Você sempre pode expulsar ou simplificar umassert
se ele nunca prova ser um problema de desempenho -. ea maioria deles nunca será -
Ser pragmático :
Suponha que você tenha um método que processa uma lista não-vazia de tuplas e a lógica do programa vai quebrar se essas tuplas não são imutáveis. Você deve escrever:def mymethod(listOfTuples): assert(all(type(tp)==tuple for tp in listOfTuples))
Este é provavelmente bom se suas listas tendem a ser as entradas dez muito tempo, mas ele pode se tornar um problema se eles têm um milhão de entradas. Mas ao invés de descartar este valioso verificação inteiramente você poderia simplesmente fazer o downgrade para
def mymethod(listOfTuples): assert(type(listOfTuples[0])==tuple) # in fact _all_ must be tuples!
que é barato, mas provavelmente vai pegar a maioria dos erros reais do programa de qualquer maneira.
Há um quadro chamado JBoss Drools para java que não runtime monitoramento de regras de negócios afirmam, que responde à segunda parte da sua pergunta. No entanto, tenho a certeza se é que existe tal estrutura para python.
Um Assert é verificar -
1. a condição válida,
2. a declaração válida,
3. verdadeira lógica;
de código fonte. Em vez de deixar todo o projeto dá um alarme de que algo não é apropriado em seu arquivo de origem.
No exemplo 1, desde variável 'str' não é nul. Portanto, não qualquer assert ou exceção se levantou.
#!/usr/bin/python
str = 'hello Pyhton!'
strNull = 'string is Null'
if __debug__:
if not str: raise AssertionError(strNull)
print str
if __debug__:
print 'FileName '.ljust(30,'.'),(__name__)
print 'FilePath '.ljust(30,'.'),(__file__)
------------------------------------------------------
Output:
hello Pyhton!
FileName ..................... hello
FilePath ..................... C:/Python\hello.py
No exemplo 2, var 'str' é nul. Portanto, estamos a poupar o usuário de ir à frente do programa defeituoso por assert comunicado.
#!/usr/bin/python
str = ''
strNull = 'NULL String'
if __debug__:
if not str: raise AssertionError(strNull)
print str
if __debug__:
print 'FileName '.ljust(30,'.'),(__name__)
print 'FilePath '.ljust(30,'.'),(__file__)
------------------------------------------------------
Output:
AssertionError: NULL String
No momento nós não queremos depuração e percebeu a questão afirmação no código fonte. Desativar a bandeira otimização
python -O assertStatement.py
nada terá de impressão
O IDE Em tais como PTVs, PyCharm, Asa declarações assert isinstance()
pode ser usado para permitir a conclusão de código para alguns objetos pouco claras.
Se você está lidando com código legado que se baseia em assert
para funcionar corretamente, embora não deve , em seguida, adicionando o seguinte código é uma solução rápida até encontrar tempo para refatorar:
try:
assert False
raise Exception('Python Assertions are not working. This tool relies on Python Assertions to do its job. Possible causes are running with the "-O" flag or running a precompiled (".pyo" or ".pyc") module.')
except AssertionError:
pass