Reputation: 92397
I'm writing a little validation API and initialize the constraints for each field in the class' __init__
method. But this setup could principially be done for the class a single time.
class Demo(Validatable):
def __init__(self):
Validatable.__init__(self)
self.count = 11
# could be done at class level
self.constrain_field("count", Max(9), Even())
But the problem is, that the constraints for each field have to be stored somewhere and the data structure to do that is part of the inherited class Validatable
. So all derived classes would share the same data structure, if the constraints were set at class level, what should not happen!
class Demo(Validatable):
# doesn't work!
Validatable.constrain_field("count", Max(9), Even())
def __init__(self):
self.count = 11
Is there a possibility to inherit the data structure and initialize it at class level in the derived class without sharing the data structure for the constraints?
Upvotes: 2
Views: 236
Reputation: 19329
There are two parts to this question.
Validatable
data structures at the subclass level, instead of in the inherited Validatable
class; andconstrain_field
method so that it can be called once on class initialization, instead of every time an instance is created.With respect to (1), the initializer of the Validatable
class can access the class of the instance using its __class__
property. For example:
class Validatable(object):
def __init__(self):
self.__class__.fieldName = "value for " + self.__class__.__name__
class Demo(Validatable):
def __init__(self):
super(Demo, self).__init__()
class Demo2(Validatable):
def __init__(self):
super(Demo2, self).__init__()
d = Demo()
d2 = Demo2()
print "Demo.fieldName = " + Demo.fieldName
print "Demo2.fieldName = " + Demo2.fieldName
This codes prints:
Demo.fieldName = value for Demo
Demo2.fieldName = value for Demo2
The constrain_field
method could then be defined to use the __class__
property of the instance it is called with to set up the necessary data structures.
Unfortunately this all requires that an instance of the class be created before the data structures can be set up and it also means that the constrain_field
method is called every time an instance is created. Clearly it would be preferable to do this on class initialization, which is part (2) of the question.
To solve part (2), I would recommend using python decorators. Consider the following code which combines the Python property function (used as a decorator) with a custom decorator function called constrain_field
:
def Max(maxValue):
def checkMax(value):
return value <= maxValue
checkMax.__doc__ = "Value must be less than or equal to " + str(maxValue)
return checkMax
def Even():
def checkEven(value):
"Value must be even"
return value%2 == 0
return checkEven
def constrain_field(*constraints):
def constraint_decorator(setter):
def checkConstraints(self, value):
ok = True
for c in constraints:
if not c(value):
ok = False
print "Constraint breached: " + c.__doc__
if ok:
setter(self, value)
return checkConstraints
return constraint_decorator
class Demo(object):
def __init__(self):
self._count = 2
@property
def count(self):
return self._count
@count.setter
@constrain_field(Max(9), Even())
def count(self, value):
self._count = value
d = Demo()
print "Setting to 8"
d.count = 8
print "Setting to 9"
d.count = 9
print "Setting to 10"
d.count = 10
print "Count is now " + str(d.count)
It prints:
Setting to 8
Setting to 9
Constraint breached: Value must be even
Setting to 10
Constraint breached: Value must be less than or equal to 9
Count is now 8
By using decorators in this fashion, all the initialization is done once during the definition of the class.
Upvotes: 2