Inspect python class attributes Inspect python class attributes python python

Inspect python class attributes


Below is the hard way. Here's the easy way. Don't know why it didn't occur to me sooner.

import inspectdef get_user_attributes(cls):    boring = dir(type('dummy', (object,), {}))    return [item            for item in inspect.getmembers(cls)            if item[0] not in boring]

Here's a start

def get_user_attributes(cls):    boring = dir(type('dummy', (object,), {}))    attrs = {}    bases = reversed(inspect.getmro(cls))       for base in bases:        if hasattr(base, '__dict__'):            attrs.update(base.__dict__)        elif hasattr(base, '__slots__'):            if hasattr(base, base.__slots__[0]):                 # We're dealing with a non-string sequence or one char string                for item in base.__slots__:                    attrs[item] = getattr(base, item)            else:                 # We're dealing with a single identifier as a string                attrs[base.__slots__] = getattr(base, base.__slots__)    for key in boring:        del attrs['key']  # we can be sure it will be present so no need to guard this    return attrs

This should be fairly robust. Essentially, it works by getting the attributes that are on a default subclass of object to ignore. It then gets the mro of the class that's passed to it and traverses it in reverse order so that subclass keys can overwrite superclass keys. It returns a dictionary of key-value pairs. If you want a list of key, value tuples like in inspect.getmembers then just return either attrs.items() or list(attrs.items()) in Python 3.

If you don't actually want to traverse the mro and just want attributes defined directly on the subclass then it's easier:

def get_user_attributes(cls):    boring = dir(type('dummy', (object,), {}))    if hasattr(cls, '__dict__'):        attrs = cls.__dict__.copy()    elif hasattr(cls, '__slots__'):        if hasattr(base, base.__slots__[0]):             # We're dealing with a non-string sequence or one char string            for item in base.__slots__:                attrs[item] = getattr(base, item)            else:                 # We're dealing with a single identifier as a string                attrs[base.__slots__] = getattr(base, base.__slots__)    for key in boring:        del attrs['key']  # we can be sure it will be present so no need to guard this    return attrs


Double underscores on both ends of 'special attributes' have been a part of python before 2.0. It would be very unlikely that they would change that any time in the near future.

class Foo(object):  a = 1  b = 2def get_attrs(klass):  return [k for k in klass.__dict__.keys()            if not k.startswith('__')            and not k.endswith('__')]print get_attrs(Foo)

['a', 'b']


Thanks aaronasterling, you gave me the expression i needed :-)My final class attribute inspector function looks like this:

def get_user_attributes(cls,exclude_methods=True):  base_attrs = dir(type('dummy', (object,), {}))  this_cls_attrs = dir(cls)  res = []  for attr in this_cls_attrs:    if base_attrs.count(attr) or (callable(getattr(cls,attr)) and exclude_methods):      continue    res += [attr]  return res

Either return class attribute variabels only (exclude_methods=True) or also retrieve the methods.My initial tests og the above function supports both old and new-style python classes.

/ Jakob