0
import os
os.chdir('G:\\f5_automation')
r = open('G:\\f5_automation\\uat.list.cmd.txt')
#print(r.read().replace('\n', ''))
t = r.read().split('\n')
for i in range(len(t)):
    if ('inherited' or 'device-group' or 'partition' or 'template' or 'traffic-group') in t[i]:
        t.pop(i)
        print(i,t[i])

In the above code, I get an index error at line 9: 'if ('inherited' or 'device-group'...etc.

I really don't understand why. How can my index be out of range if it's the perfect length by using len(t) as my range?

The goal is to pop any indexes from my list that contain any of those substrings. Thank you for any assistance!

Daerdemandt
  • 2,281
  • 18
  • 19
  • 1
    Line 9 is print(i,t[i]) correct? If so it makes sense you can no longer access the location because it's been popped out. Try printing before you do the pop. Should be good – FirebladeDan Jun 28 '16 at 15:21
  • Your `if` statement is semantically invalid `python` completely. It will select the first value that will evaluate to `True` and test if it is in your list instead of checking if any are in it – m_callens Jun 28 '16 at 15:23
  • 3
    Do you realise that `('inherited' or 'device-group' or 'partition' or 'template' or 'traffic-group') == 'inherited'`. If you want to know whether any are in `t[i]`, use `any`. – jonrsharpe Jun 28 '16 at 15:23
  • Yes, as @jonrsharpe and I have stated, your `if` statement is not doing what you think it is – m_callens Jun 28 '16 at 15:24
  • @jonrsharpe What is the syntax to use `any`? – Christian de la Peña Jun 28 '16 at 15:33
  • @ChristiandelaPeña Python has extensive documentation, I'd suggest you start there. – jonrsharpe Jun 28 '16 at 15:34
  • 1
    This question is **not** a duplicate of [this](http://stackoverflow.com/questions/15112125/how-do-i-test-one-variable-against-multiple-values) one. Iterating through list while also doing something that modifies its length is the problem here, it's right in the title. – Daerdemandt Jun 28 '16 at 15:46
  • @Daerdemandt which i am sure is not original as well but i am with you on this one. ☺ – Ma0 Jun 28 '16 at 15:52
  • @Daerdemandt the OP has several problems, all of which are duplicates. – jonrsharpe Jun 28 '16 at 15:58

3 Answers3

0

This happens because you are editing the list while looping through it, you first get the length which is 10 for example, then you loop through the thing 10 times. but as soon as you've deleted one thing the list will only be 9 long. A way around this is to create a new list of things you want to keep and use that one instead.

I've slightly edited your code and done something similar.

t = ['inherited', 'cookies', 'device-group']

interesing_things = []
for i in t:
    if i not in ['inherited', 'device-group', 'partition', 'template', 'traffic-group']:
        interesing_things.append(i)
        print(i)
Steven Stip
  • 387
  • 3
  • 11
0

As many people said in the comments, there are several problems with your code.

The or operator sees the values on its left and right as booleans and returns the first one that is True (from left to right). So your parenthesis evaluates to 'inherited' since any non-empty string is True. As a result, even if your for loop was working, you would be popping elements that are equal to 'inherited' only.

The for loop is not working though. That happens because the size of the list you are iterating over is changing as you loop through and you will get an index-out-of-range error if an element of the list is actually equal to 'inherited' and gets popped.

So, take a look at this:

import os
os.chdir('G:\\f5_automation')
r = open('G:\\f5_automation\\uat.list.cmd.txt')
print(r.read().replace('\n', ''))
t = r.read().split('\n')
t_dupl = t[:]
for i, items in enumerate(t_dupl):
    if items in ['inherited', 'device-group', 'partition', 'template', 'traffic-group']:
        print(i, items)
        t.remove(items)

By duplicating the original list, we can use its items as a "pool" of items to pick from and modify the list we are actually interested in.

Finally, know that the pop() method returns the item it removes from the list and this is something you do not need in your example. remove() works just fine for you.


As a side note, you can probably replace your first 5 lines of code with this:

with open('G:\\f5_automation\\uat.list.cmd.txt', 'r') as r:
    t = r.readlines()

the advantage of using the with statement is that it automatically handles the closing of the file by itself when the reading is done. Finally, instead of reading the whole file and splitting it on linebreaks, you can just use the built-in readlines() method which does exactly that.

Ma0
  • 15,057
  • 4
  • 35
  • 65
  • This didn't work for me. I think it's because 'inherited', 'device-group',etc. are substrings of list objects which are also strings. Here is an example: `print(t[3]) ; inherited-traffic-group true` note the whitespace before 'inherited' – Christian de la Peña Jun 28 '16 at 16:02
  • @ChristiandelaPeña to do this properly you have to provide a sample of the txt you are reading. otherwise we are both waisting our time. i should have asked for it before writing all that. – Ma0 Jun 28 '16 at 16:13
0

Let's say len(t) == 5.

We'll process i taking values [0,1,2,3,4]

After we process i = 0, we pop one value from t. len(t) == 4 now. This would mean error if we get to i = 4. However, we're still going to try to go up to 4 because our range is already inited to be up to 4.

Next (i = 1) step ensures an error on i = 3.

Next (i = 2) step ensures an error on i = 2, but that is already processed.

Next (i = 3) step yields an error.

Instead, you should do something like this:

while t:
    element = t.pop()
    print(element)

On a side note, you should replace that in check with sets:

qualities_we_need = {'inherited', 'device-group', 'partition'} # put all your qualities here

And then in loop:

if qualities_we_need & set(element):
    print(element)

If you need indexes you could either use one more variable to keep track of index of value we're currently processing, or use enumerate()

Daerdemandt
  • 2,281
  • 18
  • 19