【问题标题】:Raising an exception in a list comprehension: invalid syntax在列表理解中引发异常:语法无效
【发布时间】:2019-03-06 10:03:00
【问题描述】:

为了检查一个方法是否被正确调用,我想检查每个字符,使程序员不可能错误地使用该方法。因为该方法将在 Web 服务器中以return redirect(...) 的形式调用,而不是返回错误值(如FalseNone),所以我想引发异常。

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

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

【问题讨论】:

  • 拥有一个带有引发异常的列表作为元素意味着什么?
  • @Neb raise 关键字不会引发异常吗?它永远不会出现在列表中,因为它在找到except 语句之前会脱离该函数。要将例外作为列表中的元素,我会使用[ValueError(...) for c in uri if ...])。
  • 是的,确实如此。但是,我想知道,你为什么要把它放在一个列表中?不列清单就不能提出来吗?
  • @Neb 当然,for c in uri:\n\tif ...:\n\t\traise ValueError(...) 可以,但我想知道 为什么 raise 在列表理解中是不允许的。
  • 要知道为什么,看看这个答案stackoverflow.com/questions/1528237/…

标签: python-3.x syntax-error list-comprehension


【解决方案1】:

语法

从语法的角度来看,您的答案在full grammar spec 中。

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 在评论中指出的那样,试图返回 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!

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

我引用@Mark Ransom 对answer 对“Is it Pythonic to use list comprehensions for just side effects?”问题的评论:

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

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

【讨论】:

    猜你喜欢
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    相关资源
    最近更新 更多