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

6

94

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.

Bight answered 21/2, 2014 at 21:0 Comment(5)
I've been noodling with inspect but can't get it 100% right, but that module might help?Monometallic
I've looked at inspect also ...Bight
possible duplicate of Python dictionary from an object's fieldsNusku
have you tried the built in vars function or the field .__dict__ for your object?Thisbe
@CharlieParker tough to say. The specific memory fades after 8.5 years.Bight
B
117

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.

Biped answered 21/2, 2014 at 21:3 Comment(4)
This is per-instance.Monometallic
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.Padrone
also doesn't work if __dict __ is not defined. which it isn't always.Australopithecus
slots doesn't always contain everything you're looking for either.Australopithecus
B
21

You could use the built-in method vars()

Belvia answered 21/2, 2014 at 21:5 Comment(3)
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.Clywd
Traceback (most recent call last): File "<string>", line 1, in <module> TypeError: vars() argument must have dict attributeAustralopithecus
@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.Belvia
D
15

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.

Decigram answered 21/2, 2014 at 21:6 Comment(5)
I'm not sure, but if you can create your own members surrounded by double underscores, this'll break.Monometallic
@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.Decigram
These are naming conventions, you shouldn't create your own members by surrounding them with double underscores.Orthopsychiatry
@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."Ensphere
I'm starting to find myself in agreement with this.Australopithecus
O
9

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('__')]
Orthopsychiatry answered 21/2, 2014 at 21:3 Comment(4)
Almost. I got ['__dict__', '__doc__', '__module__', '__weakref__', 'a', 'b'] with a dummy class with a and b as class members.Monometallic
Close, but still includes __dict__, __doc__, etc.Bight
I'd add this.[f for f in dir(m) if not callable(getattr(m,f)) and not f.startswith('__')]Australopithecus
Note that __dict__ is a better option if you want to preserve the order of the fields.Raffinate
E
6

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'].

Ensphere answered 21/2, 2014 at 21:42 Comment(2)
There's no need to filter out methods, since they're in the class __dict__, not the instance __dict__s.Bolognese
@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.Ensphere
Y
-3

You can get it via fields attribute: o._fields_

Youngran answered 28/12, 2022 at 23:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.