I would like to add many dummy-properties to a class via a decorator, like this:
def addAttrs(attr_names):
def deco(cls):
for attr_name in attr_names:
def getAttr(self):
return getattr(self, "_" + attr_name)
def setAttr(self, value):
setattr(self, "_" + attr_name, value)
prop = property(getAttr, setAttr)
setattr(cls, attr_name, prop)
setattr(cls, "_" + attr_name, None) # Default value for that attribute
return cls
return deco
@addAttrs(['x', 'y'])
class MyClass(object):
pass
Unfortunately, the decoarator seems to keep the reference of attr_name
instead of its content. Therefore, MyClass.x
and MyClass.y
access both MyClass._y
:
a = MyClass()
a.x = 5
print a._x, a._y
>>> None, 5
a.y = 8
print a._x, a._y
>>> None, 8
What do I have to change to get the expected behavior?
To provide data input integrity, a decorator can be used to decorate a method defined in the class.
We use @classmethod decorator in python to create a class method and we use @staticmethod decorator to create a static method in python.
To decorate a function with a class, we must use the @syntax followed by our class name above the function definition. Following convention, we will use camel-case for our class name. In the class definition, we define two methods: the init constructor and the magic (or dunder) call method.
In Python, the @classmethod decorator is used to declare a method in the class as a class method that can be called using ClassName. MethodName() . The class method can also be called using an object of the class. The @classmethod is an alternative of the classmethod() function.
The @property is a built-in decorator for the property() function in Python. It is used to give "special" functionality to certain methods to make them act as getters, setters, or deleters when we define properties in a class.
You almost had it working. There is just one nit. When creating the inner functions, bind the current value of attr_name
into the getter and setter functions:
def addAttrs(attr_names):
def deco(cls):
for attr_name in attr_names:
def getAttr(self, attr_name=attr_name):
return getattr(self, "_" + attr_name)
def setAttr(self, value, attr_name=attr_name):
setattr(self, "_" + attr_name, value)
prop = property(getAttr, setAttr)
setattr(cls, attr_name, prop)
setattr(cls, "_" + attr_name, None) # Default value for that attribute
return cls
return deco
@addAttrs(['x', 'y'])
class MyClass(object):
pass
This produces the expected result:
>>> a = MyClass()
>>> a.x = 5
>>> print a._x, a._y
5 None
>>> a.y = 8
>>> print a._x, a._y
5 8
Hope this helps. Happy decorating :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With