Список Python и для каждого доступа (Find/Replace во встроенном списке)

Первоначально я думал, что Python является чистым языком для передачи по ссылке.

Исходя из C/С++, я не могу не думать о управлении памятью, и это трудно вывести из головы. Поэтому я пытаюсь думать об этом с точки зрения Java и думать обо всем, кроме примитивов, как о переходе по ссылке.

Проблема: у меня есть список, содержащий кучу экземпляров определенного пользователем класса.

Если я использую синтаксис for-each, то есть.:

for member in my_list:
    print(member.str);

Является ли member эквивалентом фактической ссылки на объект?

Это эквивалент:

i = 0
while i < len(my_list):
    print(my_list[i])
    i += 1

Я думаю, что это НЕ, потому что, когда я ищу замену, это не работает, то есть это не работает:

for member in my_list:
    if member == some_other_obj:
        member = some_other_obj

Простая находка и замена в списке. Может ли это быть сделано в каждом цикле, если да, то каким образом? Кроме того, мне просто нужно использовать синтаксис произвольного доступа (квадратные скобки) или будет НИКОГДА работать, и мне нужно удалить запись и вставить новую? То есть:.

i = 0
for member in my_list:
   if member == some_other_obj:
      my_list.remove(i)
      my_list.insert(i, member)
   i += 1

Ответы

Ответ 1

Ответ на это был хорошим, так как комментарии привели к улучшению моего собственного понимания переменных Python.

Как отмечено в комментариях, когда вы перебираете список с чем-то вроде for member in my_list, переменная member привязывается к каждому последующему элементу списка. Однако переустановка этой переменной внутри цикла напрямую не влияет на список. Например, этот код не изменит список:

my_list = [1,2,3]
for member in my_list:
    member = 42
print my_list

Вывод:

[1, 2, 3]

Если вы хотите изменить список, содержащий неизменяемые типы, вам нужно сделать что-то вроде:

my_list = [1,2,3]
for ndx, member in enumerate(my_list):
    my_list[ndx] += 42
print my_list

Вывод:

[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

[42, 43, 44]

Обратите внимание, что вы все еще не меняете список, просто изменяя объекты в списке.

Вам может пригодиться чтение Именование и привязка.

Ответ 2

Python - это не Java, ни C/С++ - вам нужно перестать думать, что так реально использовать силу Python.

Python не имеет пропущенных значений и не передает ссылки, но вместо этого использует pass-by-name (или pass-by-object) - другими словами, почти все связано с именем, которое вы можете использовать (два очевидных исключения - это чередование и индексирование списка).

Когда вы выполняете spam = "green", вы привязали имя spam к строковому объекту "green"; если вы делаете eggs = spam, вы ничего не скопировали, вы не указали ориентиры; вы просто связали другое имя eggs с тем же объектом ("green" в этом случае). Если вы затем привяжете spam к чему-то другому (spam = 3.14159) eggs, все равно будет привязано к "green".

Когда выполняется цикл for-loop, он берет имя, которое вы ему даете, и привязывает его по очереди к каждому объекту в iterable при запуске цикла; когда вы вызываете функцию, она принимает имена в заголовке функции и связывает их с переданными аргументами; переназначение имени фактически переименовывает имя (это может занять некоторое время, чтобы поглотить это - во всяком случае, для меня это сделало).

С помощью циклов, использующих списки, есть два основных способа назначения списка обратно:

for i, item in enumerate(some_list):
    some_list[i] = process(item)

или

new_list = []
for item in some_list:
    new_list.append(process(item))
some_list[:] = new_list

Обратите внимание на [:] на последнем some_list - он вызывает мутацию элементов some_list (установка всего элемента new_list) вместо того, чтобы пересобирать имя some_list на new_list. Это важно? Это зависит! Если у вас есть другие имена помимо some_list, привязанные к одному и тому же объекту списка, и вы хотите, чтобы они видели обновления, вам нужно использовать метод разрезания; если вы этого не сделаете, или если вы не хотите, чтобы они видели обновления, тогда rebind - some_list = new_list.

Ответ 3

Вы можете заменить что-то там, указав индекс вместе с элементом.

>>> 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']

Обратите внимание: если вы хотите удалить что-то из списка, вам нужно перебрать копию списка, иначе вы получите ошибки, так как вы пытаетесь изменить размер того, что вы итерируете. Это можно сделать довольно легко с помощью фрагментов.

Неправильно:

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

2 все еще там, потому что мы изменили размер списка по мере того, как мы его повторяли. Правильный способ:

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