Chodzi o to, że mutujesz listę podczas jej iterowania, co zawsze prowadzi do nieintuicyjnego zachowania - dlatego powinno się tego unikać.
Konkretniej: remove() usuwa pierwsze wystąpienie argumentu w liście. Jak dodasz print()a na początku...
for el in list:
print(el)
if el == 2.0:
print("usuwam!")
list.remove(el)
To zobaczysz coś takiego:
# dla [2.0, 2.0, 1.0, 1.45, 2.0]
2.0
usuwam!
1.0
1.45
2.0
usuwam!
# dla [2.0, 1.0, 1.45, 2.0]
2.0
usuwam!
1.45
2.0
usuwam!
Zauważ, że po usunięciu, następna iteracja przeskakuje jeden element - jest tak, bo przez usunięcie indeksy przesunęło się w lewo. Dlatego w pierwszym przypadku przeskakujesz jedno 2.0, więc remove() wywołuje się 2 razy mimo że są 3 elementy.
Natomiast ogólnie to co napisałeś jest... dziwne ;) Bo iterujesz się po liście i sprawdzasz czy ma elementy, ale remove() też się iteruje po liście żeby znaleźć pierwszy element do usunięcia. Więc wychodzi z tego takie przekombinowane masło maślane o kwadratowej złożoności obliczeniowej.
Zamiast tego, można prościej zapisać (zmienilem nazwe zmiennej na `tab` bo `list` to wbudowana funkcja):
while 2.0 in tab:
tab.remove(2.0)
Ale jeszcze lepiej (unika podwójnej iteracji po liście) jest użyć składni list comprehension lub ew filter():
tab = [e for e in tab if e != 2.0]
# lub:
tab = list(filter(lambda e: e != 2.0, tab))