0

I want to delete items from a list, except those defined in an exception list. However, the code below skips every second entry, up to "one", after which it processes the rest of the attribute list as expected. Why is that?

ExceptionList = ['uno', 'dos', 'tres', 'cuatro', 'OID']
AttributeList = ['uno', 'dos', 'tres', 'cuatro', 'OID', 'one', 'two', 'three', 'four', 'five']
DeleteAttributes = AttributeList

print("Attributes: ", AttributeList)
print("Exception List: ", ExceptionList)

for Attribute in AttributeList:
        print("Attribute: ", Attribute)
    for Exception in ExceptionList:
            if Exception == Attribute:
            print("Exception: " + Attribute)
            DeleteAttributes.remove(Attribute)

print("Delete Attributes: ", DeleteAttributes)

for Attribute in DeleteAttributes:
        print("deleting.. ", Attribute)
MapEngine
  • 553
  • 1
  • 9
  • 21
  • 2
    You're changing `AttributeList` as you iterate over it. `DeleteAttributes = AttributeList` doesn't create a copy, it just assigns both names to point at the same list object. – Patrick Haugh Jun 25 '18 at 15:18
  • 2
    If you intended `DeleteAttributes` to be a copy of `AttributeList`, you could try `DeleteAttributes = AttributeList.copy()` – khelwood Jun 25 '18 at 15:18

4 Answers4

1

I'm not quite sure, how the for exactly works in python. But i think it generates an integer and iterates over your list. If you remove an element, the indexes of the next elements decrese, but the incrementing integer of the for-loop remains the same and therefore it skips the next.

andii1997
  • 139
  • 2
  • 5
1

As mentioned by @khelwood and @Patrick Haugh you need a copy of AttributeList for your approach. You are iterating over something you are editing.

Do you need an approach like this for a reason?

If I understand your purpose correctly, you simply need the intersection of the Attribute and Expection Lists, if so the following should cover your use case:

DeleteAttributes = list(set(ExceptionList).intersection(AttributeList))
muddle
  • 31
  • 4
  • I need the elements left over after checking them against the exception list, so your approach works well, except with **symmetric_difference** instead of **intersection**. – MapEngine Jun 25 '18 at 15:48
  • 1
    Note that this will result in list that has only one copy of each element, no matter how many times each element appeared in the original list, and there's no guarantee they will be in the same order. – Acccumulation Jun 25 '18 at 15:56
1

DeleteAttributes = AttributeList Here DeleteAttributes is referring to same address point. And once you are deleting first matching element of AttributeList from DeleteAttributes list, It mean you have deleted from same list only. And now second element from the list become first. So It will skip the second element after every matching element.

kanishk
  • 91
  • 9
0

As mentioned in the comments, assignments of containers in python tends to make copies of the references unless specifically told otherwise, so DeleteAttributes = AttributeList just tells python that the name DeleteAttributes points to same object as AttributeList; it doesn't create a new object. To make sure you make a new object, in python 3 you can do DeleteAttributes = AttributeList.copy(), although I'm not sure that works in python 2. Also, you don't have to do for Exception in ExceptionList: if Exception == Attribute:; you can just do if Attribute in ExceptionList. Or you can replace your entire for-loop with the list comprehension DeleteAttributes = [attr in AttributeList if not attr in ExceptionList]

Acccumulation
  • 3,491
  • 1
  • 8
  • 12
  • 1
    `DeleteAttributes = AttributeList` isn't a "shallow copy of a container". The container isn't being copied at all. It is making another name refer to the same object. – khelwood Jun 25 '18 at 15:31