在Python中,有没有一种方法可以使用foreach样式的构造来更新容器?

时间:2018-10-24 17:45:03

标签: python list for-loop foreach containers

我认为Python中的foreach样式结构将允许我像C#中那样更新列表。不是。

经过一番调查,我发现在foreach样式结构的Python中使用的变量不是引用,而是单独的标量变量,因此我无法使用该变量来更新容器。有没有办法使用foreach样式更新容器?

以下代码演示了我的问题:

inputString = "   Type X Widgets  ,  25, 14.20 ,  Type Y Widgets , 4  , 1.12 "
inputList = inputString.split(',')
print(inputList) # Now I need to get rid of whitespace on the ends of each element

# The foreach-style does NOT update inputList
for element in inputList:
    element = element.strip()
    print(element, end=",") # element contains the stripped string as I wanted
print()
print(inputList) # the whitespace on the ends of the elements is still there

# The for-style with subscripts DOES update inputList
for i in range(len(inputList)):
    inputList[i] = inputList[i].strip()
    print(inputList[i], end=",") # inputList[i] contains the stripped string as I wanted
print()
print(inputList) # it finally contains the stripped strings with no whitespace on the ends

以下是上面的输出:

['   Type X Widgets  ', '  25', ' 14.20 ', '  Type Y Widgets ', ' 4  ', ' 1.12 ']
Type X Widgets,25,14.20,Type Y Widgets,4,1.12,
['   Type X Widgets  ', '  25', ' 14.20 ', '  Type Y Widgets ', ' 4  ', ' 1.12 ']
Type X Widgets,25,14.20,Type Y Widgets,4,1.12,
['Type X Widgets', '25', '14.20', 'Type Y Widgets', '4', '1.12']

第一个for循环不会更新容器。第二个做。在这种简单的情况下,不必使用下标就没什么了,但是我真的很希望能够在无法使用下标的情况下使用foreach样式来更新更复杂的容器类型。 / p>

我可以在C#中进行此操作,它是一种非常有用的工具。除了我在第一个循环中尝试过的操作以外,是否可以在Python中做到这一点? (如果是这样,我想它会涉及使用指针。Python甚至还有指针吗?)

3 个答案:

答案 0 :(得分:2)

也许这不是您想要的,但是一种简单的方法是创建一个新列表,如下所示:

inputList = [
    element.strip()
    for element in inputList]

这会将新列表分配给相同的变量,替换旧列表(旧列表将在此后的一段时间内被垃圾回收)。

缺点是,这会使使用的内存量增加一倍;完成上述声明后,可以对旧列表进行垃圾回收,但是内存使用量仍然会激增。

答案 1 :(得分:2)

在这种情况下,您不能这样做。那是因为您是将对象重新分配到新引用。

在这样的“ foreach”迭代中:

for element in inputList:

您要遍历列表中元素本身的对象。但是在这种情况下,它是一个str对象,它是不可变的。即当您尝试分配此行时:

element = element.strip()

您要从原始内容的剥离内容中为 new 对象重新分配 element 。由于它是一个新对象,因此它与inputList本身无关。

但是,在第二个示例中,您现在基于inputList遍历索引的列表

for i in range(len(inputList)):
# range(len(inputList)) -> range(0, 6)

遍历列表时,请注意,您正在重新分配inputList的特定索引:

inputList[i] = inputList[i].strip()

这会将 new 对象分配回inputList[i]。它不再是您曾经在列表中拥有的str对象。

已经说过,在其他用例中,只要对象是可变的,您正在寻找的“ foreach”就可以正常工作 。观察以下示例:

lst = [[] for _ in range(5)]
lst
# [[], [], [], [], []]
for i in lst:
    i.append('foo')

lst
# [['foo'], ['foo'], ['foo'], ['foo'], ['foo']]

请注意此处的区别:i不是重新分配的,而是通过append()方法直接更改的。为了进一步证明i是您所期望的直接对象引用,如果我在迭代完成后这样做

i.append('bar')
lst
# [['foo'], ['foo'], ['foo'], ['foo'], ['foo', 'bar']]

id(i)
# 61353816
id(lst[-1])
# 61353816

看看现在如何添加lst last 元素。这是因为i仍保留引用。 id()还会显示您所要询问的确定证据。

如果我要这样写迭代:

for i in lst:
    i = ['foo']

lst
# [[], [], [], [], []]
id(i)
# 61354112 <-- different obj id
id(lst[-1])
# 61353816

由于您的示例无法使用的相同原因,它不再起作用。因为i现在已重新分配给新对象,而不是迭代中的直接对象引用。请注意,对象ID有所不同。

答案 2 :(得分:-2)

嗯。在 for循环中,每次迭代期间,您的变量(在您的情况下为 element )将分配给列表中下一个值(对象)的副本,而不是此值(对象)的参考。 (这不是完全正确的,但是您知道我想说的话)。因此,要解决您的项目,您可能可以执行以下操作:

for element in inputList:
    inputList[inputList.index(element)] = element.strip()
print(inputList)

请注意,如果列表中有两次相同的元素,则此方法将无效。

希望它有所帮助!