上下文管理器来验证数据

发布于 2021-01-29 14:59:23

我正在努力寻找一个好的解决方案,而且没有任何想法。作为练习,我试图创建一个上下文管理器来处理数据验证,例如:

validation = lambda x: len(x) <= 10

with validator(validation):
    some_data = input("Please enter a name of 10 characters or less: ")

print(some_data)

# OUTPUT
>> Please enter a name of 10 characters or less: FooBarSpamEggs
>> Please enter a name of 10 characters of less: Adam
Adam

最初,我考虑过使用此功能,unittest.mock.patch但是我意识到,在修补原始函数时,我无法调用它,例如:

def patched(validation, *args):
    while True:
        p = __builtins__.input(args) # Doesn't work
        if validation(p):
            break
    return p

with unittest.mock.patch('builtins.input', patched):
    input("Some prompt here: ")
# fails on recursion error as patched calls itself

然后,我考虑编写一个装饰器来验证单行,但这仅在您可以执行以下操作时才有用:

@validate(lambda x: int(x) == 6)
p = input("How many sides does a d6 have? ")
# can't decorate a function call

但是,我对这种上下文管理器想法很挂念。不幸的是,我不知道上下文管理器是否有权访问其内容,或者是否仅限于其参数。有什么想法吗?

顺便说一句,我知道我可以在一个函数中呈现此功能,例如:

def validate_input(prompt, validation, msg_if_fail=None):
    while True:
        p = input(prompt)
        if validation(p):
            break
        if msg_if_fail is not None:
            print(msg_if_fail)
    return p

但这不是那么漂亮。正如我所说,这是一项练习,而不是实际问题。

关注者
0
被浏览
84
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    您可以使用常规的上下文管理器来包装unittest.mock.patch,并在对原始input函数进行修补之前保存对原始函数的引用。然后,您可以将原件传递input给您的patched函数:

    import unittest.mock
    import contextlib
    from functools import partial
    
    def patched(validation, orig_input, *args):
        while True:
            p = orig_input(*args)
            if validation(p):
                break
        return p
    
    @contextlib.contextmanager
    def validator(validate_func):
        func = partial(patched, validate_func, input)  # original input reference saved here
        patch = unittest.mock.patch('builtins.input', func)
        patch.start()
        try:
            yield 
        finally:
            patch.stop()
    
    validation = lambda x: len(x) <= 10
    

    然后,您可以像这样使用contextmanager:

    with validator(validation):
        x = input("10 or less: ")
    
    x = input("10 or less (unpatched): ")
    print("done")
    

    样本输出:

    10 or less: abcdefghijklmnop
    10 or less: abcdefgdfgdgd
    10 or less: abcdef
    10 or less (unpatched): abcdefghijklmnop
    done
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看