Возбуждение исключения в понимании списка: неверный синтаксис - PullRequest
1 голос
/ 06 марта 2019

Чтобы проверить, правильно ли вызывается метод, я хочу проверить каждый символ и лишить программиста возможности использовать метод неправильно. Поскольку метод будет вызываться на веб-сервере как return redirect(...), вместо возврата значения ошибки (например, False или None) я хочу вызвать исключение.

def redirect(uri):
    [raise ValueError('URI must be URL-encoded, ASCII only!') for c in uri if not (32 <= ord(c) <= 127)]

Это дает исключение «неверный синтаксис»:

File "server.py", line 115
    [raise ValueError('URI must be URL-encoded, ASCII only!') for c in uri if not (32 <= ord(c) <= 127)]
         ^
SyntaxError: invalid syntax

Я могу обойти эту проблему различными способами, но мне интересно: почему не допускается повышение внутри списка?

1 Ответ

1 голос
/ 10 марта 2019

Синтаксис

С синтаксической точки зрения ваш ответ в полной спецификации грамматики .

Терминал raise появляется только в правилах, полученных из stmt (оператор):

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (... | flow_stmt | ...)
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
raise_stmt: 'raise' [test ['from' test]]

В то время как первая часть понимания списка представляет собой test (логическое или выражение) или star_expr (*expr):

atom: ... | '[' [testlist_comp] ']' | ...
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )

Суть в том, что утверждение не может быть получено из (test|star_expr) (левая часть понимания списка). Следовательно, ваше выражение синтаксически неверно.

Семантический

Как отметил @Neb в комментарии, понимание списка, пытающееся вернуть a raise, не имеет смысла.

Вы, вероятно, помните, что print был оператором в Python 2 и стал функцией в Python 3:

Python 2:

>>> [print(1) for _ in range(1)]
  File "<stdin>", line 1
    [print(1) for _ in range(1)]
         ^
SyntaxError: invalid syntax

Python 3:

>>> [print(1) for _ in range(1)]
1
[None]

Понимание списка теперь синтаксически правильно. Точно так же никакое синтаксическое правило не мешает вам написать это:

>>> def raiser(): raise ValueError('URI must be URL-encoded, ASCII only!')
... 
>>> def redirect(uri): [raiser() for c in uri if not (32 <= ord(c) <= 127)]
... 
>>> redirect("abc")
>>> redirect("éàç")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in redirect
  File "<stdin>", line 1, in <listcomp>
  File "<stdin>", line 1, in raiser
ValueError: URI must be URL-encoded, ASCII only!

Но семантика остается неясной: хотите ли вы выполнить действие (то есть использовать побочный эффект от функции) или создать список? Помните, что понимание списка - это заимствование для функциональных языков, особенно Хаскелла, я думаю. Следовательно, они здесь не для того, чтобы совершать действия.

Я цитирую комментарий @Mark Ransom на ответ на " Является ли Pythonic использовать списочные выражения только для побочных эффектов? " вопрос:

Я бы пошел еще дальше и констатировал, что побочные эффекты в понимании списка являются необычными, неожиданными и, следовательно, злыми, даже если вы используете полученный список, когда закончите. - Марк Рэнсом

Я использую это правило: избегайте любых побочных эффектов в списках , даже если вы используете результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...