Why does 'return self' return None?

前端 未结 4 1907
我寻月下人不归
我寻月下人不归 2021-01-25 06:49

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

4条回答
  •  北恋
    北恋 (楼主)
    2021-01-25 07:41

    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.

提交回复
热议问题