Implementing class descriptors by subclassing the `type` class
-
17-09-2020 - |
Question
I'd like to have some data descriptors as part of a class. Meaning that I'd like class attributes to actually be properties, whose access is handled by class methods.
It seems that Python doesn't directly support this, but that it can be implemented by subclassing the type
class. So adding a property to a subclass of type
will cause its instances to have descriptors for that property. Its instances are classes. Thus, class descriptors.
Is this advisable? Are there any gotchas I should watch out for?
Solution
It is convention (usually), for a descriptor, when accessed on a class, to return the descriptor object itself. This is what property
does; if you access a property object on a class, you get the property object back (because that's what it's __get__
method chooses to do). But that's a convention; you don't have to do it that way.
So, if you only need to have a getter descriptor on your class, and you don't mind that a an attempt to set will overwrite the descriptor, you can do something like this with no metaclass programming:
def classproperty_getter_only(f):
class NonDataDescriptor(object):
def __get__(self, instance, icls):
return f(icls)
return NonDataDescriptor()
class Foo(object):
@classproperty_getter_only
def flup(cls):
return 'hello from', cls
print Foo.flup
print Foo().flup
for
('hello from', <class '__main__.Foo'>)
('hello from', <class '__main__.Foo'>)
If you want a full fledged data descriptor, or want to use the built-in property object, then you're right you can use a metaclass and put it there (realizing that this attribute will be totally invisible from instances of your class; metaclasses are not examined when doing attribute lookup on an instance of a class).
Is it advisable? I don't think so. I wouldn't do what you're describing casually in production code; I would only consider it if I had a very compelling reason to do so (and I can't think of such a scenario off the top of my head). Metaclasses are very powerful, but they aren't well understood by all programmers, and are somewhat harder to reason about, so their use makes your code harder to maintain. I think this sort of design would be frowned upon by the python community at large.
OTHER TIPS
Is this what you mean by "class attributes to actually be properties, whose access is handled by class methods"?
You can use a decorator property
to make an accessor appear to be an actual data member. Then, you can use the x.setter
decorator to make a setter for that attribute.
Be sure to inherit from object
, or this won't work.
class Foo(object):
def __init__(self):
self._hiddenx = 3
@property
def x(self):
return self._hiddenx + 10
@x.setter
def x(self, value):
self._hiddenx = value
p = Foo()
p.x #13
p.x = 4
p.x #14