Why's the index not updating on later iterations?

I’m making a little function that’s supposed to consolidate negative numbers and I’m having trouble with the indexes, here’s the relevant parts of the function:

  if str_amnt >= int_amnt:
    for i in lst:
      if f_op == False:
        if isinstance(i, str) == True:
          f_op = True
          f_op_ndx = lst.index(i)
          print(f_op_ndx)
          continue
      if f_op == True:
        if isinstance(i, int):
          f_op = False
          continue
        elif i == '-':
          if lst[f_op_ndx] != '-':
            neg_indx = lst.index(i)
            int_indx = neg_indx + 1
          elif lst[f_op_ndx] == '-':
            neg_indx = lst.index(i) + 1
            int_indx = neg_indx + 1

    numstr = str(lst[neg_indx]) + str(lst[int_indx])
    numint = numstr
    del lst[neg_indx:int_indx + 1]
    lst.insert(neg_indx, numint)
    return lst
  elif int_amnt > str_amnt:
    return lst

So the problem currently is if I input [1, '-', 1, '-', '-', 1] it returns [1, '-', '1-', '-', 1] instead of the desired output. By printing stuff out and stuff, I found that the variable f_op_ndx never changes index past the first iteration, I don’t understand why because when it gets to the 4th iteration of the for loop I think it should change the index to 3, but running the code keeps it stagnant at one. What’s happening?

As nobody else has had a crack at this, I’ll offer my 2 cents:

In line 10 of your code, you have…

if isinstance(i, int):

… which is at odds with every other if statement you have. Line 10 is implicit, where all the others are explicit, which is of course, better. So, that’s the first thing I’d check on, if I were you.

To be completely honest, I’m not 100% sure what you mean by this, but I think you mean that I don’t specify it to be
if isinstance(i, int) == True:
If this is what you mean, I went ahead and made that change to no difference in my output, but if you meant something else, please let me know and maybe dumb it down for me a little as well.

Okay, so it’s not that then.

To explain what I mean, consider this code:

x = 1
if x == True:
    print('Yes')

… which works the same as…

x = 1
if x:
    print('Yes')

The difference is that the first is explicit, that is it explicitly states if x == True whereas with the second, the == True is implied, or implicit.

From The Zen of Python, which is kind of my Bible.

Explicit is better than implicit.

So, your issue remains. I’ll have another look at your code and see if I can come up with anything else.

Is the full script too long to post?
Are you sure that the error is somewhere in that snippet?

For me personally it’s unclear what the objective is. What does “consolidate negative numbers” mean? In sample input there are no negative numbers.

I’ll take another look at you code to see why it’s not working as you expect, but as for this point:

The general consensus is that you want to use Python’s “Truthyness”, rather than checking for “True” or “False”, and in the rare case that you do want to check for the actual boolean value, you should use “is”, as they are singeltons.

So:

if isinstance(i, int):

is the Pythonic way to go.

Note, there are a number of other non-Python issues with this code – stay tuned.

Like @aivarpaalberg , I’m unclear what you are trying to do it – and the code is so convoluted, I can’t figure it out. Next time:

  1. provide a runnable sample, with everything initialized (e.g. f_op = True (or False? which is the initial condition?)

  2. Sometimes it’s hard to explain what you need in words, but at least give the expected result, so we’ll know.

  3. Maybe it’s discuss’ fault, but use FOUR spaces to indent – always!

I see two issues in your code right off:

for i in lst:
looping through a list while changing it in the loop is a bad idea – if you remove any elements, it will mess up the iteration. You probably want to loop through a copy of the list, and build up a new list the way you want it:

new_list = []
for i in lst[:]

and so on.

  1. f_op_ndx = lst.index(i) I think this is the actual bug – list.index(i) will return the index of the first occurrence of the value i – so that will always be 1 in the example you gave.

If you want to work with both the value of an item and its index, you want to use enumerate:

for idx, item in enumerate(lst[:]):

This code:

numstr = str(lst[neg_indx]) + str(lst[int_indx])
    numint = numstr
    del lst[neg_indx:int_indx + 1]
    lst.insert(neg_indx, numint)
    return lst

Is after the loop, so it’s only going to change one thing in the whole list – is that what you wanted?

Then there are LOTS of style issues here:

Use meaningful variable names:

  • Don’t use i as a name except maybe for an index.
  • Don’t use lst for a list of something – ideally, it would be named after what it is a list OF, not just that it’s a list.
  • I’d have an easier time reading this code if you’d used a longer name than f_op – I’m still not sure what that means.

Better not to use
if: ... continue
when you can use
if:...else...
it makes the control flow more clear.

I think you were trying to concatenate the minus sign onto the following number – if so, then this is how I’d write that:

lst = [1, '-', 1, '-', '-', 1]

print("original list:", lst)
# loop though the list, and make a new one with the new data
new_lst = []
neg = ""
for item in lst:
    if isinstance(item, int): # is this a number
        # add the neg sign and put it in the list
        new_lst.append(neg + str(item))
        neg = ""
    elif item == '-':
        if neg: # two neg signs in a row: put the previous one in the list
            new_lst.append(neg)
        neg = item

print("Final list:", new_lst)

which results in:

original list: [1, '-', 1, '-', '-', 1]
Final list: ['1', '-1', '-', '-1']

Thank you for the suggestions. I’ll try to clear up some stuff for you.

  1. Am I changing the list while it’s looping through? I’m not doubting you but since I’m not appending or deleting anything from the list inside the loop, I’m a bit lost.

  2. So once a variable is assigned in a for loop, it can’t be reassigned to a different value in later iterations of that for loop?

  3. As far as the code:

numstr = str(lst[neg_indx]) + str(lst[int_indx])
    numint = numstr
    del lst[neg_indx:int_indx + 1]
    lst.insert(neg_indx, numint)
    return lst

I intended it to put the negative int that it’s supposed to find into a string, then have it converted to an int, then have it replace the correct values in the list. I don’t know if that explanation helped at all, but if it does, that’s what I intended it to do so yes, it was supposed to change one thing in the whole list. At some point (when it works properly) I planned to make it recursive, so it could keep changing things into negatives until there were no negatives left.

  1. Regarding the styling issues, I’ll be sure to do my best to correct them in future code. I am still a bit new to python and whatnot, so I’ll be sure to use your suggestions. But regarding the f_op variable, it’s supposed to stand for “First Operation” as in the first operation (I.E. +, -, *, etc.) it comes across. I didn’t want to make it too long of a variable for convenience sake, but I’ll be sure to make a change.

  2. I see how my original explanation of “consolidating” negatives was a bit confusing, so I apologize for that. The function is a part of a calculator program I’m making and the point of this function is to recognize when there was an intended negative, put the negative sign and the int together, and then reinsert it into the correct spot of the list. So 1- -1 would be [1, '-', -1] in a list rather than [1, '-', '-', 1]

Onto a runnable sample, I’ll provide one below:

def join_neg(lst):
  f_op = False
  lst_two = lst.copy()
  str_amnt, int_amnt = 0, 0
  while '(' in lst_two:
    lst_two.remove('(')
  while ')' in lst_two:
    lst_two.remove(')')
  for i in lst_two:
    if isinstance(i, str) == True:
      str_amnt += 1
    elif isinstance(i, int) == True:
      int_amnt += 1

  if str_amnt >= int_amnt:
    for i in lst:
      if f_op == False:
        if isinstance(i, str) == True:
          f_op = True
          f_op_ndx = lst.index(i)
          print(f_op_ndx)
          continue
      if f_op == True:
        if isinstance(i, int) == True:
          f_op = False
          continue
        elif i == '-':
          if lst[f_op_ndx] != '-':
            neg_indx = lst.index(i)
            int_indx = neg_indx + 1
          elif lst[f_op_ndx] == '-':
            neg_indx = lst.index(i) + 1
            int_indx = neg_indx + 1

    numstr = str(lst[neg_indx]) + str(lst[int_indx])
    numint = numstr
    del lst[neg_indx:int_indx + 1]
    lst.insert(neg_indx, numint)
    return lst
  elif int_amnt > str_amnt:
    return lst

I’ll go ahead and provide an explanation to the new bit of the function now: what it does is it finds how many strings and integers are in the list, the program uses this information to determine whether or not it should run it through the for loop again (for when it’s recursive). Basically I reasoned that an arithmetic equation is always going to have more integers than operations, so if it detects more operations, than it must be a negative that hasn’t been paired up with it’s integer. Although this method could be completely wrong, so if it is, please let me know.

if isinstance(i, int) == True:

No no no!!! Please don’t write code like that. This just shows that the writer of the code doesn’t understand evaluation of boolean flags.

Every beginner probably starts off writing code like that. I know I did. But it is unnecessary and pointless. Testing flag == True is completely redundant. The flag is already usable as true or false, so you can write:

if isinstance(i, int):
    ...

The if already tests if the flag is true. Putting in a second test:

if isinstance(i, int) == True:

is just extra work for no reason. And since that second test itself evaluates to either true or false, you need to test that too:

if (isinstance(i, int) == True) == True:

But that’s still a true or false flag, and if you didn’t trust the first two, how can you trust this one? So you need:

if ((isinstance(i, int) == True) == True) == True:

if (((isinstance(i, int) == True) == True) == True) == True:

if ((((isinstance(i, int) == True) == True) == True) == True) == True:
# Help me! How do I stop???

This is, of course, silly. None of those == True tests are needed. You stop the infinite series of tests by not starting them:

# This is the way.
if isinstance(i, int):

Experts may object that, if you have a three-way flag, you may need the extra test:

Maybe = 2
if flag == True:
    ...
elif flag == Maybe:
    ...
else:
    # flag isn't true or maybe, so it must be false.

Sure. But that’s an unusual case and it doesn’t apply to any of the flags in your code sample.

It is hard to tell what it going on in this function, since half of the variables aren’t defined, and their names are cryptic and obfuscated.

“f_op”? “i” for the content of a list? str_amnt???

We don’t even know what is inside the list, except for one example input:

[1, '-', 1, '-', '-', 1]

but I have no idea what that input means or what output it should provide.

I have no idea what the function is supposed to be doing. All I know is that it isn’t doing it.

Another problem: with just two space indents, it isn’t as clear where the blocks line up. It’s easy for the eye to drift a space or two, and much easier with four spaces.

Why does the list contain both ints and strings? Is that a hard requirement? We can simplify the code if we don’t have to keep jumping back and forth between “is it a string? is it an int?”

Half the battle is picking the right input values. If possible, your code will probably be much, much simpler if the input was (say) one of these:

[1, -1, 1, -1, -1, 1]
['1', '-', '1', '-', '-', '1']

“I found that the variable f_op_ndx never changes index past the first iteration”

You have this line of code to set the value of f_op_ndx:

f_op_ndx = lst.index(i)

The index method returns the index of the first matching value.

So if you have lst = [1, 1, 1, 1, 1, 1], and loop six times, each time you call lst.index(i) you get the same index 0 returned.

Let’s fix this :slight_smile:

You are trying to iterate over the input list, and you need two values each iteration:

  • the value from inside the list;

  • and the index of that value.

Let’s do it the right way, using enumerate(). (And also some better, more descriptive names.)

for index, value in enumerate(lst):

For your input [1, '-', 1, '-', '-', 1] that will iterate over the pairs:

index = 0,  value = 1
index = 1,  value = '-'
index = 2,  value = 1
index = 3,  value = '-'
index = 4,  value = '-'
index = 5,  value = 1

You shouldn’t really berate the guy for doing something that I suggested. In my defense, with a casual look over the code, I thought that maybe he’d meant to put (and missed out) if isinstance(i, int) == False:

numstr = str(lst[neg_indx]) + str(lst[int_indx])
numint = numstr

numstr is clearly a string: you concatenate two strings together, the result is still a string.

The second line, numint = numstr, is still a string. Just because you call the variable “numint” doesn’t make its value an int.

debts = 1000000
profits = debts  # And now I am rich!!!

If only it were that easy :slight_smile:

Regarding the naming of the variables, I really just simplified stuff (and in this case oversimplified) for my own use, which is evidently not a good move. “str_amnt” refers to “string amount”, “f_op” (which I admit is a very bad name) is for “first operation” and so forth. Secondly, the program as a whole is a calculator, so when you input 1 - 1 - -1 for example, it doesn’t recognize the -1 value as it’s own integer which may be a problem on my part. Thirdly, the list is composed of integers and strings because the program will eventually have to solve the equation into a single number, and it can’t add two strings together to make a new value arithmetically, but you already knew that. So what it does with a different function is it uses a stack to find an operation, then do that operation to the integer before and after it, so they need to be integers for it to work properly later on (unless you have a different suggestion).

Also regarding the two space indents: That is a problem with the text editor that I use. I use an online editor called “replit” because I can’t download, say, pycharm or Visual Studios on that computer. On my personal computer, I use one of the two where the two space indents aren’t an issue.

Yes this is true, what would be in the final function is it would say numint = int(numstr) to convert it to an integer, but it was throwing an error, so I changed it to what it is to spot what the error, which brought me here.