Python列表和for-access访问(在内置列表中查找/替换)

Syn*_*ate 26 python foreach replace reference list

我原本以为Python是一种纯粹的传递引用语言.

来自C/C++我不禁想到内存管理,而且很难把它从我的头脑中解脱出来.因此,我试图从Java的角度来考虑它,并将所有内容都考虑在内,而不是原始内容作为参考传递.

问题:我有一个列表,其中包含一组用户定义类的实例.

如果我使用for-each语法,即:

for member in my_list:
    print(member.str);
Run Code Online (Sandbox Code Playgroud)

是否member相当于对象的实际引用?

它是否相当于:

i = 0
while i < len(my_list):
    print(my_list[i])
    i += 1
Run Code Online (Sandbox Code Playgroud)

我认为不是,因为当我想要替换时,它不起作用,也就是说,这不起作用:

for member in my_list:
    if member == some_other_obj:
        member = some_other_obj
Run Code Online (Sandbox Code Playgroud)

在列表中进行简单的查找和替换.可以在for-each循环中完成,如果是,怎么做?另外,我是否只需要使用随机访问语法(方括号),或者什么都不会工作,我需要删除该条目,并插入一个新条目?即:

i = 0
for member in my_list:
   if member == some_other_obj:
      my_list.remove(i)
      my_list.insert(i, member)
   i += 1
Run Code Online (Sandbox Code Playgroud)

Gre*_*att 48

回答这个问题一直很好,因为这些评论已经改善了我对Python变量的理解.

正如评论,当你遍历的东西,如一个列表指出for member in my_listmember变量绑定到每个连续的列表元素.但是,在循环中重新分配该变量不会直接影响列表本身.例如,此代码不会更改列表:

my_list = [1,2,3]
for member in my_list:
    member = 42
print my_list
Run Code Online (Sandbox Code Playgroud)

输出:

[1,2,3]

如果要更改包含不可变类型的列表,则需要执行以下操作:

my_list = [1,2,3]
for ndx, member in enumerate(my_list):
    my_list[ndx] += 42
print my_list
Run Code Online (Sandbox Code Playgroud)

输出:

[43,44,45]

如果列表包含可变对象,则可以member直接修改当前对象:

class C:
    def __init__(self, n):
        self.num = n
    def __repr__(self):
        return str(self.num)

my_list = [C(i) for i in xrange(3)]
for member in my_list:
    member.num += 42
print my_list
Run Code Online (Sandbox Code Playgroud)

[42,43,44]

请注意,您仍然没有更改列表,只需修改列表中的对象即可.

您可能会从阅读命名和绑定中受益.


Eth*_*man 16

Python不是Java,也不是C/C++ - 您需要停止思考如何真正利用Python的强大功能.

Python没有pass-by-value,也没有pass-by-reference,而是使用pass-by-name(或pass-by-object) - 换句话说,几乎所有东西都绑定到一个名称,然后你可以use(两个明显的例外是元组和列表索引).

执行此操作时spam = "green",您已将名称绑定spam到字符串对象"green"; 如果你那么eggs = spam你没有复制任何东西,你没有做出参考指针; 你只需将另一个名称绑定eggs到同一个对象("green"在本例中).如果你然后绑定spam到其他东西(spam = 3.14159)eggs仍将绑定"green".

当一个for循环执行时,它会获取你给它的名字,并在运行循环时依次将它绑定到iterable中的每个对象; 当你调用一个函数时,它会获取函数头中的名称并将它们绑定到传递的参数; 重新分配一个名字实际上是重新命名一个名字(它可能需要一段时间才能吸收这个 - 无论如何它都适合我).

使用for循环利用列表,有两种基本方法可以分配回列表:

for i, item in enumerate(some_list):
    some_list[i] = process(item)
Run Code Online (Sandbox Code Playgroud)

要么

new_list = []
for item in some_list:
    new_list.append(process(item))
some_list[:] = new_list
Run Code Online (Sandbox Code Playgroud)

注意[:]最后一个some_list- 它导致了some_list元素的变异(将整个元素设置为new_list元素),而不是将名称重新绑定some_listnew_list.这很重要吗?这取决于!如果除了some_list绑定到同一个列表对象之外还有其他名称,并且希望它们看到更新,那么您需要使用切片方法; 如果你不这样做,或者如果你希望他们看到更新,那么重新绑定 - some_list = new_list.


Gli*_*der 6

你可以通过获取索引和项目来替换那里的东西.

>>> foo = ['a', 'b', 'c', 'A', 'B', 'C']
>>> for index, item in enumerate(foo):
...     print(index, item)
...
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'A')
(4, 'B')
(5, 'C')
>>> for index, item in enumerate(foo):
...     if item in ('a', 'A'):
...         foo[index] = 'replaced!'
...
>>> foo
['replaced!', 'b', 'c', 'replaced!', 'B', 'C']
Run Code Online (Sandbox Code Playgroud)

请注意,如果要从列表中删除某些内容,则必须迭代列表的副本,否则您将收到错误,因为您正在尝试更改正在迭代的内容的大小.这可以通过切片很容易地完成.

错误:

>>> foo = ['a', 'b', 'c', 1, 2, 3]
>>> for item in foo:
...     if isinstance(item, int):
...         foo.remove(item)
...
>>> foo 
['a', 'b', 'c', 2]
Run Code Online (Sandbox Code Playgroud)

2仍在那里因为我们在迭代它时修改了列表的大小.正确的方法是:

>>> foo = ['a', 'b', 'c', 1, 2, 3]
>>> for item in foo[:]:
...     if isinstance(item, int):
...         foo.remove(item)
...
>>> foo 
['a', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)