Почему при использовании цикла for таким образом не заменяется каждый элемент в списке?

#python #list

#python #Список

Вопрос:

У меня есть список, который выглядит следующим образом:

 ['G1X0.000Y3.000', 'G2X2.000Y3.000I1.000J2.291', 'G1X2.000Y-0.000', 'G2X0.000Y0.000I-1.000J-2.291']
  

Форматирование таково, что если числовое содержимое после X, Y, I или J положительное, знак отсутствует, но если они отрицательные, то включается знак — . Я пытаюсь перебрать этот список и в основном добавить знак , если в начале числового содержимого нет знака. Результат должен выглядеть следующим образом:

 ['G1X 0.000Y 3.000', 'G2X 2.000Y 3.000I 1.000J 2.291', 'G1X 2.000Y-0.000', 'G2X 0.000Y 0.000I-1.000J-2.291']
  

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

 #Make sure that X, Y, I and J start with   or - 
for count,i in enumerate(fileContents):
    if 'G' in i:
        indexOfI = i.index("X")
        if(i[indexOfI 1]!="-"):
            print(i[:indexOfI 1]   " "   i[indexOfI 1:])
            fileContents[count] = i[:indexOfI 1]   " "   i[indexOfI 1:]

        indexOfY = i.index("Y")
        if(i[indexOfY 1]!="-"):
            fileContents[count] = i[:indexOfY 1]   " "   i[indexOfY 1:]

    if "G2" in i:
        indexOfI = i.index("I")
        if(i[indexOfI 1]!="-"):
            fileContents[count] = i[:indexOfI 1]   " "   i[indexOfI 1:]

        indexOfJ = i.index("J")
        if(i[indexOfJ 1]!="-"):
            fileContents[count] = i[:indexOfJ 1]   " "   i[indexOfJ 1:]
  

оператор print(i[:indexOfI 1] " " i[indexOfI 1:]) выдает в консоли вывод:

 G1X 0.000Y3.000
G2X 2.000Y3.000I1.000J2.291
G1X 2.000Y-0.000
G2X 0.000Y0.000I-1.000J-2.291
  

Который показывает мне, что это выполняет то, что я хочу, однако, если я печатаю fileContents после этой функции, в списке нет изменений. Другими словами, следующая строка кода не заменяет элемент списка в каждой позиции, как я ожидаю:

 fileContents[count] = i[:indexOfI 1]   " "   i[indexOfI 1:]
  

Почему это не работает, когда я могу сделать следующее, и он правильно обновляет список?

 #Format each command to a 32 byte string
for i, s in enumerate(fileContents):
    fileContents[i] =s.ljust(32,'#')
  

редактировать: изначально я озаглавил сообщение «Почему использование такого понимания списка не заменяет каждый элемент в списке?». Пользователи любезно указали, что это не имеет ничего общего с пониманием списка. Прошу прощения, я думал, что этот формат x in list предназначен для понимания списка.

Комментарии:

1. в вашем коде нет понимания списка

2. Не знаю, почему вы ссылаетесь на понимание списка в названии вашего вопроса, потому что вы, похоже, не используете ни один из них. Но не волнуйтесь, циклы for в порядке.

3. Что такое s[indexOfI 1] ?

4. Почему бы не изучить, а затем использовать регулярные выражения? Синтаксический анализ, основанный на .index() , обычно находится на хрупкой стороне.

5. Вы видите четыре повторяющихся бита кода — они должны быть в функции, которую вы вызываете с символом для проверки, тогда вам нужно написать и поддерживать ее только один раз

Ответ №1:

если я печатаю fileContents после этой функции, в списке нет изменений.

На самом деле, есть изменения, но добавляется не более одного (последнего).

Это потому, что вы не применяете одно и то же изменение к i , что означает, что следующие if блоки будут копировать часть из i back в, в fileContents[count] которой не было предыдущего изменения.

Быстрое решение — убедиться, что вы также применяете изменение к i . Что-то вроде:

 fileContents[count] = i = i[:indexOfI 1]   " "   i[indexOfI 1:]
#                     ^^^^
  

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

 import re
fileContents = [re.sub(r"([XYIJ])(?=d)", r"1 ", line) for line in fileContents]
  

Это будет соответствовать любому X, Y, I или J, за которым следует цифра. В этом случае между ними вставляется плюс. Если вам нужны более строгие правила сопоставления, где строка должна начинаться с «G», … и т.д., Тогда регулярное выражение станет более сложным.

Комментарии:

1. Спасибо за это быстрое исправление! Из интереса, есть ли более питонический способ сделать это?

2. К моему ответу добавлено решение с пониманием списка.

Ответ №2:

В цикле

 for i, s in enumerate(fileContents):
  

вы перебираете fileContents список, который хотите изменить в том же цикле. Это всегда опасно.

Выполните итерацию по копии этого списка, который вы можете просто создать, добавив к нему [:]:

 for i, s in enumerate(fileContents[:]):
  

Ответ №3:

Вы можете просто добавить после любого из этих символов, а затем заменить back - (для любого) на - :

 def my_replace(item):
    for char in 'XYIJ':
        item = item.replace(char, f'{char} ')
    return item.replace(' -', '-')

spam = ['G1X0.000Y3.000', 'G2X2.000Y3.000I1.000J2.291',
        'G1X2.000Y-0.000', 'G2X0.000Y0.000I-1.000J-2.291']
eggs = [my_replace(item) for item in spam] # now, this is list comprehension
print(eggs)
  

вывод

 ['G1X 0.000Y 3.000', 'G2X 2.000Y 3.000I 1.000J 2.291', 'G1X 2.000Y-0.000', 'G2X 0.000Y 0.000I-1.000J-2.291']