Laharl
Laharl

Reputation: 129

Python: Printing nested classes

What's the best way to print the contents of a nested class? For example:

class Library():
    def __init__(self):
        self.shelves = []
        self.shelf = self.Shelf()

    class Shelf():
        def __init__(self):
            self.books = []


x = Library()

I know vars(x) works if the class is not nested. But if it is, I get something like:

{'shelf': <__main__.Shelf instance at 0x7f4bae723560>, 'shelves': []}

as the output. How do I get python to print it out as:

{'shelf': {'books': []}, 'shelves': []}

Upvotes: 4

Views: 4842

Answers (2)

Alfe
Alfe

Reputation: 59526

You are aiming to have a string version of your objects. Python knows two methods for this: __repr__() and __str__(). The first is meant to be a string which, if interpreted as Python source, would re-generate the thing it represents. If you just call vars(x) you receive a dict of all fields in the given x. Most means to output this dict (and probably also yours) make it call the __repr__() function of each of its contents. That's why you see that ugly string <__main__.Shelf instance at 0x7f4bae723560> because that's the __repr__() of your Shelf object.

I propose to implement it and thus override the standard implementation. You will have to do that in Shelf:

class Library():
    def __init__(self):
        self.shelves = []
        self.shelf = self.Shelf()

    class Shelf():
        def __init__(self):
            self.books = []
        def __repr__(self):
            return repr(vars(self))

x = Library()

And while you're at it, you could do the same in your Library as well; then typing x alone (i. e. instead of vars(x)) would also give the nice output:

class Library():
    def __init__(self):
        self.shelves = []
        self.shelf = self.Shelf()
    def __repr__(self):
        return repr(vars(self))

    class Shelf():
        def __init__(self):
            self.books = []
        def __repr__(self):
            return repr(vars(self))

x = Library()

And of course you can refactor that out and put that aspect into a base class:

class Representer(object):
    def __repr__(self):
        return repr(vars(self))

class Library(Representer):
    def __init__(self):
        self.shelves = []
        self.shelf = self.Shelf()

    class Shelf(Representer):
        def __init__(self):
            self.books = []

x = Library()

Upvotes: 6

TigerhawkT3
TigerhawkT3

Reputation: 49330

First, I would recommend that you not nest classes. There isn't really any reason to do so.

Second, you're seeing exactly what you're supposed to: the value for the shelf variable is a Shelf object, and you haven't defined any special appearance (or "reproduction") for it. Since vars is not recursive, you see the default appearance with a memory location.

If you want to see the contents of a vars() call on a Shelf object when printing the Shelf object, you can define a __repr__ method for Shelf that calls vars() on itself:

class Library():
    def __init__(self):
        self.shelves = []
        self.shelf = self.Shelf()
    class Shelf():
        def __init__(self):
            self.books = []
        def __repr__(self):
            return str(vars(self))

 

>>> x = Library()
>>> vars(x)
{'shelf': {'books': []}, 'shelves': []}

Upvotes: 2

Related Questions