简体   繁体   中英

AttributeError for recursive method in python

I have a recursive method problem with python, the code is this:

class NodeTree(object):
    def __init__(self, name, children):
        self.name = name
        self.children = children

    def count(self):
        # 1 + i children's nodes
        count = 1
        for c in self.children:
            count += c.count()
        return count


def create_tree(d):
    N = NodeTree(d['name'], d['children'])
    print N.count()

d1 = {'name':'musica', 'children':[{'name':'rock', 'children':[{'name':'origini','children':[]},
                                                               {'name':'rock&roll','children':[]},
                                                               {'name':'hard rock', 'children':[]}]},
                                   {'name':'jazz', 'children':[{'name':'origini', 'children':[{'name':'1900', 'children':[]}]},
                                                               {'name':'ragtime', 'children':[]}, {'name':'swing', 'children':[]}]}]}
tree = create_tree(d1)

The error is this:

count += c.count()
AttributeError: 'dict' object has no attribute 'count'

I tried anything but it doesn't work.

Anyway, any suggestions? Thanks!

That's because Python dictionaries do not have a count method.

It'll help if we go over line by line what your code is actually doing.

def count(self):
        # 1 + i children's nodes
        count = 1
        for c in self.children:      ## self.children is a list of dictionaries, so each c is a dictionary
            count += c.count()       ## We are getting .count() of c--which is a dictionary
        return count

This is because we passed d1['children'] as self.children, which is a list of dictionaries: [<dict>, <dict>, <dict>, ... ] .

Rather than count() , what you should do is call len on the dictionary, to get the number of keys it has, thus becoming:

    for c in self.children:
        count += len(c)

d['children'] is a list of dict , as you can see in d1 dict.

Now, when you iterate over your children , in NodeTree , which is essentially d['children'] only, you will get dictionary as each element: -

for c in self.children:  // c is a `dict` type here
    count += c.count()   // there is not attribute as `count` for a `dict`.

And hence you got that error.

Well, for once, the create_tree function does not build the tree recursively. So you just add a Node on zero level and the the children are just dictionaries.

The following (modified) code (although quickly typed and sloppy) should do a recursive build of the tree. Didn't check your count code, but assuming it is correct, it should work.

class NodeTree(object):
    def __init__(self, name, children):
        self.name = name
        self.children = children

    def count(self):
        # 1 + i children's nodes
        count = 1
        for c in self.children:
            count += c.count()
        return count


def deep_create_tree(d):
    if len(d["children"]) > 0:
        chlds = []
        for i in d["children"]:
            chlds.append(deep_create_tree(i))
    else:
        chlds = []
    n = NodeTree(d["name"], chlds)
    return n


d1 = {'name':'musica', 'children':[{'name':'rock', 'children':[{'name':'origini','children':[]},{'name':'rock&roll','children':[]},{'name':'hard rock', 'children':[]}]},{'name':'jazz', 'children':[{'name':'origini', 'children':[{'name':'1900', 'children':[]}]},{'name':'ragtime', 'children':[]}, {'name':'swing', 'children':[]}]}]}

def scan_tree(tr):
    print tr.name, tr.count()
    for i in tr.children:
        scan_tree(i)

tr = deep_create_tree(d1)
scan_tree(tr)

The best (and the only desirable) way is to have a recursive creation of your node tree. This can be done in two different ways, either make your NodeTree.__init__() recursively init all of the children (which again recursively init all of their children, etc) or you can make your create_tree() function recursive.

I'd personally use recursive __init__() , but it's your call.


Recursive __init__() for creating tree structure:

def __init__(self, name, children=[]):
    self.name = name
    self.children = []
    for c in children:
        self.children.append(
            self.__class__(c['name'], c['children']))

This way self.children will contain other NodeTree s instead of dict s. Also you no longer need to declare empty children list, instead of:

d2 = {'name':'sterile', 'children':[]}

do

d2 = {'name':'sterile'}

And the initializer will automatically set children to []


If you want to use a recursive create_tree() function, it's also possible, and not a bad idea either. However you will still have to edit the __init__() -method, it no longer takes children as parameter. Or as I did here, it does, but you hardly ever use it.

# NodeTree
def __init__(self, name, children=None):
    self.name = name
    if children:
        self.children = children

def create_tree(d):
    N = NodeTree(d['name'])
    for c in d['children']:
        N.children.append(create_tree(c))
    return N

This will have basically the same results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM