Python: спробуйте заяву в один рядок


89

Чи є в python спосіб перетворити спробу / за винятком на один рядок?

щось на зразок...

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

Де bдекларована змінна, а cні ..., cто викине помилку і aстане b...

Відповіді:


60

У Python немає можливості стиснути a try/ exceptblock в один рядок.

Крім того, погано не знати, чи існує в 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]тому [], що false у логічному контексті. Оскільки існує багато значень, які можуть бути хибними, я не використовую orфокус. (Це та сама проблема, з якою стикаються люди, коли кажуть, if foo:коли мають на увазі if foo is not None:.)


Дякую. Проблема полягає в тому, що його власне запит django model.objects.get я намагаюся перевірити. .get повертає помилку, якщо дані не знайдені ... не повертає None (що мене дратує)
Brant

@Brant, Гаразд, ця ситуація дещо інша, ніж перевірка, чи встановлена ​​змінна (у Python не оголошено змінних). Типовий стиль у Python - віддавати перевагу виняткам, а не поверненню помилок як значень, які насправді люблять багато з нас. Доводиться щоразу перевіряти код повернення операції і важко відстежувати помилки, якщо цього не сталося, - це те, що я точно не пропускаю щодо C при написанні Python. У будь-якому випадку, хоча це вже обговорювалось, для a 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]

Здебільшого мене взагалі не турбує обмеження, не обмежуючи спроби вибору одного рядка, але коли я просто експериментую, і я хочу, щоб readline відкликав цілий шматок коду відразу в інтерактивному інтерпретаторі, так що я можу це якось відрегулювати, цей маленький фокус стане в нагоді.

Для фактичної мети, яку ви намагаєтесь досягти, ви можете спробувати locals().get('c', b); в ідеалі було б краще використовувати справжній словник замість локального контексту, або просто присвоїти c значення None перед тим, як запустити будь-що, що може або не може встановити його.


26
Гей, це відповідає на питання! :)
Стів Беннетт

4
Люблю цю відповідь, супер безладний, але один рядок, саме такий, як мені подобається.
Патрік Кук

Це відповідь !! буде problem[0]повернути те , що функція повертає?
SIslam

4
Exec є запахом коду, і його слід уникати, якщо ніщо інше не працює. Якщо один рядовий код настільки важливий, тоді це спрацює, але потрібно запитати себе, чому один рядок так важливий.
Gewthen

4
явно не для виробничого використання, а саме того, що потрібно для незручного сеансу налагодження.
ThorSummoner


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
Це чудово! Оскільки не існує явної спроби / крім того, не могли б ви коротко пояснити, як менеджер контексту має справу з помилками?
Патрік

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

Для чого потрібна кома в "ожидаемо_вихід = (Виняток,)"? Ви можете пояснити, будь ласка?
ibilgen


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і т.д.


4

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

>>> b = 'some variable'
>>> a = vars().get('c', b)

3
Це не працює точно так само, як перевірка, чи встановлена ​​змінна (хоча вона працює, якщо вас цікавить певний обсяг.) Також, ewwwwwwww .....
Mike Graham

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. Можливо, це вам допоможе.


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

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 небезпечно використовувати з даними, над якими ви не маєте контролю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.