The instance of a Descriptor is a property of the class of the object that uses the Descriptors. This means that if you are creating a Descriptor to store an individual value in each instance, that value has to be saved in the instance that Descriptor is associated with - which is why the Descriptor methods __get__
, __set__
and __del__
receive the parameter "instance".
(You may have another "global" object for your descriptord that saves the data separate from the instances - but it is not worth it, because you would have to create a whole logical part to erase the data referring to instances that no longer exist).
In general, as it is in your own code, these values are stored in the instance itself - or with direct access to the attribute, with the operator .
, or can be using the __dict__
of the instance, as you do.
but then, the value stored by the first Descriptor - the one you want to verify - is retrieved from the instance, not from Descriptor. You can either "hardcodar" the access to that value, or rather simply use Scriptor itself to access the value - which is better.
Another cool thing you can use, since you’re writing your Scriptors, is to implement method __set_name__
, which exists from Python 3.6 onwards, and is called when the class that defined the Descriptor is created, with the name it will have. With this, you can internally store the name of the Descriptor, and use a variant of that name (for example, with a prefix), to store the related values in the __dict__
of the instance.
Before I put a concrete example, one last tip: don’t use __
as a prefix of variable names in Python to find them to be "private variables". This prefix makes name mangling - something that has not been done to indicate that a variable is private - so that a class can have independent variables from other classes that it inherits from itself. Python has no private variables - has a convention of which names started with _
(a single underline) are private - and this signals your class users not to access these names directly.
def MyDescriptorBase:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__["_" + self.name]
def NonNegative(MyDescriptorBase):
def __set__(self, instance, value):
if value < 0:
raise ValueError("...")
instance.__dict__["_" + self.name] = value
def NoLessThanDescriptor(MyDescriptorBase):
def __init__(self, other_desc):
self.other_desc = other_desc
def __set__(self, instance, value):
# Para recuperar o valor que o outro descriptor tem:
instance_value = self.other_desc.__get__(instance, owner)
if value < instance_value:
raise ValueError("...")
# E então, você pode referenciar um descriptor
# como parâmetro de outro no corpo de uma classe,
# respeitando a ordem (só é possível referenciar
# um descriptor que já foi definido)
class Exemplo:
positivo = NonNegative()
maior_numero = NoLessThanDescriptor(positivo)
...
What answers your question is how to recover the value of the other Scriptor: simple call is made explicit to the method __get__
of it, passing the parameters of instance and Owner.
(Of course, you don’t need to have a base class for the Descriptors, but in this example the methods __get__
and __set_name__
would be the same - no need to repeat code)
But then - maybe you prefer this other approach, at the same time more generic and simpler: instead of passing another Descriptor that will be the basis of comparison, simply pass the name of an attribute, as string itself. That way you can use the builtin getattr
python, and retrieve the value that is in the instance for the other attribute, which does not need to be a Descriptor - can be any type of attribute:
def NoLessThanAttribute(MyDescriptorBase):
def __init__(self, other_attr: str):
self.other_attr = other_attr
def __set__(self, instance, value):
instance_value = getattr(instance, self.other_attr)
if value < instance_value:
raise ValueError("...")
Try creating an example minimum, complete and verifiable, would be easier to try to help.
– Sidon