how to get names of attributes

Steven D'Aprano steve at pearwood.info
Wed Dec 30 18:58:17 EST 2015


On Wed, 30 Dec 2015 10:51 pm, Charles T. Smith wrote:

> Hi,
> 
> How can I get *all* the names of an object's attributes?  

In the most general case, you cannot.

Classes can define a __getattr__ method (and a __getattribute__ method, for
new-style classes only) which implement dynamic attributes. These can be
*extremely* dynamic and impossible to predict ahead of time. Starting from
the simplest cases to the most horrible:

    def __getattr__(self, name):
        if name == "spam":
            return 1
        elif name == self._attribute_name:
            return 2
        elif name == some_function(10, 20, 30):
            return 3
        elif name.lower() in ("x", "y") or name.startswith("foo"):
            return 4
        elif 1626740500 <= hash(name) <= 1626740600:
            return 5
        elif name == "surprise" and random.random() < 0.5:
            return 6
        raise AttributeError


So you can see that even in principle, there is no way for the Python
interpreter to look inside the __getattr__ method and determine what
attributes exist.

Fortunately, there's a way around that: you can customise the list of
attribute names returned by dir():

py> class X:
...     def __dir__(self):
...             return dir(X) + ["a", "b"]
...     def spam(self):
...             pass
...
py> x = X()
py> dir(x)
['__dir__', '__doc__', '__module__', 'a', 'b', 'spam']


So if you have dynamic attributes generated by __getattr__, the polite thing
to do is to return their names from __dir__.



> I have legacy 
> code with mixed new style classes and old style classes and I need to
> write methods which deal with both.  That's the immediate problem, but
> I'm always running into the need to understand how objects are linked, in
> particular when in pdb.  The answers one always sees on StackOverflow is
> that you don't need to understand, understanding is not the pythonic way
> to do things.

That's why I don't think much of the majority of StackOverflow answers.


> Alternatively, is there are map documented somewhere - more complete than
> python/python-2.7.3-docs-html/library/stdtypes.html?
> highlight=class#special-attributes
> 
> Or, is the code available uncompiled somewhere on my machine?

That depends on how you installed Python. If you installed it from source,
then it will be, unless you deleted it after compiling. But it is easy
enough to get the Python source code:

https://www.python.org/downloads/source/

https://docs.python.org/devguide/setup.html#checkout

https://hg.python.org/cpython/file/tip


> Does anyone know *why* the __members__ method was deprecated, to be
> replaced by dir(), which doesn't tell the truth (if only it took an
> optional parameter to say: "be truthful")

dir() is intentionally meant for use in the interactive interpreter, to
return "interesting" attributes. It isn't really intended for programmatic
use.

For that, you might wish to write your own version of dir(). Here is some
pseudo-code:


def mydir(obj):
    if type(obj) defines __dir__, return the output of __dir__
    names = set()
    if obj is an instance:
        if obj has __slots__, then add each slot to names;
        try:
            add each key from vars(obj) to names;
        except TypeError:
            # no instance __dict__
            pass
        call mydir(type(obj)) and add the names it returns to names;
    else obj must be a class or type:
        add each key from vars(obj) to names;
        if obj is an old-style class:
            do the same for each class in obj.__bases__;
        else obj must be a new-style class:
            do the same for each class in obj.__mro__
    return sorted(names)

But I expect that the differences between this and what the built-in dir()
return will be minimal, and only attributes which are part of the machinery
used to get classes themselves working, not part of your class or instance.


-- 
Steven




More information about the Python-list mailing list