首页 > 解决方案 > 在列表理解中引发异常:语法无效

问题描述

为了检查一个方法是否被正确调用,我想检查每个字符,使程序员不可能错误地使用该方法。因为该方法将在 Web 服务器中调用return redirect(...),而不是返回错误值(如Falseor 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

我可以通过各种方式解决这个问题,但我想知道:为什么不允许在列表理解中提出?

标签: python-3.xsyntax-errorlist-comprehension

解决方案


句法

从语法的角度来看,您的答案在完整的语法规范中。

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 中变成了一个函数:

蟒蛇2:

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

蟒蛇 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!

但是语义仍然不清楚:您是要执行一个动作(即使用函数的副作用)还是构建一个列表?请记住,列表推导是对函数式语言的借用,我认为尤其是 Haskell。因此,他们不是在这里执行操作。

我引用@Mark Ransom 评论来回答使用列表推导来解决副作用是 Pythonic 吗? ”问题:

我会更进一步,并指出列表理解中的副作用是不寻常的、出乎意料的,因此是邪恶的,即使您在完成后使用结果列表也是如此。– 马克赎金

我使用这个经验法则:避免列表推导中的任何副作用,即使您使用结果。


推荐阅读