我正在努力考虑一个很好的解决方案,并没有想到任何事情。作为练习,我正在尝试创建一个处理数据验证的上下文管理器,例如:
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 :(得分:2)
您可以使用常规上下文管理器来包装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
答案 1 :(得分:1)
接受参数的装饰器可以很好地完成(http://www.artima.com/weblogs/viewpost.jsp?thread=240845):
max10 = lambda x: len(x) <= 10
def validator(test):
def wrap(func):
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)
if test(result):
return result
return None
return wrapped
return wrap
使用它的常规方法:
@validator(max10)
def valid_input(prompt="Please enter a name: "):
return raw_input(prompt)
print valid_input()
手动应用装饰器看起来更接近你的要求:
valid_input = validator(max10)(raw_input)
print valid_input("Please enter a name: ")