我正在努力寻找一个好的解决方案,而且没有任何想法。作为练习,我试图创建一个上下文管理器来处理数据验证,例如:
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但是我意识到,在修补原始函数时,我无法调用它,例如:
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
但这不是那么漂亮。正如我所说,这是一项练习,而不是实际问题。
您可以使用常规的上下文管理器来包装unittest.mock.patch,并在对原始input函数进行修补之前保存对原始函数的引用。然后,您可以将原件传递input给您的patched函数:
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