Reputation: 95
As the title says, I'm trying to give set/get functionality to my static classes in python. Basically, for a given static class with static attribute _x
, I want to define a static property x
that gives custom get and set function wrappers for _x
.
Strangely it seems like the official Python docs say this functionality should exist. The docs say that as of version 3.9, you can use the @classmethod
decorator to wrap property methods. However, it seems that this only works for getters, and not setters. For example, when I try to define a static property using this method, I get the following error:
class Foo:
_x = 0
@classmethod
@property
def x(self):
return self._x
@classmethod
@x.setter
def x(self, n):
self._x = n
AttributeError: 'classmethod' object has no attribute 'setter'
If I flip the order, like:
class Foo:
_x = 0
@property
@classmethod
def x(self):
return self._x
@x.setter
@classmethod
def x(self, n):
self._x = n
Then Foo.x
returns a property object instead of calling the property's fget
function.
So it seems this approach is not going to work. I then tried to exploit the way that function decorators work, by redefining Foo:
class Foo:
_x = 0
def __get_x(self):
return self._x
def __set_x(self, n):
self._x = n
x = classmethod(property(fget=__get_x, fset=__set_x))
But no luck. Whenever I try to set Foo.x
, i.e. with Foo.x = 2
, it does not call the property setter but instead directly overwrites x entirely.
One solution I've seen bounced around is using python metaclasses to implement this functionality. While that could fix the issue, it isn't ideal because A. I have many different static classes in my project and B. I use Sphinx to generate documentation for my code and it doesn't always play nice with metaclasses.
I've been looking for a solution to this issue for a while and I'm not sure what the best "pythonic" fix for it is. If metaclasses are the "cleanest" solution, how can I implement them in such a way that I don't have to write a lot of redundant code? Maybe I could write a custom function decorator that enables class property-esque functionality? Any help would be appreciated.
Upvotes: 3
Views: 3441
Reputation: 184191
An instance's properties must be defined on its class. To define properties on a class, the rule still holds: when considering the class as an instance, its properties must be defined on the class's class (its metaclass).
class Meta(type):
@property
def x(self):
return self._x
@x.setter
def x(self, n):
self._x = n
class Foo(metaclass=Meta):
_x = 0
Despite the drawbacks of using metaclasses, this is the way that works.
Upvotes: 5