0

So, I was messing around removing non-integers from a list in Python (very exciting stuff). I came across an issue with the following code that I cannot seem to resolve:

list_a = ["a", 1, 2, 3, False, [1, 2, 3]]

for item in list_a:
    if not isinstance(item, int):
        list_a.remove(item)

I'm trying to remove all non int items in the list but the the above code does not remove False.

What I want: [1, 2, 3]

What I'm getting: [1, 2, 3, False]

Any help figuring this out would be much appreciated!

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253

6 Answers6

5

Your issue is that bool is a subclass of int:

>>> issubclass(bool, int)
True

Therefore, all bools are ints (where False is 0 and True is 1) and could be used for indexing and such. You can test type(item) is bool instead.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26
4

In Python, bool is a subclass of int:

>>> isinstance(True, int)
True
>>> isinstance(True, bool)
True

This means that you need a more refined check. You could, for example, check that type(item) == int.

For background, see PEP 285:

    6) Should bool inherit from int?

    => Yes.

       In an ideal world, bool might be better implemented as a
       separate integer type that knows how to perform mixed-mode
       arithmetic.  However, inheriting bool from int eases the
       implementation enormously (in part since all C code that calls
       PyInt_Check() will continue to work -- this returns true for
       subclasses of int).  Also, I believe this is right in terms of
       substitutability: code that requires an int can be fed a bool
       and it will behave the same as 0 or 1.  Code that requires a
       bool may not work when it is given an int; for example, 3 & 4
       is 0, but both 3 and 4 are true when considered as truth
       values.

Another, unrelated, issue is that you're modifying a list while iterating over it. Have a read of Modifying list while iterating and links therein.

This results in a subtle bug in your code. For example, it fails to remove "b" from the following list (try it!):

list_a = ["a", "b", 1, 2, 3]

for item in list_a:
    if not isinstance(item, int):
        list_a.remove(item)

One clean way to fix this is by using a list comprehension.

Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012
3

Check not if an item is an instance of something. Check its type directly:

[x for x in list_a if type(x)==int]

[1, 2, 3]

DYZ
  • 55,249
  • 10
  • 64
  • 93
1

You are making two errors here:

  • Assuming isinstance works with bool (others have explained how bool actually sub-classes from int).
  • Altering a list while you iterate through it

This may be one of those cases where explicitly testing using type(val) == int is warranted:

list_a = ["a", 1, 2, 3, False, [1, 2, 3]]

for item in list_a[:]:
    if not type(item) == int:
        list_a.remove(item)

which yields the wanted result; notice how list_a is copied with [:] assuring we iterate over all its contents despite removing from it.

In a comprehension form, this is prettier:

res = [i for i in list_a if type(i) == int]
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
1

A clear way to do it.

list_a = ["a", 1, 2, 3, False, [1, 2, 3]]
list_b = []
for item in list_a:
    if isinstance(item, int) and not isinstance(item, bool):
        list_b.append(item)
print list_b

Output

[1, 2, 3]
Niklas Rosencrantz
  • 25,640
  • 75
  • 229
  • 424
0

Bool is a subclass of Int, furthermore type() and isinstance() should not be confused. This is because before boolean became its own class "1"s and "0"s represented True and False respectively.

As a result you should use type() due to inheritance.

def f(randomList):
    result = []
    for elem in randomList:
        if type(elem)==int:
            result.append(elem)
    return result

This is a non-destructive modified list because sometimes modifying a list you're iterating through can cause errors or be a pain at least.