How to list all fields of a class (and no methods)?

88,389

Solution 1

You can get it via the __dict__ attribute, or the built-in vars function, which is just a shortcut:

>>> class A(object):
...     foobar = 42
...     def __init__(self):
...         self.foo = 'baz'
...         self.bar = 3
...     def method(self, arg):
...         return True
...
>>> a = A()
>>> a.__dict__
{'foo': 'baz', 'bar': 3}
>>> vars(a)
{'foo': 'baz', 'bar': 3}

There's only attributes of the object. Methods and class attributes aren't present.

Solution 2

You could use the built-in method vars()

Solution 3

The basic answer is "you can't do so reliably". See this question.

You can get an approximation with [attr for attr in dir(obj) if attr[:2] + attr[-2:] != '____' and not callable(getattr(obj,attr))].

However, you shouldn't rely on this, because:

Because dir() is supplied primarily as a convenience for use at an interactive prompt, it tries to supply an interesting set of names more than it tries to supply a rigorously or consistently defined set of names, and its detailed behavior may change across releases.

In other words, there is no canonical way to get a list of "all of an object's attributes" (or "all of an object's methods").

If you're doing some kind of dynamic programming that requires you to iterate over unknwon fields of an object, the only reliable way to do it is to implement your own way of keeping track of those fields. For instance, you could use an attribute naming convention, or a special "fields" object, or, most simply, a dictionary.

Solution 4

This should work for callables:

[f for f in dir(o) if not callable(getattr(o,f))]

You could get rid of the rest with:

[f for f in dir(o) if not callable(getattr(o,f)) and not f.startswith('__')]

Solution 5

You can iterate through an instance's __dict__ attribute and look for non-method things. For example:

CALLABLES = types.FunctionType, types.MethodType
for key, value in A().__dict__.items():
    if not isinstance(value, CALLABLES):
        print(key)

Output:

foo
bar

You can do it in a single statement with a list comprehension:

print([key for key, value in A.__dict__.items() if not isinstance(value, CALLABLES)])

Which would print ['foo', 'bar'].

Share:
88,389
Eric Wilson
Author by

Eric Wilson

Software developer, experienced in Java and Python in Linux environments, formerly a professor of mathematics. I'm a father of five children, and a husband of one wife.

Updated on November 13, 2020

Comments

  • Eric Wilson
    Eric Wilson over 3 years

    Suppose o is a Python object, and I want all of the fields of o, without any methods or __stuff__. How can this be done?

    I've tried things like:

    [f for f in dir(o) if not callable(f)]
    
    [f for f in dir(o) if not inspect.ismethod(f)]
    

    but these return the same as dir(o), presumably because dir gives a list of strings. Also, things like __class__ would be returned here, even if I get this to work.

  • 2rs2ts
    2rs2ts about 10 years
    This is per-instance.
  • 2rs2ts
    2rs2ts about 10 years
    Almost. I got ['__dict__', '__doc__', '__module__', '__weakref__', 'a', 'b'] with a dummy class with a and b as class members.
  • Eric Wilson
    Eric Wilson about 10 years
    Close, but still includes __dict__, __doc__, etc.
  • Admin
    Admin about 10 years
    This is probably the best approximation you're going to get, but it should be noted that this considers callable instance attributes (which are sometimes used and are effectively like methods) as non-methods and considers class attributes with no corresponding instance attribute not a field (even though it acts like one for most purposes). It also ignores properties and fails on classes with __slots__, which may or may not matter.
  • 2rs2ts
    2rs2ts about 10 years
    I'm not sure, but if you can create your own members surrounded by double underscores, this'll break.
  • BrenBarn
    BrenBarn about 10 years
    @2rs2ts: Yes, that is true. There is no way around that. There's no way to programatically tell if a double-underscore name is "magic" or not; you have to read the documentation.
  • Benjamin Toueg
    Benjamin Toueg about 10 years
    These are naming conventions, you shouldn't create your own members by surrounding them with double underscores.
  • Blckknght
    Blckknght about 10 years
    There's no need to filter out methods, since they're in the class __dict__, not the instance __dict__s.
  • martineau
    martineau about 10 years
    @Blckknght: I put the test in because a class is an object, too. However, I realized after your comment the need to check for than one type of callable, and have modified my answer. Thanks. If one can assume that the object is a new-style class instance, then what you said is true and the for loops could be simplified.
  • martineau
    martineau about 6 years
    @Benjamin: Indeed. At the end of the Descriptive: Naming Styles section of PEP 8 - Style Guide for Python Code, regarding names with both double-underscore-prefix & suffixes, it says "Never invent such names; only use them as documented."
  • Eray Erdin
    Eray Erdin about 5 years
    I'm in favor of calling other methods instead of mangled magic methods and properties in Python. I think this is much more Pythonic way. So, take my +1.
  • John Sohn
    John Sohn over 2 years
    also doesn't work if __dict __ is not defined. which it isn't always.
  • John Sohn
    John Sohn over 2 years
    slots doesn't always contain everything you're looking for either.
  • John Sohn
    John Sohn over 2 years
    Traceback (most recent call last): File "<string>", line 1, in <module> TypeError: vars() argument must have dict attribute
  • John Sohn
    John Sohn over 2 years
    I'm starting to find myself in agreement with this.
  • John Sohn
    John Sohn over 2 years
    I'd add this.[f for f in dir(m) if not callable(getattr(m,f)) and not f.startswith('__')]
  • El Bert
    El Bert over 2 years
    @JohnSohn hard to figure out the issue from just the error message but it is telling us that whatever is passed to vars() doesn't have a __dict__ attribute indicating that what's passed probably isn't an object. And indeed with a = "text"; vars(a) we get a similar message.