我不明白这个带有extend和append的扁平化是如何在Python3.6中工作的

我不明白这个带有extend和append的扁平化是如何在Python3.6中工作的,python,python-3.x,Python,Python 3.x,我把下面的代码放在Pythontutor.com上,看看我是否能理解它是如何工作的。然而,尽管阅读了关于展平、扩展和附加的文章,我还是有点迷茫。我的问题是,为什么它会对“b”进行两次评估?例如,它进入extend,然后创建一个新列表,然后将“b”带到else和appends?我将非常感谢任何能让我更清楚这一点的帮助 aList = ['b','a','c',2],[[[3]],'dog',4,5] def flatten(aList): newList = [ ] for i

我把下面的代码放在Pythontutor.com上,看看我是否能理解它是如何工作的。然而,尽管阅读了关于展平、扩展和附加的文章,我还是有点迷茫。我的问题是,为什么它会对“b”进行两次评估?例如,它进入extend,然后创建一个新列表,然后将“b”带到else和appends?我将非常感谢任何能让我更清楚这一点的帮助

aList = ['b','a','c',2],[[[3]],'dog',4,5]  
def flatten(aList):
    newList = [ ]
    for item in aList:
        if type(item) == type([]):
            newList.extend(flatten(item))
        else:
            newList.append(item)
    return newList

print(flatten(aList))
函数用于再次调用自身。其思想是将一个较大的问题分解为较小的部分,每个部分独立解决,然后将结果组合起来解决较大的问题

在这里,只要当前序列中包含的元素是列表,Flatte就会再次调用自己。这些递归调用将继续,直到较小的部分不再包含更多列表

需要记住的是,像newList这样的本地名称对于每个函数调用都是本地的。即使展平调用本身,每个调用都会产生一个独立的新的本地newList值

对于您的输入,一个元组:

['b', 'a', 'c', 2], [[[3]], 'dog', 4, 5]
第一个元素也是一个列表:

['b', 'a', 'c', 2]
因此,这将传递给一个新的扁平化调用。该子列表中没有更多的列表,因此该函数所做的只是将每个项附加到newList列表中,并将其作为结果返回。返回后,恢复第一个展平函数,并通过extend调用将返回的列表添加到本地newList

一直以来,当您看到Pythontutor如何将其可视化时,您会注意到原始对象中有许多指向这些列表的指针:

您可以看到,第一个展平调用引用了一个包含两个元素的元组,第二个展平调用引用了该元组的第一个元素,即包含的列表。Python值都存在于一个称为“heap”的专用内存区域中,名称和列表元素都只是标签、引用、带有字符串的名称标签,您可以拥有任意数量的此类标签。看见两个展平函数都有自己的指向列表对象的newList引用,而当前活动的展平函数正忙于将列表引用中的值复制到newList

因此,一旦对扁平化的递归调用将控制权返回到剩余的、仍处于活动状态的扁平化函数。使用返回值扩展本地newList函数后,该函数将移动到下一个元素[[[3]],“dog',4,5],该元素还有几个列表要处理,首先[[3]],然后[3],然后再没有嵌套列表要处理

如果您用缩进法将这些内容全部写出来,您将得到:

->展平['b','a','c',2],[[3],'dog',4,5] newList设置为空列表 项目设置为['b','a','c',2] typeitem是一个列表,因此递归 ->展平['b','a','c',2] newList设置为空列表 项设置为“b”,而不是列表,附加到新列表,现在为['b'] 项目设置为“a”,而不是列表,附加到新列表,现在为['b','a'] 项目设置为“c”,而不是列表,附加到新列表,现在为['b'、'a'、'c'] 项目设置为2,而不是列表,附加到newList,现在为['b','a','c',2] 循环完成后,返回newList 展平[[3]],“狗”,4,5] newList设置为空列表 项目设置为[[3]] typeitem是一个列表,因此递归 ->展平[[3]] newList设置为空列表 项目设置为[3] typeitem是一个列表,因此递归 ->展平[3] newList设置为空列表 项目设置为3 typeitem是一个列表,因此递归 ->展平[3] 项设置为3,而不是列表,附加到newList,现在为[3] 循环完成后,返回newList
如果写得更简单,也许更容易理解:

aList = ['b','a','c',2],[[[3]],'dog',4,5]  
def flatten(value):
    if not isinstance(value,(list,tuple)) : return [value]
    return [ item for subItem in value for item in flatten(subItem) ]
如果value参数是一个列表或元组,则将每个元素连接起来以形成平坦的输出第二行。因为这些元素中的每一个元素本身都可以是列表或元组,所以函数会在连接到其他元素之前调用自身以展平该项。当函数的参数是标量值(即不是列表或元组)时,函数将停止调用自身。在这种情况下,它将返回值本身作为单个元素列表的第1行,因为它不能进一步展平,并且它的调用者本身需要一个列表

flatten( aList ) : returns ['b']+['a']+['c']+[2]+[3]+['dog']+[4]+[5]

   --> flatten( ['b','a','c',2] ) : returns ['b']+['a']+['c']+[2]
         --> flatten('b') : returns ['b']     
         --> flatten('a') : returns ['a']     
         --> flatten('c') : returns ['c']     
         --> flatten(2)   : returns [2] 

   --> flatten( [[[3]],'dog',4,5] ): returns [3]+['dog']+[4]+[5]
         --> flatten([[3]]) : returns [3]
               --> flatten([3]) : returns [3]
                     --> flatten(3) : returns [3]
         --> flatten('dog') : returns ['dog']
         --> flatten(4)     : returns [4]
         --> flatten(5)     : returns [5]

函数递归,再次调用自身来处理问题的一个子集。如果不清楚的话,值得指出的是,输入是一个元组。括号不是必须的。这对我来说更有意义了!谢谢你的帮助!很高兴能帮上忙!如果您觉得它对您有用,请随时联系-