saintlyzero
saintlyzero

Reputation: 1852

Python Recursion inside Inherited Class method

I have a class and a method:

class Demo(dict):

  def foo(self, key):
     bar = self[key]
     foo(bar, key)

Class Demo accepts a dict as an argument
From app.py I'm calling Demo as:

data = { 'a': { 'a' : 123 } }

Demo(data).foo('a')

My question is, how do I pass the newly formed dictionary bar = self[key] as an argument to foo() while recursing?

Should I use this instead ?:

class Demo():

  def foo(self, data, key):
     bar = data[key]
     foo(bar, key)

Is there any other better approach?

Upvotes: 1

Views: 715

Answers (4)

blhsing
blhsing

Reputation: 106512

You should instantiate a new instance of the same class with the sub-dict to recursively traverse down the dict with the same key:

class Demo(dict):
    def foo(self, key):
        if isinstance(self[key], dict):
            return self.__class__(self[key]).foo(key)
        return self[key]

so that:

data = {'a': {'a': 123}}
print(Demo(data).foo('a'))

outputs:

123

Demo: https://repl.it/@blhsing/PassionateOddAssembly

Upvotes: 1

Blckknght
Blckknght

Reputation: 104712

Your foo method needs to be called on an instance of Demo, not by itself. In both versions of your code, calling foo without looking it up in something (probably an instance) is an error.

Perhaps you want:

def foo(self, key):
    obj = Demo(self[key])
    obj.foo(key)

This will work for as long as your data has nested dictionaries under the same key. It will fail when self[key] doesn't return a dictionary. Presumably you want to have a base case to handle that:

def foo(self, key):
    value = self[key]  # you might want a further base case to handle the key not existing at all!
    if not isinstance(value, dict):
        pass           # if you have something to do for the base case, do it here
    else:              # recursive case
        obj = Demo(value)
        obj.foo(key)

Now, this class is a bit silly, it copies lots of stuff just so that you can have a method that runs on a dict. A much more sensable approach would get rid of the class and just use a recursive function with two arguments, and you'd have no need for creating obj in the recursive case:

# this foo is a top-level function, not part of a class!
def foo(data, key):  # no self argument any more
    value = data[key]
    ...
    foo(value, key)  # recursive call is simpler now

Upvotes: 1

Carcigenicate
Carcigenicate

Reputation: 45745

You could use an inner recursive function instead:

def foo(self, key):
    def inner(new_d):
        bar = new_d[key]
        inner(bar)

    inner(self)  # Or whatever you want to be the initial value

Note how it formed a closure over key so that doesn't need to be passed constantly. It can just be accessed from the outer scope.

Of course this function doesn't really make much sense though since it will recurse forever, so I'm assuming this is a stripped down example.

Upvotes: 3

RightmireM
RightmireM

Reputation: 2492

It IS possible to do what you want, although I don't know if it's the best solution. It really depends on what you're trying to accomplish.

The following code, recurses the way you want. The advantage is (if you want it) that the self.data parameter in the root class (self) changes throughout the recusion. This is good is you WANT that data to change (for example for later access to the changed data by another method later.)

class Demo(dict):
    def __init__(self, data):
        self.data = data

    def foo(self, key):
        try:
            self.data = self.data[key]
            return self.foo(key)
        except:
            return self.data

data = { 'a': { 'a' : 123 } }

print(Demo(data).foo('a'))

If you DON'T want the self.data to be permanently changed, you should use the second method, were data and key are passed into the function together, and do not affect the self.data... or the other answer (about inner recursive function by @Carcigenicate)

Upvotes: 2

Related Questions