为了账号安全,请及时绑定邮箱和手机立即绑定

我不明白这对 Python 3.6 的扩展和追加是如何工作的

我不明白这对 Python 3.6 的扩展和追加是如何工作的

料青山看我应如是 2021-07-04 12:57:03
我把下面的代码放在 Pythontutor.com 中,看看我是否能理解它是如何工作的。然而,尽管阅读了扁平化、扩展和附加,我还是有点迷茫。我的问题是为什么它会评估 'b' 两次?例如,它先扩展然后创建一个新列表,然后将 'b' 带到 else 并附加?我将不胜感激任何能让我更清楚这一点的帮助。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 newListprint(flatten(aList))
查看完整描述

2 回答

?
动漫人物

TA贡献1815条经验 获得超10个赞

该函数使用递归再次调用自身。这个想法是你将一个更大的问题分解成更小的部分,每个部分独立解决,然后结合结果来解决更大的问题。

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

需要记住的是,本地名称newList对于每个函数调用都是本地的。即使flatten()调用自身,每次调用都会产生一个新的、newList独立的本地值。

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

['b', 'a', 'c', 2], [[[3]], 'dog', 4, 5]

第一个元素也是一个列表:

['b', 'a', 'c', 2]

所以这被传递给一个新的flatten()电话。该子列表中没有更多列表,因此该函数所做的就是将每个项目附加到newList列表中并将其作为结果返回。返回后,将flatten()恢复第一个函数,并newList通过extend()调用将返回的列表添加到本地。

在您查看 Pythontutor 如何将其可视化的同时,您会注意到原始对象中有很多指向这些列表的指针:

//img1.sycdn.imooc.com//60e40bca0001ca0204650538.jpg

您可以看到第一个flatten()调用引用了一个包含两个元素的元组,第二个flatten()调用引用了该元组的第一个元素,即包含的列表。Python 值都存在于称为“堆”的专用内存区域中,名称和列表元素都只是标签、引用、带有附加到这些对象的字符串的名称标签,您可以拥有任意数量的此类标签。请参阅Ned Batchelder 关于该主题的优秀文章。这两个flatten()函数都有自己的newList指向列表对象的引用,当前活动的flatten()函数正忙于从aList它所拥有的引用复制值newList

因此,一旦递归调用flatten()将控制权返回给剩余的仍处于活动状态的flatten()函数。一旦newList使用返回值扩展了本地函数,该函数就会移动到下一个元素[[[3]], 'dog', 4, 5],该元素还有几个列表要处理,首先[[3]],然后[3]没有更多的嵌套列表要处理。

如果你用新调用的缩进写出这一切,你会得到:

  • -> flatten((['b', 'a', 'c', 2], [[[3]], 'dog', 4, 5]))

  • newList 设置为空列表

  • item 设定为 ['b', 'a', 'c', 2]

  • type(item) 是一个列表,所以递归

    • -> flatten(['b', 'a', 'c', 2])

    • newList 设置为空列表

    • item设置为'b',不是列表,附加到newList,现在['b']

    • item设置为'a',不是列表,附加到newList,现在['b', 'a']

    • item设置为'c',不是列表,附加到newList,现在['b', 'a', 'c']

    • item设置为2,不是列表,附加到newList,现在['b', 'a', 'c', 2]

    • 循环完成,返回 newList

    • <- ['b', 'a', 'c', 2]

  • newList扩展为['b', 'a', 'c', 2],所以现在['b', 'a', 'c', 2]

  • item 设定为 [[[3]], 'dog', 4, 5]

  • type(item) 是一个列表,所以递归

    • -> flatten([[3]])

    • newList 设置为空列表

    • item 设定为 [3]

    • type(item) 是一个列表,所以递归

    • newList扩展为[3],所以现在[3]

    • 循环完成,返回 newList

    • <- [3]

    • -> flatten([3])

    • item设置为3,不是列表,附加到newList,现在[3]

    • 循环完成,返回 newList

    • <- [3]

    • -> flatten([3])

    • newList 设置为空列表

    • item 设定为 3

    • type(item) 是一个列表,所以递归

    • newList扩展为[3],所以现在[3]

    • 循环完成,返回 newList

    • <- [3]

    • -> flatten([[[3]], 'dog', 4, 5])

    • newList 设置为空列表

    • item 设定为 [[3]]

    • type(item) 是一个列表,所以递归

    • newList扩展为[3],所以现在[3]

    • item设置为'dog',不是列表,附加到newList,现在[3, 'dog']

    • item设置为4,不是列表,附加到newList,现在[3, 'dog', 4]

    • item设置为5,不是列表,附加到newList,现在[3, 'dog', 4, 5]

    • 循环完成,返回 newList

    • <- [3, 'dog', 4, 5]

  • newList扩展为[3, 'dog', 4, 5],所以现在['b', 'a', 'c', 2, 3, 'dog', 4, 5]

  • <- ['b', 'a', 'c', 2, 3, 'dog', 4, 5]

在 Pythontutor 可视化(Pythontutor 用于 Python 代码的默认可视化)中,您看到"b" 两次实际上是 Pythontutor 使用的简化的产物。虽然列表和元组显示为单独的对象,箭头显示它们是如何被引用的,但“原始”类型(例如字符串和整数)显示在列表内或直接在函数框架中的变量内显示。

实际上,这些对象也是独立的,它们也存在于堆中并被引用。该"b"值作为单个对象存在,多个列表引用它。但是,您可以选择不同的可视化:

//img1.sycdn.imooc.com//60e40bdd0001155404480941.jpg

使用该选项,可视化变得更大:

//img1.sycdn.imooc.com//60e40beb00017f3303360074.jpg

在这里您可以看到,newList在活动函数框架中和从输入元组引用的原始列表对象都引用了一个str具有 value 的对象"b"。但是您可能会看到,在这种详细程度的情况下,事情有点过于冗长,无法一次性完成。


查看完整回答
反对 回复 2021-07-06
?
三国纷争

TA贡献1804条经验 获得超7个赞

如果写得更简单,也许会更容易理解:


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参数是列表或元组,则连接每个元素以形成扁平输出(第 2 行)。因为这些元素中的每一个本身都可以是一个列表或元组,所以该函数会调用自己在连接到其他元素之前将元素展平。当其参数是标量值(即不是列表或元组)时,该函数将停止调用自身。在这种情况下,它会将值本身作为单个元素列表(第一行)返回,因为它无法进一步展平,并且它的调用者(本身)需要一个列表。


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]


查看完整回答
反对 回复 2021-07-06
  • 2 回答
  • 0 关注
  • 72 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信