Consider this toy example where I use a descriptor to validate that a particular value doesn't exceed certain maximum value
class MaxValidator:
def __init__(self, max=10):
self.max = max
def __set__(self, obj, value):
if value > self.max:
raise RuntimeError(f"value {value} must be smaller than {self.max}")
obj._value = value
def __get__(self, obj):
return obj._value
class MyValue:
value = MaxValidator(max=5)
def __init__(self, value):
self.value = value # implicit validation takes place here
What happens now if I want a validator with a maximum value different than 5? The only solution I got was to create a class factory function:
def MyValueFactory(maximum):
class _MyValue:
value = MaxValidator(max=maximum)
def __init__(self, value):
self.value = value # implicit validation takes place here
return _MyValue
MyValue = MyValueFactory(5) # this class has the same validator as the previous MyValue
I think a class factory function is a bit of overkill. Is there another pattern I can use when dealing with "parameterized" python descriptors?
Attempt to insert the descriptor in __init__
class MyValue:
def __init__(self, value, maximum=5):
self.value = MaxValidator(max=maximum)
# but the following is not possible anymore
self.value = value #this is reassignment to self.value, the descriptor is lost
Don´t forget that at execution time, the descriptors method for
__get__and__set__have access to the instance and class where they live in.So, all you need is a class attribute (and even an instance attribute) to configure the behavior of your descriptor class-wide. That can be done either with a fixed name, that will affect all descriptors of a certain kind, or better yet, a descriptor could check for its name - which can also be automatically attributed, and use that as a prefix for the maximum.
This is one other way of doing it. The factory function is not that terrible as well - but you seem to have forgotten the descriptor can check the class and instances themselves.
As for creating the descriptor itself inside
__init__- it is possible, but one have to keep in mind the descriptor must be a class attribute, and whenver you create a new instance of the class, the descriptor would be overriden with the new configurations: