The instance of a descriptor is a property of the object class 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 stored in the instance to which the descriptor is associated - so that the methods descriptor __get__
, __set__
and __del__
are given the "instance" parameter.
(You can 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 logic aside to delete the data for instances that do not there are more).
In general, as it is in your own code, these values are saved in the instance itself - or with direct access to the attribute, with the .
operator, or it 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 check - is retrieved from the instance, not from the descriptor. You can either "hardcode" access to that value, or rather simply use the descriptor itself to access the value - which is better.
Another cool thing you can use, since you are writing your descriptors, is to implement __set_name__
method, which exists from Python 3.6 onwards, and is called when the class that defined the descriptor is created, named that he will have. This allows you to internally store the descriptor name, and use a variant of that name (for example, with a prefix), to store the related values in __dict__
of the instance.
Before I put a concrete example, one last tip: do not use __
as a prefix of variable names in Python to think they are "private variables". This prefix does name mangling - it is something that was not meant to indicate that a variable is private - it is for a class to have variables independent of other classes that inherit from it. Python has no private variables - it has the convention that names started with _
(a single underline) are private - and this signals that users of their classes do not access those 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)
...
The answer to your question is how to recover the value of the other descriptor: simply make the explicit call to the __get__
method, passing the instance and owner parameters.
(Of course you do not need to have a base class for descriptors, but in this example the __get__
and __set_name__
methods would be the same - no need to repeat code)
But then - perhaps 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, like string itself. This way, you can use% builtin% of Python, and retrieve the value that is in the instance for the other attribute, which does not have 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("...")