14

Suppose we have the following class hierarchy:

class ClassA:

    @property
    def foo(self): return "hello"

class ClassB(ClassA):

    @property
    def bar(self): return "world"

If I explore __dict__ on ClassB like so, I only see the bar attribute:

for name,_ in ClassB.__dict__.items():

    if name.startswith("__"):
        continue

    print(name)

Output is bar

I can roll my own means to get attributes on not only the specified type but its ancestors. However, my question is whether there's already a way in python for me to do this without re-inventing a wheel.

def return_attributes_including_inherited(type):
    results = []
    return_attributes_including_inherited_helper(type,results)
    return results

def return_attributes_including_inherited_helper(type,attributes):

    for name,attribute_as_object in type.__dict__.items():

        if name.startswith("__"):
            continue

        attributes.append(name)

    for base_type in type.__bases__:
        return_attributes_including_inherited_helper(base_type,attributes)

Running my code as follows...

for attribute_name in return_attributes_including_inherited(ClassB):
    print(attribute_name)

... gives back both bar and foo.

Note that I'm simplifying some things: name collisions, using items() when for this example I could use dict, skipping over anything that starts with __, ignoring the possibility that two ancestors themselves have a common ancestor, etc.

EDIT1 - I tried to keep the example simple. But I really want both the attribute name and the attribute reference for each class and ancestor class. One of the answers below has me on a better track, I'll post some better code when I get it to work.

EDIT2 - This does what I want and is very succinct. It's based on Eli's answer below.

def get_attributes(type):

    attributes = set(type.__dict__.items())

    for type in type.__mro__:
        attributes.update(type.__dict__.items())

    return attributes

It gives back both the attribute names and their references.

EDIT3 - One of the answers below suggested using inspect.getmembers. This appears very useful because it's like dict only it operates on ancestor classes as well.

Since a large part of what I was trying to do was find attributes marked with a particular descriptor, and include ancestors classes, here is some code that would help do that in case it helps anyone:

class MyCustomDescriptor:

    # This is greatly oversimplified

    def __init__(self,foo,bar):
        self._foo = foo
        self._bar = bar
        pass

    def __call__(self,decorated_function):
        return self

    def __get__(self,instance,type):

        if not instance:
            return self

        return 10

class ClassA:

    @property
    def foo(self): return "hello"

    @MyCustomDescriptor(foo="a",bar="b")
    def bar(self): pass

    @MyCustomDescriptor(foo="c",bar="d")
    def baz(self): pass

class ClassB(ClassA):

    @property
    def something_we_dont_care_about(self): return "world"

    @MyCustomDescriptor(foo="e",bar="f")
    def blah(self): pass

# This will get attributes on the specified type (class) that are of matching_attribute_type.  It just returns the attributes themselves, not their names.
def get_attributes_of_matching_type(type,matching_attribute_type):

    return_value = []

    for member in inspect.getmembers(type):

        member_name = member[0]
        member_instance = member[1]

        if isinstance(member_instance,matching_attribute_type):
            return_value.append(member_instance)

    return return_value

# This will return a dictionary of name & instance of attributes on type that are of matching_attribute_type (useful when you're looking for attributes marked with a particular descriptor)
def get_attribute_name_and_instance_of_matching_type(type,matching_attribute_type):

    return_value = {}

    for member in inspect.getmembers(ClassB):

        member_name = member[0]
        member_instance = member[1]

        if isinstance(member_instance,matching_attribute_type):
            return_value[member_name] = member_instance

    return return_value
4
  • What exactly are you going to do with the attribute list once you have it? Commented Dec 16, 2011 at 2:44
  • The classes in question represent messages that have been deserialized. The messages use inheritance when they share fields. There's a method that needs to print information about each field of each message. Each attribute of these message classes is actually marked with a descriptor but I left that detail out since I thought it was irrelevant. Commented Dec 16, 2011 at 2:48
  • ... So, update the descriptor so that it adds the described attribute to a list? Or - and this might be a bit too radical - use a dict with string keys instead of separate attributes? Commented Dec 16, 2011 at 3:02
  • Yes, the descriptor information could be added to a list somewhere (not on the class itself as static data but somewhere). Commented Dec 16, 2011 at 3:18

5 Answers 5

13

You should use python's inspect module for any such introspective capabilities.

.
.
>>> class ClassC(ClassB):
...     def baz(self):
...         return "hiya"
...
>>> import inspect
>>> for attr in inspect.getmembers(ClassC):
...   print attr
... 
('__doc__', None)
('__module__', '__main__')
('bar', <property object at 0x10046bf70>)
('baz', <unbound method ClassC.baz>)
('foo', <property object at 0x10046bf18>)

Read more about the inspect module here.

2
  • Sweet, it's like a better version of dict because whereas dict just operates on the class, inspect.getmembers appears to operate on ancestors as well. I'll post some additional code samples on my original approach using this technique. Commented Dec 16, 2011 at 4:06
  • This doesn't appear to be exactly the same as accessing __dict__ of the classes due to the descriptor protocol. __dict__ will give you the descriptor object whereas inspect.getmembers() returns the results of a descriptor's __get__ method
    – D-Rock
    Commented Apr 6, 2021 at 20:44
6

You want to use dir:

for attr in dir(ClassB):
    print attr
4
  • Interesting. I assumed dir() just gave back the keys from dict but you're right, one key difference is that dir() operates on the class and all its ancestors whereas dict seems to operate on just the class (not its ancestors). I do need access to the attributes themselves (not just the names) but it looks like I can use a combination of dir() and getattr(). Thanks! Commented Dec 16, 2011 at 2:51
  • 1
    Yep dir() and gettattr() is the normal way to go about that. Commented Dec 16, 2011 at 2:51
  • Sidenote: The inspect module is what I believe the dir() command is built upon. I think it uses mro and getmembers() to find "all reachable attributes" of the object.
    – jdi
    Commented Dec 16, 2011 at 2:54
  • 1
    No, the inspect module is built on dir. If you inspect inspect.py, getmembers() uses dir (which is implemented in C). Commented Dec 16, 2011 at 2:57
4

Sadly there isn't a single composite object. Every attribute access for a (normal) python object first checks obj.__dict__, then the attributes of all it's base classes; while there are some internal caches and optimizations, there isn't a single object you can access.

That said, one thing that could improve your code is to use cls.__mro__ instead of cls.__bases__... instead of the class's immediate parents, cls.__mro__ contains ALL the ancestors of the class, in the exact order Python would search, with all common ancestors occuring only once. That would also allow your type-searching method to be non-recursive. Loosely...

def get_attrs(obj):
    attrs = set(obj.__dict__)
    for cls in obj.__class__.__mro__:
        attrs.update(cls.__dict__)
    return sorted(attrs)

... does a fair approximation of the default dir(obj) implementation.

5
  • Oh, that's great to know! Sounds like I have two choices now. zeekay suggested using dir() (which included attribute names of ancestor classes) though I'd then need to getattr() on each name. Or I could use your method. My example made it appear that I only need the attribute names but in reality I do need references to the attributes themselves. So I think I'll use your code. Thanks! Commented Dec 16, 2011 at 2:53
  • Also, thanks for the tip on mro. I think I'd seen someone use it but I didn't realize what it was. That's very handy! Commented Dec 16, 2011 at 2:54
  • So this gives back just attribute names, right? I'm working right now on modifying it to give back both attribute names and their references. If you already have something in mind to do that, could you post it? Commented Dec 16, 2011 at 3:04
  • Yikes, how do I post code within comments? Anyway, I think this works: def get_attributes(type): attributes = set(type.__dict__.items()) for type in type.__mro__: attributes.update(type.__dict__.items()) return attributes Commented Dec 16, 2011 at 3:06
  • @MatthewLund: To quote code within comments, surround the code with backticks. Since the comments are not kind to whitespace, you might also consider using a pastebin or ideone.come
    – unutbu
    Commented Dec 16, 2011 at 4:07
1

Here is a function I wrote, back in the day. The best answer is using the inspect module, as using __dict__ gives us ALL functions (ours + inherited) and (ALL?) data members AND properties. Where inspect gives us enough information to weed out what we don't want.

def _inspect(a, skipFunctionsAlways=True, skipMagic = True):
    """inspects object attributes, removing all the standard methods from 'object',
    and (optionally) __magic__ cruft.

    By default this routine skips __magic__ functions, but if you want these on
    pass False in as the skipMagic parameter.

    By default this routine skips functions, but if you want to see all the functions,
    pass in False to the skipFunctionsAlways function. This works together with the
    skipMagic parameter: if the latter is True, you won't see __magic__ methods.
    If skipFunctionsAlways = False and skipMagic = False, you'll see all the __magic__
    methods declared for the object - including __magic__ functions declared by Object

    NOT meant to be a comprehensive list of every object attribute - instead, a
    list of every object attribute WE (not Python) defined. For a complete list
    of everything call inspect.getmembers directly"""

    objType = type(object)
    def weWantIt(obj):
        #return type(a) != objType
        output= True
        if (skipFunctionsAlways):
            output = not ( inspect.isbuiltin(obj) ) #not a built in 

        asStr = ""
        if isinstance(obj, types.MethodType):
            if skipFunctionsAlways:  #never mind, we don't want it, get out.
                return False
            else:
                asStr = obj.__name__
                #get just the name of the function, we don't want the whole name, because we don't want to take something like:
                #bound method LotsOfThings.bob of <__main__.LotsOfThings object at 0x103dc70>
                #to be a special method because it's module name is special
                #WD-rpw 02-23-2008

                #TODO: it would be great to be able to separate out superclass methods
                #maybe by getting the class out of the method then seeing if that attribute is in that class?
        else:
            asStr = str(obj)

        if (skipMagic):
            output = (asStr.find("__") == -1 ) #not a __something__

        return (output)

    for value in inspect.getmembers( a, weWantIt ):
        yield value
0
0
{k: getattr(ClassB, k) for k in dir(ClassB)}

Proper values (instead of <property object...>) will be presented when using ClassB instance.

And of course You can filter this by adding things like if not k.startswith('__') in the end.

Not the answer you're looking for? Browse other questions tagged or ask your own question.