Estou escrevendo meu próprio contêiner, que precisa dar acesso a um dicionário dentro por chamadas de atributo. O uso típico do contêiner seria assim:
dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo
Sei que pode ser estúpido escrever algo assim, mas essa é a funcionalidade que preciso fornecer. Eu estava pensando em implementar isso da seguinte maneira:
def __getattribute__(self, item):
try:
return object.__getattribute__(item)
except AttributeError:
try:
return self.dict[item]
except KeyError:
print "The object doesn't have such attribute"
Não tenho certeza se os blocos try / except aninhados são uma boa prática; portanto, outra maneira seria usar hasattr()
e has_key()
:
def __getattribute__(self, item):
if hasattr(self, item):
return object.__getattribute__(item)
else:
if self.dict.has_key(item):
return self.dict[item]
else:
raise AttributeError("some customised error")
Ou, para usar um deles e tentar um bloco de captura como este:
def __getattribute__(self, item):
if hasattr(self, item):
return object.__getattribute__(item)
else:
try:
return self.dict[item]
except KeyError:
raise AttributeError("some customised error")
Qual opção é mais pitônica e elegante?
if 'foo' in dict_container:
. Amém.Respostas:
Seu primeiro exemplo está perfeitamente bem. Até os documentos oficiais do Python recomendam esse estilo conhecido como EAFP .
Pessoalmente, prefiro evitar o aninhamento quando não for necessário:
PS.
has_key()
foi descontinuado por muito tempo no Python 2. Use emitem in self.dict
vez disso.fonte
return object.__getattribute__(item)
está incorreto e produzirá umTypeError
porque o número errado de argumentos está sendo passado. Em vez disso, deveria serreturn object.__getattribute__(self, item)
.from None
significa na última linha?Enquanto em Java é realmente uma má prática usar Exceções para controle de fluxo (principalmente porque as exceções forçam a jvm a reunir recursos ( mais aqui )), em Python você tem 2 princípios importantes: Duck Typing e EAFP . Isso basicamente significa que você é incentivado a tentar usar um objeto da maneira que você acha que ele funcionaria, e a lidar quando as coisas não são assim.
Em resumo, o único problema seria seu código ficar muito recuado. Se você quiser, tente simplificar alguns dos aninhamentos, como o lqc sugerido
fonte
Para o seu exemplo específico, você realmente não precisa aninhá-los. Se a expressão no
try
bloco for bem-sucedida, a função retornará, portanto, qualquer código após todo o bloco try / except será executado apenas se a primeira tentativa falhar. Então você pode fazer:Aninhando-os não é ruim, mas sinto que deixá-lo plano torna a estrutura mais clara: você está tentando seqüencialmente uma série de coisas e retornando a primeira que funciona.
Aliás, você pode querer pensar se realmente deseja usar em
__getattribute__
vez de__getattr__
aqui. O uso__getattr__
simplificará as coisas porque você saberá que o processo normal de pesquisa de atributos já falhou.fonte
Apenas tenha cuidado - neste caso, primeiro
finally
é tocado, mas também ignorado.fonte
finally
blocos são executadosa(0)
, mas apenas o paifinally-return
é retornado.Na minha opinião, essa seria a maneira mais pitônica de lidar com isso, embora e porque torne sua pergunta discutível. Observe que isso define em
__getattr__()
vez de__getattribute__()
fazê-lo, porque isso significa que ele só precisa lidar com os atributos "especiais" mantidos no dicionário interno.fonte
except
bloco pode gerar resultados confusos no Python 3. Isso ocorre porque (de acordo com o PEP 3134) o Python 3 rastreia a primeira exceção (theKeyError
) como o "contexto" da segunda exceção (theAttributeError
) e se atinge o valor nível superior, ele imprimirá um rastreamento que inclui as duas exceções. Isso pode ser útil quando uma segunda exceção não era esperada, mas se você deliberadamente criar a segunda exceção, é indesejável. Para Python 3.3, o PEP 415 adicionou a capacidade de suprimir o contexto usandoraise AttributeError("whatever") from None
.KeyError
emAttributeError
e mostrar que foi o que aconteceu em um retorno seria útil e apropriado.__getattr__
uma exceção, o erro provavelmente será um erro de digitação no acesso ao atributo, não um erro de implementação no código da classe atual. Mostrar a exceção anterior como contexto pode atrapalhar isso. E mesmo quando você suprime o contextoraise Whatever from None
, ainda é possível obter a exceção anterior, se necessário viaex.__context__
.__getattribute__()
.No Python, é mais fácil pedir perdão do que permissão. Não se preocupe com o tratamento de exceções aninhadas.
(Além disso,
has*
quase sempre usa exceções nos bastidores).fonte
De acordo com a documentação , é melhor lidar com várias exceções por meio de tuplas ou assim:
fonte
Um exemplo bom e simples para try / except aninhado pode ser o seguinte:
Agora tente várias combinações e você obterá o resultado correto:
[é claro que estamos entorpecidos e não precisamos criar essa função]
fonte
Uma coisa que eu gosto de evitar é criar uma nova exceção ao lidar com uma antiga. Isso torna as mensagens de erro confusas para serem lidas.
Por exemplo, no meu código, escrevi originalmente
E eu recebi esta mensagem.
O que eu queria era isso:
Não afeta como as exceções são tratadas. Em qualquer bloco de código, um KeyError teria sido capturado. Isso é apenas uma questão de obter pontos de estilo.
fonte
from None
para obter ainda mais pontos de estilo. :)Se tentar, exceto, finalmente, estiver aninhado dentro do bloco finalmente, o resultado de "filho" finalmente será preservado. Ainda não encontrei a expainação oficial, mas o seguinte trecho de código mostra esse comportamento no Python 3.6.
fonte
Não acho que seja uma questão de ser pitônico ou elegante. É uma questão de evitar exceções, tanto quanto possível. Exceções destinam-se a manipular erros que podem ocorrer em código ou eventos sobre os quais você não tem controle. Nesse caso, você tem controle total ao verificar se um item é um atributo ou um dicionário, portanto, evite exceções aninhadas e continue com sua segunda tentativa.
fonte