I have made a custom descriptor which I've assigned to a value in my class. However I would like to set the instance variable initially without using the descriptors __set__
method.
class Descriptor:
def __set_name__(self, owner, name):
self.private_name = '_' + name
def __get__(self, instance, owner=None):
if instance is None:
return self
return getattr(instance, self.private_name)
def __set__(self, instance, value):
if value == 'bye':
raise ValueError('blah')
self.toggle(instance)
setattr(instance, self.private_name, value)
@staticmethod
def toggle(instance):
pass
class Foo:
bar = Descriptor()
def __init__(self, bar):
# no need to call descriptor initially for some reason ok? but is still called (unnecessary)
self.bar = bar
foo = Foo('hi')
foo.bar = 'bye' # descriptor needed here and with every subsequent change to bar.
Is there a way to work around this and set self.bar
directly?
I have this question as originally I was using the @property
descriptor, which when used allowed both the descriptor method and the private variable to be called, which was exactly what I needed. Since @property
is a descriptor there must be a sensible way to do this (Even though the reason this works with @property
is that the private variable is defined within the scope of my class, rather than the descriptor class as done with custom descriptors which allows for both to be modified within the class, but only the public version outside the scope of the class (I know private variables are not truly private but that's beside the point).)
For example this same code using @property:
class Foo:
def __init__(self, bar):
# no need to call setter initially
self._bar = bar
@property
def bar(self):
return self._bar
@bar.setter
def bar(self, value):
self._bar = value
foo = Foo('hi')
foo.bar = 'bye' # property setter called.
However in this case, the getters and setters take up way more room when more instance variables are introduced, and the toggle
method from the original decorator has to be rewritten for each new setter added (which is what caused me to rewrite the @properties as a custom descriptor class as it seemed like a place to use oop to not have to repeat myself).
None of that scope stuff you were thinking about actually matters. Your property
sets self._bar
, and you bypass it by setting self._bar
directly. Your custom descriptor does the exact same thing, just through setattr
, so you still bypass it by setting the instance's _bar
attribute directly:
class Foo
bar = Descriptor()
def __init__(self, bar):
self._bar = bar