3

Please do not ask how I get myself into this situation. Lets say I have a class called ccollection.

this class has the following attributes at runtime:

ccollection.a.b.x = 1
ccollection.a.b.y = 3
ccollection.a.b.z = 4
...
ccollection.a.c = 3
ccollection.b = 3

this class will be setup dynamically as described above. so there is no way to know the attributes in the class before hand.

Now I would like to print all the attributes in this class, for example:

ccollection.a.b should print

ccollection.a.b.x = 1
ccollection.a.b.y = 3
ccollection.a.b.z = 4

and ccollection.a should print

ccollection.a.b.x = 1
ccollection.a.b.y = 3
ccollection.a.b.z = 4
ccollection.a.c = 3

I think you get the idea. Each print should starts printing all the elements at the same level and below. I am looking for a way to recursively traverse all the attributes (which is a tree-like data structure)

theAlse
  • 5,577
  • 11
  • 68
  • 110
  • What is the type of `collection.a`? I ask because `collection.a.b` is not an attribute of your `collection` object, but an attribute of `collection.a`. You may need to make several classes with custom `__str__` or `__repr__` methods for this to work the way you want. – Blckknght Nov 15 '12 at 08:23
  • @Blckknght, in ccollection.a.b.x = 1, a itself is a ccollection, b is also a ccollection. x is set to 1 – theAlse Nov 15 '12 at 08:32
  • Ok. How are you creating the attributes. Are you just directly assigning them (e.g. `ccollection.a = ccollection()`)? Or is there some specific way for a given instance to know what attributes it should have? If not, you can use `dir` but it will be a pain to weed out the various methods and other cruft that will be included in that list. – Blckknght Nov 15 '12 at 08:35

2 Answers2

1

This situation really calls for refactoring. You are using an object that is not designed as a container. Instead, use a container such as a dict or a class that inherits from dict.


If you must use the current setup, I agree with Blckknght that the most promising approach appears to use dir.

class CCollection(object):
  def get_children_strings(self):
    list_of_strings = []
    for attr_name in dir(self):
      if attr_name not in dir(CCollection()):
        attr = getattr(self, attr_name)
        if hasattr(attr, 'get_children_strings'):
          list_of_strings.extend(["." + attr_name + child_string for child_string in attr.get_children_strings()])
        else:
          list_of_strings.append("." + attr_name + " = " + str(attr))
    return list_of_strings

  def print_tree(self, prefix):
    print [prefix + s for s in self.get_children_strings()]

Then you can

m = CCollection()
m.a = CCollection()
m.a.b = CCollection()
m.a.b.x = 1
m.a.b.y = 2
m.a.c = 3
m.d = 4

m.print_tree("m")
m.a.print_tree("m.a")
m.a.b.print_tree("m.a.b")

and get the outputs:

>>> m.print_tree("m")
['m.a.b.x = 1', 'm.a.b.y = 2', 'm.a.c = 3', 'm.d = 4']
>>> m.a.print_tree("m.a")
['m.a.b.x = 1', 'm.a.b.y = 2', 'm.a.c = 3']
>>> m.a.b.print_tree("m.a.b")
['m.a.b.x = 1', 'm.a.b.y = 2']

To take this further, you probably would want to use a class with tree-traversal functions. You could automatically generate the info currently being passed in via the prefix argument to the print_tree function, if you had a function to get the parent node, a guarantee of no loops, and a class variable holding the node's name.

Community
  • 1
  • 1
beaslera
  • 863
  • 9
  • 19
0

It looks like you want a tree like structure with attribute access. This can be done by subclassing dict and then setting the appropriate __getattr__ and __setattr__ to get the access api that you desire and at the same time the printing that you wish.

Also overriding the __str__ can be used to make it print exactly how you wish to.

EDIT:

To quickly describe this I would have it look like this.

class DictTree( object ):
    _children = {}

    def __getattr__( self, name ):
        if not name in self._children:
            self._children[name] = DictTree()
        return self._children[name]

    def __setattr__( self, name, v ):
        self._children[name] = v

The above works provides the access and API interface you desire, but when printing it I get a RuntimeError: maximum recursion depth exceeded because of how the __getattr__ is working. If you tweak the above code to not have this issue then it should get you what you want. The fix involves the __str__ method.

sean
  • 3,955
  • 21
  • 28
  • I am sure it can be done that way (I don´t know how though). I was looking for a way to recursively traverse this tree-like structure. I can successfully traverse down the tree but I always lose the upper levels overriding __str__ – theAlse Nov 15 '12 at 07:35
  • You should be printing out the values at the current level before recursing into the tree from what I can see. – sean Nov 15 '12 at 15:01
  • It seems unlikely that you want to instantiate a dictionary as the `_children` class variable outside of a class function, since all DictTree objects will use the same (single) dictionary. See, e.g., [this SO answer](http://stackoverflow.com/a/6021050/783037). Instead, it seems you would want to create a new dictionary for each instance, e.g., in `__init__`. Unfortunately, that is difficult due to the changed `__setattr__`. – beaslera Jul 10 '13 at 17:47
  • Recursion limit errors should be addressed by calling the superclass's methods, as in [this SO answer](http://stackoverflow.com/a/371833/783037). – beaslera Jul 10 '13 at 18:46