简体   繁体   English

递归函数中的Python命名空间

[英]Python Namespaces in Recursive Functions

My question is relatively simple compared to how I got there. 与我到达那里相比,我的问题相对简单。 Do recursive functions in Python create a new namespace each time the function calls itself? Python中的递归函数每次函数调用自身时都会创建一个新的命名空间吗?

I was doing some reading on mergesort and came across this tutorial: https://interactivepython.org/runestone/static/pythonds/SortSearch/TheMergeSort.html#lst-merge 我正在阅读mergesort并阅读本教程: https ://interactivepython.org/runestone/static/pythonds/SortSearch/TheMergeSort.html#lst-merge

def mergeSort(alist):
    print("Splitting ",alist)
    if len(alist)>1:
        mid = len(alist)//2
        lefthalf = alist[:mid]
        righthalf = alist[mid:]

        mergeSort(lefthalf)
        mergeSort(righthalf)

        i=0
        j=0
        k=0
        while i < len(lefthalf) and j < len(righthalf):
            if lefthalf[i] < righthalf[j]:
                alist[k]=lefthalf[i]
                i=i+1
            else:
                alist[k]=righthalf[j]
                j=j+1
            k=k+1

        while i < len(lefthalf):
            alist[k]=lefthalf[i]
            i=i+1
            k=k+1

        while j < len(righthalf):
            alist[k]=righthalf[j]
            j=j+1
            k=k+1
    print("Merging ",alist)

alist = [54,26,93,17,77,31,44,55,20]
mergeSort(alist)
print(alist)

I'm understanding the divide-and-conquer well enough, but what I can't get past at the moment is the question I asked above. 我已经足够了解分而治之,但我现在无法理解的是我上面提到的问题。 I can follow the code, but I'm not quite understanding the use of lefthalf as the argument that gets passed into the recursive function call of mergeSort . 我可以遵循代码,但我不太了解使用lefthalf作为传递给mergeSort的递归函数调用的参数。

I get that when mergeSort is first called way down at the bottom, alist gets chopped into [54, 26, 93, 17] and [17, 77, 31, 44, 55, 20]. 我得到的是当mergeSort首先在底部调用时,alist被切入[54,26,93,17]和[17,27,31,44,55,20]。 These are lefthalf and righthalf. 这些是lefthalf和righthalf。 Then mergeSort gets called on lefthalf. 然后在lefthalf上调用mergeSort。 This is where I get confused. 这是我感到困惑的地方。 Does the recursive call to mergeSort create a whole new namespace, and is that why the lefthalf that gets passed in doesn't collide with the lefthalf defined within the function? 对mergeSort的递归调用是否会创建一个全新的命名空间,这就是为什么传入的lefthalf不会与函数中定义的lefthalf冲突的原因?

I know the answer to this is really simple and fundamental, so your patience is much appreciated. 我知道答案是非常简单和基本的,所以你的耐心非常感谢。 Thanks in advance! 提前致谢!

Yes, a function call (any function call, not just recursive ones) creates a new namespace. 是的,函数调用(任何函数调用,而不仅仅是递归调用)都会创建一个新的命名空间。 BUT, when given as parameters, objects are passed by reference (in your example, the object is a list). 但是,当作为参数给出时,对象通过引用传递(在您的示例中,对象是列表)。

So, the new namespace get its own copy of this reference but it still refers to the same object as in the calling function, and if you change the content of that object, you will notice the change in the calling function. 因此,新命名空间获得它自己的引用副本,但它仍然引用与调用函数中相同的对象,如果更改该对象的内容,您将注意到调用函数的更改。

Not sure I'm being clear enough. 不确定我是否足够清楚。 Here is a nice diagram that may help understanding how this works. 这是一个很好的图表 ,可能有助于理解这是如何工作的。

Does the recursive call to mergeSort create a whole new namespace, [...]? 对mergeSort的递归调用是否会创建一个全新的命名空间,[...]?

Yes. 是。

Whenever the Interpreter encounters a call to a function, its creates a frame object , which is pushed to a frame stack . 只要解释器​​遇到对函数的调用,它就会创建一个框架对象 ,并将其推送到框架堆栈 Each time a frame is created, that frame is given its own private namespace, where each variable in the frame is re-defined. 每次创建一个帧时,该帧都被赋予其自己的私有名称空间,其中帧中的每个变量都被重新定义。

In your case, each time mergeSort() is called, Python creates a new frame object, and pushes it to the frame stack. 在您的情况下,每次调用mergeSort() ,Python都会创建一个新的框架对象,并将其推送到框架堆栈。 Each time Python creates a frame from a call to mergeSort() , lefthalf is re-defined. 每次Python从调用mergeSort()创建一个帧时, lefthalf重新定义lefthalf

With a few well placed calls to print() , you can see the value of lefthalf at each call to mergeSort() : 通过几次调用print() ,您可以在每次调用mergeSort()看到lefthalf的值:

 This is the 1 recursive call to mergeSort()
 lefthalf is:  [54, 26, 93, 17]
 alist is:  [54, 26, 93, 17, 77, 31, 44, 55, 20]
  This is the 2 recursive call to mergeSort()
  lefthalf is:  [54, 26]
  alist is:  [54, 26, 93, 17]
   This is the 3 recursive call to mergeSort()
   lefthalf is:  [54]
   alist is:  [54, 26]
    This is the 4 recursive call to mergeSort()
     This is the 5 recursive call to mergeSort()
      This is the 6 recursive call to mergeSort()
      lefthalf is:  [93]
      alist is:  [93, 17]
       This is the 7 recursive call to mergeSort()
        This is the 8 recursive call to mergeSort()
         This is the 9 recursive call to mergeSort()
         lefthalf is:  [77, 31]
         alist is:  [77, 31, 44, 55, 20]
          This is the 10 recursive call to mergeSort()
          lefthalf is:  [77]
          alist is:  [77, 31]
           This is the 11 recursive call to mergeSort()
            This is the 12 recursive call to mergeSort()
             This is the 13 recursive call to mergeSort()
             lefthalf is:  [44]
             alist is:  [44, 55, 20]
              This is the 14 recursive call to mergeSort()
               This is the 15 recursive call to mergeSort()
               lefthalf is:  [55]
               alist is:  [55, 20]
                This is the 16 recursive call to mergeSort()
                 This is the 17 recursive call to mergeSort()
[17, 20, 26, 31, 44, 54, 55, 77, 93]
>>> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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