, , (, ), - , .;)
:
from abc import ABCMeta, abstractproperty
class ValidateProperty:
def __init__(inst, exception, arg_type, valid):
inst.exception = exception
inst.arg_type = arg_type
inst.validator = valid
def __call__(inst, func):
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):
return
@luminance.setter
@ValidateProperty(Exception, int, lambda x: 0 <= x <= 15)
def luminance(self, value):
return
class TestClass(AbstractTestClass):
val = 7
@property
def luminance(self):
return self.val
@luminance.setter
def luminance(self, value):
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 , : self new_value
class , , __init__ __call__, def d
,
( )
, abstractproperty, :
from abc import ABCMeta, abstractproperty
class AbstractTestClassMeta(ABCMeta):
def __new__(metacls, cls, bases, clsdict):
new_cls = super().__new__(metacls, cls, bases, clsdict)
base_dicts = [b.__dict__ for b in bases]
if not base_dicts:
return new_cls
for name, obj in clsdict.items():
if not isinstance(obj, (property)):
continue
prop_set = getattr(obj, 'fset')
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 , . :
- , ,
- abstractproperty
fset, , ValidateProperty - ,
ValidateProperty :
class ValidateProperty:
def __init__(self, exception, arg_type):
self.exception = exception
self.arg_type = arg_type
self.validator = None
def __call__(self, func_or_value):
if self.validator is None:
self.validator = func_or_value
return self
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):
pass
@luminance.setter
@ValidateProperty(Exception, int)
def luminance(self, value):
return 0 <= value <= 15
:
class TestClass(AbstractTestClass):
val = 7
@property
def luminance(self):
return self.val
@luminance.setter
def luminance(self, value):
self.val = value