基于Python descriptor的输入验证重输逻辑及方案合理性咨询
问题1:校验失败后重新触发用户输入的实现
你之前尝试在descriptor抛出异常时调用instance.start()会出现逻辑耦合甚至递归死循环的问题,正确的做法是将重试逻辑放到执行属性赋值的start()方法中,通过循环+异常捕获实现重试:
def start(self): while True: input_name = input("Please enter your name: ") try: self.name = input_name break # 校验通过就退出循环 except ValueError as e: print(e) # 打印错误提示,引导用户重新输入
问题2:当前descriptor实现方案的合理性与优化
你当前的方案存在明显缺陷,不合理的点有两个:
- 属性值存储在descriptor实例本身:由于descriptor是类属性,所有
InputForm的实例会共享同一个InputValidator实例,会出现多实例下属性值互相覆盖的问题,完全不符合正常使用逻辑。 - 硬绑定校验规则与属性名:当前descriptor只支持小写校验,且固定把值存在
self._name,无法复用在其他属性、其他校验规则的场景。
最优实现方案
可以实现一个通用的校验descriptor,支持自定义校验规则,且把属性值存储在对应实例上,避免多实例冲突:
class Validator: def __set_name__(self, owner, name): # 获取descriptor绑定的属性名,自动生成实例上存储的key self.storage_key = f"_{name}" def __init__(self, validation_rule, error_msg): self.validation_rule = validation_rule self.error_msg = error_msg def __set__(self, instance, value): # 执行传入的校验规则 if not self.validation_rule(value): raise ValueError(self.error_msg) # 值存在对应实例上,不是descriptor本身 setattr(instance, self.storage_key, value) def __get__(self, instance, owner): return getattr(instance, self.storage_key, "") class InputForm: # 定义name属性的校验规则:全小写 name = Validator( validation_rule=lambda x: x == x.lower(), error_msg="Please enter lowercase letters" ) def __init__(self): self.name = "" self.start() def start(self): while True: input_name = input("Please enter your name: ") try: self.name = input_name break except ValueError as e: print(e) INPUT = InputForm() print(INPUT.name)
上面的实现同时解决了所有问题:
- 重试逻辑和校验逻辑完全解耦,descriptor只负责校验和属性读写,不涉及业务逻辑
- Validator可以复用在任意属性、任意校验规则的场景,比如后续加年龄字段可以直接写
age = Validator(lambda x: x.isdigit() and 0 < int(x) < 120, "请输入合法年龄")
内容的提问来源于stack exchange,提问作者Not_here_to_play
相关产品推荐
相关产品推荐

