Skip to content Skip to sidebar Skip to footer

Is It Possible To Display An Objects Instance Variables In IPython Like Matlab Does?

I'm trying to move away from Matlab to Python. While the magic ? in IPython is nice, one very nice feature of Matlab is that you can see on the command line (by omitting the ;) th

Solution 1:

The short answer is that there is no way to get a list of all attributes of an object in Python, because the attributes could be generated dynamically. For an extreme example, consider this class:

>>> class Spam(object):
...     def __getattr__(self, attr):
...         if attr.startswith('x'):
...             return attr[1:]
>>> spam = Spam()
>>> spam.xeggs
'eggs'

Even if the interpreter could someone figure out a list of all attributes, that list would be infinite.

For simple classes, spam.__dict__ is often good enough. It doesn't handle dynamic attributes, __slots__-based attributes, class attributes, C extension classes, attributes inherited from most of the above, and all kinds of other things. But it's at least something—and sometimes, it's the something you want. To a first approximation, it's exactly the stuff you explicitly assigned in __init__ or later, and nothing else.

For a best effort aimed at "everything" aimed at human readability, use dir(spam).

For a best effort aimed at "everything" for programmatic use, use inspect.getmembers(spam). (Although in fact the implementation is just a wrapper around dir in CPython 2.x, it could do more—and in fact does in CPython 3.2+.)

These will both handle a wide range of things that __dict__ cannot, and may skip things that are in the __dict__ but that you don't want to see. But they're still inherently incomplete.

Whatever you use, to get the values as well as the keys is easy. If you're using __dict__ or getmembers, it's trivial; the __dict__ is, normally, either a dict, or something that acts close enough to a dict for your purposes, and getmembers explicitly returns key-value pairs. If you're using dir, you can get a dict very easily:

{key: getattr(spam, key) for key in dir(spam)}

One last thing: "object" is a bit of an ambiguous term. It can mean "any instance of a class derived from object", "any instance of a class", "any instance of a new-style class", or "any value of any type at all" (modules, classes, functions, etc.). You can use dir and getmembers on just about anything; the exact details of what that means are described in the docs.

One even-last-er thing: You may notice that getmembers returns things like ('__str__', <method-wrapper '__str__' of Spam object at 0x1066be790>), which you probably aren't interested in. Since the results are just name-value pairs, if you just want to remove __dunder__ methods, _private variables, etc., that's easy. But often, you want to filter on the "kind of member". The getmembers function takes a filter parameter, but the docs don't do a great job explaining how to use it (and, on top of that, expect that you understand how descriptors work). Basically, if you want a filter, it's usually callable, lambda x: not callable(x), or a lambda made up of a combination of inspect.isfoo functions.

So, this is common enough you may want to write it up as a function:

def get_public_variables(obj):
    return [(name, value) for name, value 
            in inspect.getmembers(obj, lambda x: not callable(x))
            if not name.startswith('_')]

You can turn that into a custom IPython %magic function, or just make a %macro out of it, or just leave it as a regular function and call it explicitly.


In a comment, you asked whether you can just package this up into a __repr__ function instead of trying to create a %magic function or whatever.

If you've already got all of your classes inheriting from a single root class, this is a great idea. You can write a single __repr__ that works for all of your classes (or, if it works for 99% of them, you can override that __repr__ in the other 1%), and then every time you evaluate any of your objects in the interpreter or print them out, you'll get what you want.

However, a few things to keep in mind:

Python has both __str__ (what you get if you print an object) and __repr__ (what you get if you just evaluate an object at the interactive prompt) for a reason. Usually, the former is a nice human-readable representation, while the latter is something that's either eval-able (or typable-into-the-interactive-prompt), or the concise angle-bracket form that gives you just enough to distinguish the type and identity of an object.

That's just a convention rather than a rule, so you can feel free to break it. However, if you are going to break it, you may still want to make use of the str/repr distinction—e.g., make repr give you a complete dump of all the internals, while str shows just the useful public values.

More seriously, you have to consider how repr values are composed. For example, if you print or repr a list, you get, effectively, '[' + ', '.join(map(repr, item))) + ']'. This is going to look pretty odd with a multi-line repr. And it'll be even worse if you use any kind of pretty-printer that tries to indent nested collections, like the one that's built into IPython. The result probably won't be unreadable, it'll just defeat the benefits that the pretty-printer is meant to provide.

As for the specific stuff you want to display: That's all pretty easy. Something like this:

def __repr__(self):
    lines = []

    classes = inspect.getmro(type(self))
    lines.append(' '.join(repr(cls) for cls in classes))

    lines.append('')
    lines.append('Attributes:')
    attributes = inspect.getmembers(self, callable)
    longest = max(len(name) for name, value in attributes)
    fmt = '{:>%s}: {}' % (longest, )
    for name, value in attributes:
        if not name.startswith('__'):
            lines.append(fmt.format(name, value))

    lines.append('')
    lines.append('Methods:')
    methods = inspect.getmembers(self, negate(callable))
    for name, value in methods:
        if not name.startswith('__'):
            lines.append(name)

    return '\n'.join(lines)

Right-justifying the attribute names is the hardest part here. (And I probably got it wrong, since this is untested code…) Everything else is either easy, or fun (playing with different filters to getmembers to see what they do).


Solution 2:

I was able to achieve what I wanted with IPython (at least roughly) by implementing _repr_pretty_:

def get_public_variables(obj):
    from inspect import getmembers
    return [(name, value) for name, value in
            getmembers(obj, lambda x: not callable(x)) if
            not name.startswith('__')]


class MySuperClass(object):
    def _repr_pretty_(self, p, cycle):
        for (name, value) in get_public_variables(self):
            f = '{:>12}{} {:<} \n'
            line = f.format(str(name), ':', str(value))
            # p.text(str(name) + ': ' + str(value) + '\n')
            p.text(line)

class MyClass(MySuperClass):
    _x = 5

    @property
    def x(self):
        return self._x + 100

gives me out of:

mc = MyClass()
mc
Out[15]: 
          _x: 5 
           x: 105 

clearly there is some fine tuning to be done in terms of whitespace, etc. But this was roughly what I was trying to accomplish


Solution 3:

You can get to an object's instance variable using obj.__dict__, e.g.:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age


d1 = Dog("Fido", 7)

for key, val in d1.__dict__.items():
    print(key, ": ", val)

Output:

age :  7
name :  Fido

You might find this doesn't work well with real objects that might have a large number of instance variables and methods though.


Post a Comment for "Is It Possible To Display An Objects Instance Variables In IPython Like Matlab Does?"