if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Qual deve ser preferido e por quê?
if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Qual deve ser preferido e por quê?
Respostas:
hasattr
executa interna e rapidamente a mesma tarefa que otry/except
bloco: é uma ferramenta muito específica, otimizada, de uma tarefa e, portanto, deve ser preferida, quando aplicável, à alternativa de uso muito geral.fonte
hasattr
irá capturar todas as exceções em Python 2.x. Veja minha resposta para um exemplo e uma solução alternativa trivial.try
pode transmitir que a operação deve funcionar. Emboratry
a intenção de nem sempre seja essa, é comum, por isso pode ser considerada mais legível.Quaisquer bancos que ilustram a diferença de desempenho?
hora em que é seu amigo
$ python -mtimeit -s 'class C(object): a = 4 c = C()' 'hasattr(c, "nonexistent")' 1000000 loops, best of 3: 1.87 usec per loop $ python -mtimeit -s 'class C(object): a = 4 c = C()' 'hasattr(c, "a")' 1000000 loops, best of 3: 0.446 usec per loop $ python -mtimeit -s 'class C(object): a = 4 c = C()' 'try: c.a except: pass' 1000000 loops, best of 3: 0.247 usec per loop $ python -mtimeit -s 'class C(object): a = 4 c = C()' 'try: c.nonexistent except: pass' 100000 loops, best of 3: 3.13 usec per loop $ |positive|negative hasattr| 0.446 | 1.87 try | 0.247 | 3.13
fonte
try
é quase duas vezes mais rápido quehasattr()
. Caso contrário,try
é cerca de 1,5x mais lento do quehasattr()
(e ambos são substancialmente mais lentos do que se o atributo existir). Provavelmente, isso ocorre porque, no caminho feliz,try
quase nada faz (o Python já está pagando pela sobrecarga de exceções, independentemente de você usá-las), mashasattr()
requer uma pesquisa de nome e chamada de função. No caminho infeliz, ambos têm que fazer algum tratamento de exceção e umgoto
, mas ohasattr()
fazem em C em vez de bytecode Python.Existe uma terceira alternativa, muitas vezes melhor:
attr = getattr(obj, 'attribute', None) if attr is not None: print attr
Vantagens:
getattr
não tem o mau comportamento de engolir exceções apontado por Martin Geiser - nos Pythons antigos,hasattr
engole até mesmo aKeyboardInterrupt
.A razão normal pela qual você está verificando se o objeto tem um atributo é para que você possa usar o atributo, e isso naturalmente leva a isso.
O atributo é lido atomicamente e está protegido de outras threads que alteram o objeto. (Porém, se esta for uma grande preocupação, você pode querer considerar o bloqueio do objeto antes de acessá-lo.)
É mais curto que
try/finally
e geralmente menor quehasattr
.Um
except AttributeError
bloqueio amplo pode pegar algo diferente doAttributeErrors
que você está esperando, o que pode levar a um comportamento confuso.Acessar um atributo é mais lento do que acessar uma variável local (especialmente se não for um atributo de instância simples). (Embora, para ser honesto, a microotimização em Python costuma ser uma missão tola.)
Uma coisa a ter cuidado é se você se preocupa com o caso em que
obj.attribute
está definido como Nenhum, você precisará usar um valor de sentinela diferente.fonte
Quase sempre uso
hasattr
: é a escolha correta para a maioria dos casos.O caso problemático é quando uma classe sobrescreve
__getattr__
:hasattr
irá capturar todas as exceções em vez de capturar exatamenteAttributeError
como você espera. Em outras palavras, o código abaixo será impressob: False
, embora seja mais apropriado ver umaValueError
exceção:class X(object): def __getattr__(self, attr): if attr == 'a': return 123 if attr == 'b': raise ValueError('important error from your database') raise AttributeError x = X() print 'a:', hasattr(x, 'a') print 'b:', hasattr(x, 'b') print 'c:', hasattr(x, 'c')
O erro importante, portanto, desapareceu. Isso foi corrigido no Python 3.2 (problema 9666 ), onde
hasattr
agora apenas detectaAttributeError
.Uma solução fácil é escrever uma função de utilitário como esta:
_notset = object() def safehasattr(thing, attr): return getattr(thing, attr, _notset) is not _notset
Vamos
getattr
lidar com a situação e, em seguida, pode gerar a exceção apropriada.fonte
hasattr
pelo menos não pegueKeyboardInterrupt
etc.safehasattr
apenas usargetattr
para copiar o valor em uma variável local se for usá-la, o que quase sempre é feito.hasattr
tinha sido melhorado assim.hasattr
, e fui verificar. Tivemos alguns erros bzr engraçados onde o hasattr engoliu ^ C.Eu diria que depende se sua função pode aceitar objetos sem o atributo por design , por exemplo, se você tiver dois chamadores para a função, um fornecendo um objeto com o atributo e o outro fornecendo um objeto sem ele.
Se o único caso em que você obterá um objeto sem o atributo for devido a algum erro, eu recomendo usar o mecanismo de exceções, mesmo que seja mais lento, porque acredito que é um design mais limpo.
Resumindo: acho que é um problema de design e legibilidade, e não de eficiência.
fonte
Se não ter o atributo não é uma condição de erro, a variante de tratamento de exceções tem um problema: ela capturaria também AttributeErrors que podem vir internamente ao acessar obj.attribute (por exemplo, porque o atributo é uma propriedade, então acessá-lo chama algum código).
fonte
Este assunto foi abordado na palestra EuroPython 2016 Escrevendo Python mais rápido por Sebastian Witowski. Aqui está uma reprodução de seu slide com o resumo do desempenho. Ele também usa a terminologia look antes de você pular nesta discussão, que vale a pena mencionar aqui para marcar essa palavra-chave.
3 PERMISSÕES OU PERDÃO?
# CASE 1 -- Attribute Exists class Foo(object): hello = 'world' foo = Foo() if hasatter(foo, 'hello'): foo.hello ## 149ns ## try: foo.hello except AttributeError: pass ## 43.1 ns ## ## 3.5 times faster # CASE 2 -- Attribute Absent class Bar(object): pass bar = Bar() if hasattr(bar, 'hello'): bar.hello ## 428 ns ## try: bar.hello except AttributeError : pass ## 536 ns ## ## 25% slower
fonte
Se for apenas um atributo que você está testando, eu diria para usar
hasattr
. No entanto, se você estiver fazendo vários acessos a atributos que podem ou não existir, o uso de umtry
bloco pode economizar alguma digitação.fonte
Eu sugeriria a opção 2. A opção 1 tem uma condição de corrida se algum outro thread estiver adicionando ou removendo o atributo.
Além disso, python tem um idioma , que EAFP ('mais fácil pedir perdão do que permissão') é melhor do que LBYL ('olhe antes de pular').
fonte
De um ponto de vista prático, na maioria das linguagens, usar uma condicional sempre será consideravelmente mais rápido do que lidar com uma exceção.
Se você deseja lidar com o caso de um atributo não existir em algum lugar fora da função atual, a exceção é o melhor caminho a seguir. Um indicador de que você pode querer usar uma exceção em vez de uma condicional é que a condicional meramente define um sinalizador e aborta a operação atual, e algo em outro lugar verifica esse sinalizador e executa uma ação com base nele.
Dito isso, como Rax Olgud aponta, a comunicação com os outros é um atributo importante do código, e o que você quer dizer ao dizer "esta é uma situação excepcional" em vez de "isso é algo que espero que aconteça" pode ser mais importante .
fonte
O primeiro.
Quanto mais curto, melhor. As exceções devem ser excepcionais.
fonte
for
instrução e tambémhasattr
usa uma. No entanto, "quanto mais curto é melhor" (e "mais simples é melhor"!) SE APLICA, então o hasattr mais simples, mais curto e mais específico é realmente preferível.Pelo menos quando se trata apenas do que está acontecendo no programa, deixando de fora a parte humana da legibilidade, etc. (que é na verdade na maioria das vezes mais importante do que o desempenho (pelo menos neste caso - com aquele intervalo de desempenho), como Roee Adler e outros apontaram).
No entanto, olhando dessa perspectiva, torna-se uma questão de escolher entre
try: getattr(obj, attr) except: ...
e
try: obj.attr except: ...
já que
hasattr
usa apenas o primeiro caso para determinar o resultado. Alimento para o pensamento ;-)fonte