Why does 'return self' return None?

2020-04-20 05:48发布

问题:

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

回答1:

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.



回答2:

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>


回答3:

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.



回答4:

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.