继承验证类的直观方法
Posted
技术标签:
【中文标题】继承验证类的直观方法【英文标题】:Intuative way to inherit validating classes 【发布时间】:2021-12-16 21:16:09 【问题描述】:我一直在使用这种继承方式来验证对象实例上设置的值,但我想知道是否有更流畅的方法来做到这一点。
我遵循一个规范,其中特定分类 (Foo
) 的项目包含特定组合 (Fe
) 的元素。
class Typed:
def __set__(self, obj, value):
assert isinstance(value, self._type), 'Incorrect type'
class Integer(Typed):
_type = int
class Float(Typed):
_type = float
class Positive(Typed):
def __set__(self, obj, value):
super().__set__(obj, value)
assert value >= 0, 'Positive Values Only Accepted'
class PositiveInteger(Integer, Positive):
pass
class PositiveFloat(Float, Positive):
pass
class Sized(Typed):
def __set__(self, obj, value):
super().__set__(obj, value)
assert value <= 2**self.size-1, f'value is too High'
class Fe(Sized, PositiveInteger):
name = 'Integer, 8 bit unsigned'
size = 8
class Foo(Fe):
name = 'Classificaion1'
def __set__(self, obj, id):
super().__set__(obj, id)
obj._id = id
def __get__(self, obj, objType=None):
return obj._id
def __del__(self):
pass
【问题讨论】:
不要使用assert
;任何人都可以通过使用python -O ...
运行脚本来禁用您的检查。使用if
语句检查条件并改为引发ValueError
。
【参考方案1】:
如果您真的需要这种抽象级别,这可能是最好的方法。我的建议如下,也许可以为每节课节省一行。 如果您负担得起要定义的“大小”和“类型”等属性 在最终类上,可以像这样使用更丰富的基类和包含检查的声明性结构作为“lambda 函数”。
注意__init_subclass__
的用法来检查是否所有的参数
定义了保护表达式所需的:
from typing import Sequence
GUARDS =
"typed": ((lambda self, value: "Incorrect type" if not instance(value, self._type) else None), ("_typed",)),
"positive": ((lambda self, value: "Only positive values" if value < 0 else None), ()),
"sized": ((lambda self, value: None if value <= 2 ** self.size - 1 else f"value must be smaller than 2**self.size"), ("size",)),
class DescriptorBase:
guards: Sequence[str]
def __init_subclass__(cls):
_sentinel = object()
for guard_name in cls.guards:
guard = GUARDS[guard_name]
required_attrs = guard[1]
missing = []
for attr in required_attrs:
if getattr(cls, attr, _sentinel) is _sentinel:
missing.append(attr)
if missing:
raise TypeError("Guarded descriptor cls.__name__ did not declare required attrs: missing")
def __set_name__(self, owner, name):
self._name = f"_name""
def __set__(self, instance, value):
errors = []
for guard_name in self.guards:
if (error:= GUARDS[guard_name](self, value)) is not None:
errors.append(error)
if errors:
raise ValueError("\n".join(errors))
setattr (instance, self._name, value)
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.name)
def __del__(self, instance):
delattr(instance, self._name)
class Foo(DescriptorBase):
guards = ("typed", "positive", "sized")
size = 8
type_ = int
# No other code required here: __get__, __set__, __del__ handled in superclass
class UseAttr:
# Actual smart-attr usage:
my_foo = Foo()
其实,如果你想要类层次结构,行数更少(不需要在每个类中声明__set__
方法),也可以使用这种方法:
只需更改__init_superclass__
即可在所有超类中收集“守卫”,
并在正在定义的类上合并一个警卫列表,然后
将您的可组合保护类定义为:
class Positive(BaseDescriptor):
guards = ("positive",)
class Sized(BaseDescriptor):
guards = ("sized",)
size = None
class Foo(Positive, Sized):
size = 8
class Fe(Foo):
name = "Fe name"
实际上,实现此功能所需的更改可以很简单:
def __init_subclass__(cls):
_sentinel = object()
all_guards = []
for supercls in cls.__mro__:
all_guards.extend(getattr(supercls, "guards", ()))
# filter unique:
seem =
new_guards = []
for guard in all_guards:
if guard not in seem:
new_guards.append(guard)
seem.add(guard)
cls.guards = new_guards
for guard_name in cls.guards:
还请注意,您还可以从每个定义的类中收集“GUARDS”注册表的内容,而不必事先将所有内容声明为 lambda。我想你可以从这里开始。
【讨论】:
以上是关于继承验证类的直观方法的主要内容,如果未能解决你的问题,请参考以下文章