Список Python & для каждого доступа (Найти / Заменить во встроенном списке) - PullRequest
25 голосов
/ 15 сентября 2011

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

Исходя из C / 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

Простой поиск и замена в списке.Можно ли это сделать в цикле 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

Ответы [ 3 ]

47 голосов
/ 15 сентября 2011

Ответ на этот вопрос был хорошим, так как комментарии привели к улучшению моего собственного понимания переменных 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]

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

Возможно, вам будет полезно прочитать Имена и привязки .

16 голосов
/ 15 сентября 2011

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

Python не имеет ни передачи по значению, ни передачи поссылка, но вместо этого используется передача по имени (или передача по объекту) - иными словами, почти все связано с именем, которое вы затем можете использовать (два очевидных исключения: индексация кортежей и списков).

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

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

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

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, связанные с тем же объектом списка, и вы хотите, чтобы они видели обновления, тогда вам нужно использовать метод нарезки;если вы этого не сделаете, или если вы не хотите, чтобы они видели обновления, перепривязайте - some_list = new_list.

6 голосов
/ 15 сентября 2011

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

>>> 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']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...