Add an attribute spec descriptor.

Here's something I've thought about off and on for a while.
Occasionally it would be useful to me to have a class attribute I can use to represent an attribute that will exist on *instances* of the class. Properties provide that to an extent, but they are data descriptors which means they will not defer to like-named instance attributes. However, a similar non-data descriptor would fit the bill.
For the sake of clarity, here is a simple implementation that demonstrates what I mean. I know it's asking a lot <wink>, but try to focus on the idea rather than the code. I've posted a more complete (and feature-rich) implementation online [1].
class Attr: """A non-data descriptor specifying an instance attribute.""" def __init__(self, name, doc=None): self.__name__ = name self.__doc__ = doc def __get__(self, obj, cls): if obj is None: return self else: # The attribute wasn't found on the instance. raise AttributeError(self.__name__)
def attribute(f=None): """A decorator that converts a function into an attribute spec.""" return Attr(f.__name__, f.__doc__)
def attrs(names): """A class decorator that adds the requested attribute specs.""" def decorator(cls): for name in names: attr = Attr(name) setattr(cls, name, attr) return cls return decorator
Other features not shown here (see [1]):
* an optional "default" Attr value * an optional "type" Attr (derived from f.__annotations__['return']) * __qualname__ * auto-setting self.__name__ during the first Attr.__get__() call * a nice repr * Attr.from_func() * proper ABC handling in attrs() (not an obvious implementation) * optionally inheriting docstrings
Such a descriptor is particularly useful for at least 2 things:
1. indicating that an abstractproperty is "implemented" on *instances* of a class 2. introspecting (on the class) all the attributes of instances of a class
Alternatives:
* "just use a property". As already noted, a property would work, but is somewhat cumbersome in the case of writable attributes. A non-data descriptor is a more natural fit in that case. * for #1, "just use a normal class attribute". This would mostly work. However, doing so effectively sets a default value, which you may not want. Furthermore, it may not be clear to readers of the code (or of help()) what the point of the class attr is.
Thoughts?
-eric
[1] https://bitbucket.org/ericsnowcurrently/odds_and_ends/src/default/attribute.... [2] Where would Attr/attribute/attrs live in the stdlib? inspect? types?

In selling this idea, I would focus on the immediate impact it could have on "help(cls)", as well as the automated testing possibilities (checking all attributes are set on an instance).
There's also the class-only descriptor behaviour we added for enums to consider, where retrieval via an instance throws AttributeError.
Essentially - interesting idea, but one you can experiment with outside the stdlib :)
Cheers, Nick.

On 01/20/2014 10:26 PM, Eric Snow wrote:
Occasionally it would be useful to me to have a class attribute I can use to represent an attribute that will exist on *instances* of the class. Properties provide that to an extent, but they are data descriptors which means they will not defer to like-named instance attributes. However, a similar non-data descriptor would fit the bill.
Have you checked out Lib/types.py/DynamicClassAttribute ?
It may be worth building on that.
-- ~Ethan~
participants (3)
-
Eric Snow
-
Ethan Furman
-
Nick Coghlan