gpt4 book ai didi

validation - 如何在抽象类级别提供值验证?

转载 作者:行者123 更新时间:2023-12-03 09:55:03 26 4
gpt4 key购买 nike

我有一个 ABC BaseAbstract 类,其中定义了几个 getter/setter 属性。

我想要求要设置的值是 int和从 0 - 15。

@luminance.setter
@abstractproperty
@ValidateProperty(Exception, types=(int,), valid=lambda x: True if 0 <= x <= 15 else False)
def luminance(self, value):
"""
Set a value that indicate the level of light emitted from the block

:param value: (int): 0 (darkest) - 15 (brightest)
:return:
"""
pass

有人可以帮我弄清楚我的 ValidateProperty 类/方法应该是什么样子。我从一个类(class)开始,并调用 accepts方法,但这会导致错误:

function object has no attribute 'func_code'



当前来源:
class ValidateProperty(object):
@staticmethod
def accepts(exception, *types, **kwargs):
def check_accepts(f, **kwargs):
assert len(types) == f.func_code.co_argcount

def new_f(*args, **kwds):
for i, v in enumerate(args):
if f.func_code.co_varnames[i] in types and\
not isinstance(v, types[f.func_code.co_varnames[i]]):
arg = f.func_code.co_varnames[i]
exp = types[f.func_code.co_varnames[i]]
raise exception("arg '{arg}'={r} does not match {exp}".format(arg=arg,
r=v,
exp=exp))
# del exp (unreachable)

for k,v in kwds.__iter__():
if k in types and not isinstance(v, types[k]):
raise exception("arg '{arg}'={r} does not match {exp}".format(arg=k,
r=v,
exp=types[k]))

return f(*args, **kwds)

new_f.func_name = f.func_name
return new_f

return check_accepts

最佳答案

我们中的一个人对如何 decorators 感到困惑, descriptors (例如属性)和 abstracts工作——我希望不是我。 ;)

这是一个粗略的工作示例:

from abc import ABCMeta, abstractproperty

class ValidateProperty:
def __init__(inst, exception, arg_type, valid):
# called on the @ValidateProperty(...) line
#
# save the exception to raise, the expected argument type, and
# the validator code for later use
inst.exception = exception
inst.arg_type = arg_type
inst.validator = valid
def __call__(inst, func):
# called after the def has finished, but before it is stored
#
# func is the def'd function, save it for later to be called
# after validating the argument
def check_accepts(self, value):
if not inst.validator(value):
raise inst.exception('value %s is not valid' % value)
func(self, value)
return check_accepts

class AbstractTestClass(metaclass=ABCMeta):
@abstractproperty
def luminance(self):
# abstract property
return
@luminance.setter
@ValidateProperty(Exception, int, lambda x: 0 <= x <= 15)
def luminance(self, value):
# abstract property with validator
return

class TestClass(AbstractTestClass):
# concrete class
val = 7
@property
def luminance(self):
# concrete property
return self.val
@luminance.setter
def luminance(self, value):
# concrete property setter
# call base class first to activate the validator
AbstractTestClass.__dict__['luminance'].__set__(self, value)
self.val = value

tc = TestClass()
print(tc.luminance)
tc.luminance = 10
print(tc.luminance)
tc.luminance = 25
print(tc.luminance)

结果是:
7
10
Traceback (most recent call last):
File "abstract.py", line 47, in <module>
tc.luminance = 25
File "abstract.py", line 40, in luminance
AbstractTestClass.__dict__['luminance'].__set__(self, value)
File "abstract.py", line 14, in check_accepts
raise inst.exception('value %s is not valid' % value)
Exception: value 25 is not valid

几点思考:
  • ValidateProperty简单得多,因为属性 setter 只需要两个参数:selfnew_value
  • 使用 class 时对于装饰器,并且装饰器接受参数,那么您将需要 __init__保存参数,和__call__实际处理 def d 函数
  • 调用基类属性 setter 很丑陋,但您可以将其隐藏在辅助函数中
  • 您可能希望使用自定义元类来确保运行验证代码(这也将避免丑陋的基类属性调用)


  • 我建议使用上面的元类来消除直接调用基类的 abstractproperty 的需要。 ,这是一个这样的例子:
    from abc import ABCMeta, abstractproperty

    class AbstractTestClassMeta(ABCMeta):

    def __new__(metacls, cls, bases, clsdict):
    # create new class
    new_cls = super().__new__(metacls, cls, bases, clsdict)
    # collect all base class dictionaries
    base_dicts = [b.__dict__ for b in bases]
    if not base_dicts:
    return new_cls
    # iterate through clsdict looking for properties
    for name, obj in clsdict.items():
    if not isinstance(obj, (property)):
    continue
    prop_set = getattr(obj, 'fset')
    # found one, now look in bases for validation code
    validators = []
    for d in base_dicts:
    b_obj = d.get(name)
    if (
    b_obj is not None and
    isinstance(b_obj.fset, ValidateProperty)
    ):
    validators.append(b_obj.fset)
    if validators:
    def check_validators(self, new_val):
    for func in validators:
    func(new_val)
    prop_set(self, new_val)
    new_prop = obj.setter(check_validators)
    setattr(new_cls, name, new_prop)

    return new_cls

    这个子类 ABCMeta ,并有 ABCMeta首先完成它的所有工作,然后进行一些额外的处理。即:
  • 浏览创建的类并查找属性
  • 检查基类以查看它们是否具有匹配的抽象属性
  • 检查抽象属性的fset代码查看它是否是 ValidateProperty 的实例
  • 如果是这样,请将其保存在验证器列表中
  • 如果验证器列表不为空
  • 制作一个包装器,在调用实际属性的 fset 之前调用每个验证器代码
  • 将找到的属性替换为使用包装器作为 setter 的新属性。代码
  • ValidateProperty也有点不同:
    class ValidateProperty:

    def __init__(self, exception, arg_type):
    # called on the @ValidateProperty(...) line
    #
    # save the exception to raise and the expected argument type
    self.exception = exception
    self.arg_type = arg_type
    self.validator = None

    def __call__(self, func_or_value):
    # on the first call, func_or_value is the function to use
    # as the validator
    if self.validator is None:
    self.validator = func_or_value
    return self
    # every subsequent call will be to do the validation
    if (
    not isinstance(func_or_value, self.arg_type) or
    not self.validator(None, func_or_value)
    ):
    raise self.exception(
    '%r is either not a type of %r or is outside '
    'argument range' %
    (func_or_value, type(func_or_value))
    )

    底座 AbstractTestClass现在使用新的 AbstractTestClassMeta , 并直接在 abstractproperty 中有验证器代码:
    class AbstractTestClass(metaclass=AbstractTestClassMeta):

    @abstractproperty
    def luminance(self):
    # abstract property
    pass

    @luminance.setter
    @ValidateProperty(Exception, int)
    def luminance(self, value):
    # abstract property validator
    return 0 <= value <= 15

    最后一个类是相同的:
    class TestClass(AbstractTestClass):
    # concrete class

    val = 7

    @property
    def luminance(self):
    # concrete property
    return self.val

    @luminance.setter
    def luminance(self, value):
    # concrete property setter
    # call base class first to activate the validator
    # AbstractTestClass.__dict__['luminance'].__set__(self, value)
    self.val = value

    关于validation - 如何在抽象类级别提供值验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35369824/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com