Python: инструкция try в одной строке

89

Есть ли способ в python превратить try / except в одну строку?

что-то типа...

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

Где bобъявленная переменная, а cне ... поэтому cвыдаст ошибку и aстанет b...

Казарка
источник

Ответы:

60

В Python нет способа сжать блок try/ exceptв одну строку.

Кроме того, плохо не знать, существует ли переменная в Python, как и в некоторых других динамических языках. Более безопасный способ (и преобладающий стиль) - установить все переменные на что-то. Если они не могут быть настроены, установите их в Noneпервую очередь ( 0или ''или что-то еще, если это более применимо).


Если вы делаете правопреемником все имена , которые вы заинтересованы в первых, у вас есть варианты.

  • Лучшим вариантом является оператор if.

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • Однострочный вариант - это условное выражение.

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • Некоторые люди для этого злоупотребляют коротким замыканием or. Это подвержено ошибкам, поэтому я никогда им не пользуюсь.

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

    Рассмотрим следующий случай:

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

    В этом случае, aвероятно, должно быть [], но это [1, 2]потому, что []в логическом контексте ложно. Поскольку существует множество значений, которые могут быть ложными, я не использую этот orтрюк. (Это та же проблема, с которой сталкиваются люди, когда говорят, if foo:когда имеют в виду if foo is not None:.)

Майк Грэм
источник
Спасибо. Проблема в том, что на самом деле это запрос django model.objects.get, который я пытаюсь проверить. .get возвращает ошибку, если данные не найдены ... он не возвращает None (что меня раздражает)
Брант,
@Brant, Хорошо, эта ситуация немного отличается от проверки, установлена ​​ли переменная (в Python переменные не объявляются). Типичный стиль Python - предпочитать создание исключений возврату ошибок в виде значений, что многие из нас действительно любят. Необходимость проверять код возврата операции каждый раз и трудности с отслеживанием ошибок, если я этого не сделаю, - это то, чего я определенно не скучаю в C при написании Python. В любом случае, хотя это уже обсуждалось, для try/ exceptblock не существует однострочного синтаксиса . К счастью, линии дешевы, поэтому решение из 4 строк должно вам подойти. ;-)
Майк Грэм
Это часть большого набора кортежей внутри дикта ... Я просто пытался немного сократить
Брант,
2
Не используйте, getесли не хотите исключений. filterВместо этого используйте .
jcdyer
@MikeGraham Хороший ответ - намек (ссылка?), Почему короткое замыкание подвержено ошибкам, было бы неплохо.
Кратенко
83

Это ужасно хакерски, но я использовал его в командной строке, когда хотел написать последовательность действий для отладки:

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

По большей части меня совсем не беспокоит ограничение no-single-line-try-except, но когда я просто экспериментирую и хочу, чтобы readline вызывала сразу весь фрагмент кода в интерактивном интерпретаторе, поэтому что я могу как-то это отрегулировать, этот маленький трюк пригодится.

Для реальной цели, которую вы пытаетесь достичь, вы можете попробовать locals().get('c', b); в идеале было бы лучше использовать реальный словарь вместо локального контекста или просто присвоить c параметру None перед запуском того, что может или не может быть установлено.

Вальтер Мундт
источник
26
Эй, это ответ на вопрос! :)
Стив Беннетт
4
Мне нравится этот ответ, супер беспорядочный, но одна строчка, такая, как мне нравится.
Патрик Кук
Это ответ !! будет problem[0]вернуть то , что функция возвращает?
SIslam
4
Exec - это запах кода, и его следует избегать, если ничего не работает. Если код одной строки так важен, тогда это сработает, но вы должны спросить себя, почему одна строка так важна.
Gewthen
4
явно не для производственного использования, а именно то, что нужно для неудобного сеанса отладки.
ThorSummoner 07
46

В python3 вы можете использовать contextlib.suppress :

from contextlib import suppress

d = {}
with suppress(KeyError): d['foo']
dset0x
источник
8
это должен быть стандартный ответ
Sphynx-HenryAY
13

Другой способ - определить диспетчер контекста:

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

Затем используйте withоператор, чтобы игнорировать ошибки в одной строке:

>>> 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

В случае ошибки выполнения исключение не возникает. Это как try:без except:.

битагорас
источник
1
Это круто! Поскольку здесь нет явного try / except, не могли бы вы вкратце объяснить, как диспетчер контекста обрабатывает ошибки?
Патрик
8

Версия ответа poke53280 с ограниченными ожидаемыми исключениями.

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

и его можно было бы использовать как

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
Павел Правдюков
источник
Для чего нужна запятая в "expected_exc = (Exception,)"? Вы можете объяснить, пожалуйста?
ibilgen
7
parse_float = lambda x, y=exec("def f(s):\n try:\n  return float(s)\n except:  return None"): f(x)

Всегда есть решение.

Миклош Хорват
источник
5

Проблема в том, что на самом деле это запрос django model.objects.get, который я пытаюсь проверить. .get возвращает ошибку, если данные не найдены ... он не возвращает None (что меня раздражает)

Используйте что-то вроде этого:

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

Где try_or - это определенная вами служебная функция:

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

При желании вы можете ограничить допустимые типы исключений до NameError, AttributeErrorи т. Д.

тыкать53280
источник
4

Вы можете сделать это путем доступа к пространству имен Dict с использованием vars(), locals()или globals(), в зависимости от того является наиболее подходящей для вашей ситуации.

>>> b = 'some variable'
>>> a = vars().get('c', b)
jcdyer
источник
3
Это не работает точно так же, как проверка, установлена ​​ли переменная (хотя это работает, если вас интересует конкретная область видимости). Кроме того, ewwwwwwww ...
Майк Грэм,
4

Как насчет использования двух строк. Это нормально ?

>>> try: a = 3; b= 0; c = a / b
... except : print('not possible'); print('zero division error')
...
not possible
zero division error
Сурендра Бен
источник
2

Вы упомянули, что используете django. Если это имеет смысл для того, что вы делаете, вы можете использовать:

my_instance, created = MyModel.objects.get_or_create()

createdбудет True или False. Может это тебе поможет.

Blokeley
источник
1

если вам действительно нужно управлять исключениями:
(изменено из ответа 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
>>> 

обратите внимание, что если исключение не поддерживается, оно вызовет ожидаемый результат:

>>> 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'
>>> 

также, если Exceptionуказан, он будет соответствовать чему-либо ниже.
( BaseExceptionвыше, поэтому не будет совпадать)

>>> try_or( context, {Exception: lambda: print('exception')} )
exception
Tcll
источник
1

Работает на Python3, вдохновленный Вальтером Мундтом

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

Для нескольких строк в одну строку

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

Ps: Exec небезопасно использовать с данными, которые вы не контролируете.

Пуннеруд
источник