6

我正在尝试对我的 python 代码执行良好的输入有效性检查,但我也希望它简洁。也就是说,我不想使用的解决方案是这个:

def some_func(int_arg, str_arg, other_arg):
    try:
        int_arg = int(int_arg)
    except TypeError, ValueError
        logging.error("int_arg must respond to int()")
        raise TypeError
    try:
        if str_arg is not None:
            str_arg = str(str_arg) 
    except TypeError
        logging.error("Okay, I'm pretty sure this isn't possible, bad example")
        raise TypeError
    if other_arg not in (VALUE1, VALUE2, VALUE3):
        logging.error("other arg must be VALUE1, VALUE2, or VALUE3")
        raise TypeError

这只是太多的代码和太多的空间来检查 3 个参数。

我目前的做法是这样的:

def some_func(int_arg, str_arg, other_arg):
    try:
        int_arg = int(int_arg)  #int_arg must be an integer
        str_arg is None or str_arg = str(str_arg)  #str_arg is optional, but must be a string if provided
        assert other_arg in (VALUE1, VALUE2, VALUE3)
    catch TypeError, ValueError, AssertionError:
        logging.error("Bad arguments given to some_func")
        throw TypeError

我失去了我的日志消息的特殊性,但在我看来,这更简洁,老实说更具可读性。

我特别想知道的一件事是断言语句的使用。我读过不鼓励使用断言作为检查输入有效性的一种方式,但我想知道这是否是一种合法的使用方式。
如果没有,是否有类似的方法来执行此检查(或一般进行此验证)仍然非常简洁?

4

4 回答 4

10

你可以发明一个装饰器来验证你的论点。

这就是语法的样子:

@validate(0, int)
@validate(1, str, logMessage='second argument must respond to str()')
@validate(2, customValidationFunction)
def some_func(int_arg, str_arg, other_arg):
    # the control gets here only after args are validated correctly
    return int_arg * str_arg

这是验证装饰器工厂的简单实现。

def validate(narg, conv, logMessage = None):
    def decorate(func):
        def funcDecorated(*args):
            newArgs = list(args)
            try:
                newArgs[narg] = conv(newArgs[narg])
            except Exception, e:
                # wrong argument! do some logging here and re-raise
                raise Exception("Invalid argument #{}: {}".format(narg, e))
            else:
                return func(*newArgs)

        return funcDecorated
    return decorate

是的,这里有一些函数嵌套,但这一切都说得通。让我解释:

  • 装饰器是接受一个函数并返回另一个函数的东西
  • 我们希望validate(narg, converter)成为一个函数,它接受一些设置并返回一个特定的装饰器 ( decorate),它根据这些设置运行
  • decorate然后用于装饰给定函数 ( func),该函数接受一些位置参数,方法是创建一个新函数,该函数接受与( )funcDecorated相同的参数,并根据输入函数和初始设置编写。func*argsfuncnargconv

实际的验证发生在 funcDecorated 内部,它...

  • 接受输入参数列表,
  • conv通过验证和/或转换它(无论做什么)替换第 n 个参数,
  • func使用更改的参数列表调用输入。

为了对多个参数执行此操作,我们validate使用不同的参数多次应用。(可以将其重写为仅装饰一次,但这样 IMO 看起来更清晰。)

在行动中看到它:http: //ideone.com/vjgIS


请注意,conv可以采取任何行动...

  • 作为验证函数(返回它收到的任何内容,但如果它无效则抛出)
  • 作为转换函数(返回转换后的值,如果不能转换则抛出)

请注意,此实现不处理关键字参数。

于 2012-08-28T07:02:12.783 回答
1

使用断言非常适合检查参数的有效性(类型、类或值,这正是您要检查的内容)。

你说:

我特别想知道的一件事是断言语句的使用。

所以我认为这个页面对你有用。

特别是这部分:

断言应用于测试由于错误的用户输入或操作系统/环境故障而可能发生的故障情况,例如找不到文件。

于 2012-08-28T07:59:41.137 回答
1

更新:看看 mypy:https ://mypy.readthedocs.io/en/stable/

def some_func(int_arg: int, str_arg: str, other_arg):

@Vicent提供了一个很棒的页面的链接(也许它同时被编辑了)。如果您需要检查,建议按如下方式进行类型检查

from types import IntType, StringType
def some_func(int_arg, str_arg, other_arg):
    assert type(int_arg) == IntType, "id is not an integer: %r" % int_arg
    assert type(str_arg) == StringType or not str_arg
    assert other_arg in (VALUE1, VALUE2, VALUE3), "other arg must be VALUE1, VALUE2, or VALUE3"

assert 的好处是它显示了失败的代码行。

于 2017-05-12T15:10:01.473 回答
0

您可以查看合同库。它允许您声明对函数参数(甚至其返回值)的约束。

于 2012-08-28T08:04:02.480 回答