[Python-ideas] Add an attribute spec descriptor.

Eric Snow ericsnowcurrently at gmail.com
Tue Jan 21 07:26:22 CET 2014


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.py
[2] Where would Attr/attribute/attrs live in the stdlib?  inspect? types?


More information about the Python-ideas mailing list