I follow the tutorial out of the docs and an example by fluent Python. In the book they teach me how to avoid the AttributeError
by get, (e.g., when you do z = Testing.x
) and I wanted to do something simliar with the set method. But it seems like, it lead to a broken class with no error.
To be more specific about the issue:
- With outcommented line
Testing.x = 1
it invoke the__set__
methods. - With uncommented line
#Testing.x = 1
it does not invoke the__set__
methods.
Can someone teach me why it behaves this way?
import abc
class Descriptor:
def __init__(self):
cls = self.__class__
self.storage_name = cls.__name__
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(instance, self.storage_name)
def __set__(self, instance, value):
print(instance,self.storage_name)
setattr(instance, self.storage_name, value)
class Validator(Descriptor):
def __set__(self, instance, value):
value = self.validate(instance, value)
super().__set__(instance, value)
@abc.abstractmethod
def validate(self, instance, value):
"""return validated value or raise ValueError"""
class NonNegative(Validator):
def validate(self, instance, value):
if value <= 0:
raise ValueError(f'{value!r} must be > 0')
return value
class Testing:
x = NonNegative()
def __init__(self,number):
self.x = number
#Testing.x = 1
t = Testing(1)
t.x = 1
CodePudding user response:
The line
Testing.x = 1
replaces the descriptor you've set as a class attribute for Testing
with an integer.
Since the descriptor is no more, self.x = ...
or t.x = ...
is just an assignment that doesn't involve a descriptor.
As an aside, surely you've noticed there is no true x
attribute anymore with your descriptor, and you can't use more than one instance of the same descriptor without conflicts?
class Testing:
x = NonNegative()
y = NonNegative()
def __init__(self, number):
self.x = number
t = Testing(2345)
t.x = 1234
t.y = 5678
print(vars(t))
prints out
{'NonNegative': 5678}