I'm trying to get the top node of a chain in getTopParent()
. When I print out self.name
, it indeed prints out the name of the parent instance; however, when I return self
, it returns None. Why is this?
class A:
def __init__( self, name ):
self.name = name
self.owner = None
def setChild( self, l ):
l.owner = self
def getTopParent( self ):
if( self.owner == None ): # None == top parent
print( "Got top: %s" % self.name )
return self
else:
print( "checking %s" % self.name )
self.owner.getTopParent()
a = A( "parent" )
b = A( "child1" )
c = A( "child2" )
d = A( "child3" )
a.setChild( b )
b.setChild( c )
c.setChild( d )
print( d.getTopParent() )
>>> checking child3
checking child2
checking child1
Got top: parent
None
You need to return self.owner.getTopParent()
!
In Python, you must actually use the return
statement to return something from a function, otherwise None
is returned.
use return
in
else:
print( "checking %s" % self.name )
return self.owner.getTopParent()
as None
is the default return Value, then it'll output:
checking child3
checking child2
checking child1
Got top: parent
<__main__.A instance at 0xb77a628c>
I made some slight modifications to your code:
class A:
def __init__( self, name ):
self.name = name
self.owner = None
def setChild( self, l ):
l.owner = self
def getTopParent( self , depth=0):
if( self.owner == None ): # None == top parent
print( ' ' * depth + "Got top: %s" % self.name )
print( ' ' * depth + "Returning: %s" % self)
return self
else:
print( ' ' * depth + "checking %s" % self.name )
tmp = self.owner.getTopParent(depth=depth + 1)
print( ' ' * depth + "returned from call while checking %s" % self.name)
print( ' ' * depth + "returned value was: %s" % tmp)
a = A( "parent" )
b = A( "child1" )
c = A( "child2" )
d = A( "child3" )
a.setChild( b )
b.setChild( c )
c.setChild( d )
d_top_parent = d.getTopParent()
print('----------------')
print d_top_parent
Now you can see what's going on when you call d.getTopParent()
. Here's the output:
checking child3
checking child2
checking child1
Got top: parent
Returning: <__main__.A instance at 0x0000000002DE8DC8>
returned from call while checking child1
returned value was: <__main__.A instance at 0x0000000002DE8DC8>
returned from call while checking child2
returned value was: None
returned from call while checking child3
returned value was: None
----------------
None
In the middle there you can see it found the top parent and returned it. But return
returns to the place where this function was called from[1]. So that only returns to the "checking child1" invocation.
Back in the "checking child1" invocation, it's called self.owner.getTopParent()
, which has returned the top parent. In your original version, there was no more code after that, so the execution "dropped off the end" of the function. In my version, it stores the value that was returned from self.owner.getTopParent()
into a variable tmp
and prints it, so we can see what it was. But then it also drops off the end of the function.
In Python, hitting the end of a function is equivalent to return None
, so that's what gets returned to the caller of "checking child1". So the "checking child2" invocation gets the value None
returned from self.owner.getTopParent()
. And then drops off the end of the function, returning None
to its caller.[2] And so on.
If you add return tmp
to my version, after the printing in the else
branch, you get this output:
checking child3
checking child2
checking child1
Got top: parent
Returning: <__main__.A instance at 0x0000000002E68DC8>
returned from call while checking child1
returned value was: <__main__.A instance at 0x0000000002E68DC8>
returned from call while checking child2
returned value was: <__main__.A instance at 0x0000000002E68DC8>
returned from call while checking child3
returned value was: <__main__.A instance at 0x0000000002E68DC8>
----------------
<__main__.A instance at 0x0000000002E68DC8
Now each "checking childN" call is receives a value from calling self.owner.getTopParent()
and returns it to the next-outermost caller.
If you call something, and you want its return value to go anywhere, you have to tell it where to go. That can be storing it somewhere with an assignment statement, or passing it directly as an argument to another call, or returning it directly. If you just have a bare call like this line:
self.owner.getTopParent()
Then the returned value goes nowhere and can't be used for any purpose[3].
The fact that the function you're calling happens to have the same name as the function you're in is irrelevant; recursive calls work exactly the same as non-recursive calls in every single way. I'm always surprised by how confusing recursion is for many people, but this is probably just because I don't remember what it was like to be learning it anymore. :)
[1] If you think about it, return
has to work this way. How could you write a function that calls other functions if when they returned the return value jumped all the way to the very outer-most call (and appeared as output, if you're running in the interactive interpreter)?
[2] This None
has nothing to do with receiving None
from the call to self.owner.getTopParent()
.
[3] Unless you typed that directly in the interactive interpreter, in which case Python prints the return value for you so you can see what it was. It also secretly saves it in the variable _
, so you can get at what it was if you decide you did need it after all after seeing it. But in principle, and in any other context, if you don't do anything with the return value of something you call then you lose the value.
Returning self
worked just fine in the True
side of the if
. However, the recursive call in the else
side does not return anything. Use return self.owner.getTopParent()
instead.