Python: declaração try em uma única linha

97

Existe uma maneira em python de transformar um try / except em uma única linha?

algo como...

b = 'some variable'
a = c | b #try statement goes here

Onde bé uma variável declarada e cnão é ... então clançaria um erro e ase tornaria b...

Brant
fonte

Respostas:

63

Não há como compactar um try/ exceptblock em uma única linha no Python.

Além disso, é uma coisa ruim não saber se uma variável existe no Python, como você faria em algumas outras linguagens dinâmicas. A maneira mais segura (e o estilo predominante) é definir todas as variáveis ​​para algo. Se eles não forem definidos, defina-os como Noneprimeiro (ou 0ou ''ou algo se for mais aplicável).


Se você fazer atribuir todos os nomes que você está interessado em primeiro lugar, você tem opções.

  • A melhor opção é uma instrução if.

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • A opção de uma linha é uma expressão condicional.

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • Algumas pessoas abusam do comportamento de curto-circuito de orpara fazer isso. Isso está sujeito a erros, então nunca o uso.

    c = None
    b = [1, 2]
    a = c or b
    

    Considere o seguinte caso:

    c = []
    b = [1, 2]
    a = c or b
    

    Nesse caso, aprovavelmente deveria ser [], mas é [1, 2]porque []é falso em um contexto booleano. Como há muitos valores que podem ser falsos, não uso esse ortruque. (Este é o mesmo problema que as pessoas enfrentam quando dizem if foo:quando querem dizer if foo is not None:).

Mike Graham
fonte
Obrigado. O problema é que na verdade é uma consulta django model.objects.get que estou tentando testar. o .get retorna um erro se nenhum dado for encontrado ... ele não retorna Nenhum (o que me irrita)
Brant
@Brant, Ok, essa situação é um pouco diferente de verificar se uma variável está definida (nenhuma variável é declarada em Python). O estilo típico em Python é preferir levantar exceções a retornar erros como valores, o que muitos de nós realmente amam. Ter que verificar o código de retorno de uma operação todas as vezes e ter dificuldade em rastrear erros se não o fizer é algo que definitivamente não sinto falta em C ao escrever Python. Em qualquer caso, embora tenha sido discutido, não há sintaxe de uma linha para um bloco try/ except. Felizmente, as linhas são baratas, então a solução de 4 linhas deve funcionar para você. ;-)
Mike Graham
É parte de um grande conjunto de tuplas dentro de um dicionário. Eu estava apenas tentando encurtar as coisas um pouco
Brant
2
Não use getse você não quiser uma exceção. Use em seu filterlugar.
jcdyer
@MikeGraham Boa resposta - uma dica (link?) De por que o curto-circuito está sujeito a erros seria bom.
Kratenko de
85

Isso é terrivelmente hackeado, mas usei-o no prompt quando queria escrever uma sequência de ações para depuração:

exec "try: some_problematic_thing()\nexcept: problem=sys.exc_info()"
print "The problem is %s" % problem[1]

Na maior parte do tempo, não estou nem um pouco incomodado com a restrição de não tentar uma única linha, mas quando estou apenas experimentando e quero que o readline lembre um pedaço inteiro de código de uma vez no interpretador interativo, que eu possa ajustá-lo de alguma forma, este pequeno truque é útil.

Para o propósito real que está tentando realizar, você pode tentar locals().get('c', b); idealmente, seria melhor usar um dicionário real em vez do contexto local, ou apenas atribuir c a None antes de executar qualquer opção que possa ou não definir.

Walter Mundt
fonte
26
Ei, isso responde à pergunta! :)
Steve Bennett
4
Amei essa resposta, super confusa, mas uma linha, do jeito que eu gosto.
Patrick Cook
Esta é a resposta !! irá problem[0]devolver o que essa função retorna?
SIslam
5
Exec é um odor de código e deve ser evitado, a menos que nada mais funcione. Se um código de linha for tão importante, isso funcionará, mas você precisa se perguntar por que uma linha é tão importante.
Gewthen
4
claramente não para uso em produção, mas exatamente o que é necessário para uma sessão de depuração estranha.
ThorSummoner de
54

Em python3, você pode usar contextlib.suppress :

from contextlib import suppress

d = {}
with suppress(KeyError): d['foo']
dset0x
fonte
8
esta deve ser a resposta padrão
Sphynx-HenryAY
13

Outra maneira é definir um gerenciador de contexto:

class trialContextManager:
    def __enter__(self): pass
    def __exit__(self, *args): return True
trial = trialContextManager()

Em seguida, use a withinstrução para ignorar os erros em uma única linha:

>>> with trial: a = 5      # will be executed normally
>>> with trial: a = 1 / 0  # will be not executed and no exception is raised
>>> print a
5

Nenhuma exceção será levantada no caso de um erro de tempo de execução. É como um try:sem o except:.

bitágoras
fonte
1
Isso é ótimo! Já que não há try / except explícito, você poderia explicar brevemente como o gerenciador de contexto lida com os erros?
Patrick
10

Versão da resposta poke53280 com exceções esperadas limitadas.

def try_or(func, default=None, expected_exc=(Exception,)):
    try:
        return func()
    except expected_exc:
        return default

e poderia ser usado como

In [2]: try_or(lambda: 1/2, default=float('nan'))
Out[2]: 0.5

In [3]: try_or(lambda: 1/0, default=float('nan'), expected_exc=(ArithmeticError,))
Out[3]: nan

In [4]: try_or(lambda: "1"/0, default=float('nan'), expected_exc=(ArithmeticError,))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
[your traceback here]
TypeError: unsupported operand type(s) for /: 'str' and 'int'

In [5]: try_or(lambda: "1"/0, default=float('nan'), expected_exc=(ArithmeticError, TypeError))
Out[5]: nan
Pavlo Pravdiukov
fonte
Para que serve a vírgula em "expected_exc = (Exception,)"? Você pode explicar, por favor?
ibilgen
1
@ibilgen A vírgula converte a expressão em uma tupla . Escrever (Exception)é igual a omitir os colchetes. (Exception, )diz ao interpretador que esta é uma tupla (algo como uma lista) com uma entrada. Neste exemplo, isso é usado, portanto, expected_excpode haver mais de uma exceção.
miile7
7
parse_float = lambda x, y=exec("def f(s):\n try:\n  return float(s)\n except:  return None"): f(x)

Sempre existe uma solução.

Miklos Horvath
fonte
5

O problema é que, na verdade, é uma consulta django model.objects.get que estou tentando testar. o .get retorna um erro se nenhum dado for encontrado ... ele não retorna Nenhum (o que me irrita)

Use algo assim:

print("result:", try_or(lambda: model.objects.get(), '<n/a>'))

Onde try_or é uma função de utilidade definida por você:

def try_or(fn, default):
    try:
        return fn()
    except:
        return default

Opcionalmente, você pode restringir os tipos de exceção aceitou NameError, AttributeErroretc.

poke53280
fonte
5

Que tal usar duas linhas. esta ok?

>>> try: a = 3; b= 0; c = a / b
... except : print('not possible'); print('zero division error')
...
not possible
zero division error
Surendra Ben
fonte
4

Você pode fazê-lo acessando o dict namespace usando vars(), locals()ou globals(), consoante o que for mais adequado para sua situação.

>>> b = 'some variable'
>>> a = vars().get('c', b)
Jcdyer
fonte
3
Isso não funciona exatamente da mesma forma que verificar se uma variável está definida (embora funcione se você estiver interessado em um escopo específico). Além disso, ewwwwwwww .....
Mike Graham
2

Você mencionou que está usando o Django. Se fizer sentido para o que você está fazendo, você pode usar:

my_instance, created = MyModel.objects.get_or_create()

createdserá verdadeiro ou falso. Talvez isso ajude você.

Blokeley
fonte
2

Funciona em Python3, inspirado em Walter Mundt

exec("try:some_problematic_thing()\nexcept:pass")

Para linhas múltiplas em uma linha

exec("try:\n\tprint('FirstLineOk')\n\tsome_problematic_thing()\n\tprint('ThirdLineNotTriggerd')\nexcept:pass")

Ps: Exec não é seguro para usar em dados que você não tem controle.

Punnerud
fonte
1

se você realmente precisa gerenciar exceções:
(modificado da resposta de poke53280)

>>> def try_or(fn, exceptions: dict = {}):
    try:
        return fn()
    except Exception as ei:
        for e in ei.__class__.__mro__[:-1]:
            if e in exceptions: return exceptions[e]()
        else:
            raise


>>> def context():
    return 1 + None

>>> try_or( context, {TypeError: lambda: print('TypeError exception')} )
TypeError exception
>>> 

observe que se a exceção não for suportada, ela aumentará conforme o esperado:

>>> try_or( context, {ValueError: lambda: print('ValueError exception')} )
Traceback (most recent call last):
  File "<pyshell#57>", line 1, in <module>
    try_or( context, {ValueError: lambda: print('ValueError exception')} )
  File "<pyshell#38>", line 3, in try_or
    return fn()
  File "<pyshell#56>", line 2, in context
    return 1 + None
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
>>> 

também se Exceptionfor fornecido, ele corresponderá a qualquer coisa abaixo.
( BaseExceptioné mais alto, então não vai corresponder)

>>> try_or( context, {Exception: lambda: print('exception')} )
exception
Tcll
fonte
0

Aqui está uma versão mais simples da resposta fornecida por @surendra_ben

a = "apple"try: a.something_that_definitely_doesnt_exist
except: print("nope")

...

nope
HashRocketSyntax
fonte
0

Use a withsintaxe em uma linha:

class OK(): __init__ = lambda self, *isok: setattr(self, 'isok', isok); __enter__ = lambda self: None; __exit__ = lambda self, exc_type, exc_value, traceback: (True if not self.isok or issubclass(exc_type, self.isok) else None) if exc_type else None

Ignore quaisquer erros:

with OK(): 1/0

Ignore os erros especificados:

with OK(ZeroDivisionError, NameError): 1/0
Sakuya
fonte