如何指定无法访问的Python代码

xkrw2x1b  于 2023-03-21  发布在  Python
关注(0)|答案(8)|浏览(177)

在Python中指定不可访问代码的Python方式是什么:

gender = readFromDB(...) # either 'm' or 'f'
if gender == 'm':
    greeting = 'Mr.'
elif gender == 'f':
    greeting = 'Ms.'
else:
    # What should this line say?
r1wp621o

r1wp621o1#

raise ValueError('invalid gender %r' % gender)
4dbbbstv

4dbbbstv2#

你可以抛出一个异常:

raise ValueError("Unexpected gender; expected 'm' or 'f', got %s" % gender)

或者如果希望数据库只返回'm'或'f',则使用assert False:

assert False, "Unexpected gender; expected 'm' or 'f', got %s" % gender
o7jaxewo

o7jaxewo3#

这取决于你对性别是'm'还是'f'的确定程度。
如果你非常确定,使用if...else而不是if...elif...else,这样对每个人来说都更容易。
但是,如果有任何错误的数据,你可能应该提出一个异常,使测试和错误修复更容易。在这种情况下,你可以使用性别中立的问候语,但对于任何更大的,特殊的值只会使错误更难找到。

xggvc2p6

xggvc2p64#

我真的觉得有个地方可以放这个。

class SeriousDesignError(Exception):
    pass

所以你能做到

if number % 2 == 0:
    result = "Even"
elif number % 2 == 1:
    result = "Odd"
else:
    raise SeriousDesignError()

我认为这是最有意义的错误信息。这种事情只能通过设计错误(或糟糕的维护,这是一回事)产生

eiee3dmh

eiee3dmh5#

我有时会这样做:

if gender == 'm':
    greeting = 'Mr.'
else:
    assert gender == 'f'
    greeting = 'Ms.'

我认为这很好地告诉了代码的读者,(在这种情况下)只有两种可能性,以及它们是什么。尽管你可以提出一个比AssertionError更具描述性的错误。

kiayqfof

kiayqfof6#

这完全取决于您希望错误发出的信号,但在这种情况下,我会使用字典:

greetings = {'m': 'Mr.', 'f': 'Ms.'}
gender = readFromDB(...)  # either 'm' or 'f'
greeting = greetings[gender]

如果gender既不是m也不是f,这将引发一个包含意外值的KeyError:

greetings = {'m': 'Mr.', 'f': 'Ms.'}

>>> greetings['W']

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    greetings['W']
KeyError: 'W'

如果你想在消息中获得更多细节,你可以捕获并重新提出它:

try:
    greeting = greetings[gender]
except KeyError,e:
    raise ValueError('Unrecognized gender %s' % gender)
91zkwejq

91zkwejq7#

到目前为止,我通常使用John Fouhy的答案的变体-但这并不完全正确,正如Ethan指出的那样:

assert gender in ('m', 'f')
if gender == 'm':
    greeting = 'Mr.'
else:
    greeting = 'Ms.'

使用assert的主要问题是,如果任何人使用-O或-OO标志运行您的代码,则assert会被优化。正如Ethan在下面指出的那样,这意味着您现在根本没有数据检查。Assert是一种开发辅助工具,不应该用于生产逻辑。我将养成使用check()函数的习惯-这允许像assert一样使用干净的调用语法:

def check(condition, msg=None):
    if not condition:
        raise ValueError(msg or '')

check(gender in ('m', 'f'))
if gender == 'm':
    greeting = 'Mr.'
else:
    greeting = 'Ms.'

回到最初的问题,我认为在if/else逻辑之前使用assert()或check()更容易阅读,更安全,更明确:

  • 在开始处理数据之前,它首先测试数据质量--如果if/else链中有'=='以外的运算符,这一点可能很重要
  • 它将Assert测试从分支逻辑中分离出来,而不是将它们交织在一起--这使得阅读和重构更加容易
gblwokeq

gblwokeq8#

在python 3.11+版本中,你可以使用和typing.assert_never来Assert不可达性,这种方式可以通过静态类型检查器(如mypypyright)进行验证。(在python 3.11之前的版本中,你需要从typing_extensions模块导入assert_never。)
下面是一个使用typing.assert_nevertyping.Literal来AssertOP问题中的else块不可访问的示例:

from typing import Literal
from typing_extensions import assert_never

var: Literal["m", "f"] = "m"

if var == "m":
    ...
elif var == "f":
    ...
else:
    assert_never(var)  # unreachable

静态类型检查器将验证上面的else-块确实是不可访问的。如果在运行时到达assert_never块,它将抛出AssertionError

>>> from typing_extensions import assert_never
>>> assert_never(123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/homestar/tmp/.direnv/python-3.9.15/lib/python3.9/site-packages/typing_extensions.py", line 1993, in assert_never
    raise AssertionError("Expected code to be unreachable")
AssertionError: Expected code to be unreachable

下面是一个函数的例子,它的返回类型注解是typing.NoReturn

import time
from typing import NoReturn
from typing_extensions import Never, assert_never

def foo() -> NoReturn:
    while True:
        time.sleep(1)

bar: Never = foo()

assert_never(bar)  # unreachable

typing.Never类型在python〉=3.11的typing模块中可用,在python的早期版本的typing_extensions中可用。
下面是另一个例子,使用if/elif/else块来匹配变量的类型:

from typing import Union
from typing_extensions import assert_never

var: Union[int, str, float] = "123"

if isinstance(var, int):
    ...
elif isinstance(var, str):
    ...
elif isinstance(var, float):
    ...
else:
    assert_never(var)  # unreachable

有关更多示例,包括与枚举类型的值进行匹配,请参阅Unreachable Code and Exhaustiveness Checking上的Python类型文档。

相关问题