Reputation: 1780
Is there a way to apply the same property logic to a set of attributes in a class? For example, I want to apply the same @attr1.setter
decorator to attr2
, attr3
, and attr4
without having to define the property for each attribute.
class Sample:
def __init__(self):
self.attr1 = None
self.attr2 = None
self.attr3 = None
self.attr4 = None
@property
def attr1(self):
return self.__attr1
@attr1.setter
def attr1(self, val):
if val < 0:
self.__attr1 = 0
else:
self.__attr1 = val
Upvotes: 4
Views: 695
Reputation: 6506
You could override the __getattr__
and __setattr__
to behave the way you want them. This way you don't need to define any private variables nor initialize any of the member variables either.
class Sample:
def __getattr__(self, attr):
return self.__dict__.get(attr)
def __setattr__(self, attr, val):
if val is not None and val < 0:
self.__dict__[attr] = 0
else:
self.__dict__[attr] = val
s = Sample()
print(s.attr1) # None
s.attr1 = 10
print(s.attr1) # 10
s.attr1 = -10
print(s.attr1) # 0
s.attr1 = None
print(s.attr1) # None
Upvotes: 1
Reputation: 95873
Just create your own descriptor for this:
class MyDescriptor:
def __set_name__(self, owner, name):
self.name = f'_{name}'
def __get__(self, instance, owner):
return getattr(instance, self.name)
def __set__(self, instance, val):
if val is None:
setattr(instance, self.name, None)
elif val < 0:
setattr(instance, self.name, 0)
else:
setattr(instance, self.name, val)
class Sample:
attr1 = MyDescriptor()
attr2 = MyDescriptor()
attr3 = MyDescriptor()
attr4 = MyDescriptor()
def __init__(self):
self.attr1 = None
self.attr2 = None
self.attr3 = None
self.attr4 = None
Now, in action:
In [3]: s = Sample()
In [4]: s.attr1 = -99
In [5]: s.attr1
Out[5]: 0
In [6]: s.attr2
In [7]: s.attr2 = 10
In [8]: s.attr2
Out[8]: 10
In [9]: s.attr2 = -1
In [10]: s.attr2
Out[10]: 0
See the Descriptor HOWTO and some more relevant documentation
Note, I incorporated the possibility of None
in your setter logic (your code would have raised a TypeError
on initialization of an instance, because the setter checks if None < 0
). Also note, you probably don't want to be using double-underscore name-mangling (which doesn't mean private), so I used the conventional single-underscore to denote a variable not part of the public api. Using double-underscore name-mangling complicates things here.
Upvotes: 1