Список 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']