Неожиданный вывод при фильтрации списка Python: что я делаю не так? - PullRequest
1 голос
/ 26 ноября 2010

Я пытаюсь отфильтровать список, вот код:

 test=['aaa','bbb','ccc','ddd','footer','header']

 def rm_hf(x): return x != 'footer'

 filter(rm_hf,test)

Результаты:

>>>['aaa','bbb','ccc','ddd','header']

Ожидаемый результат, поиск «нижнего колонтитула» в списке иудалите его.

Теперь я хочу удалить оба заголовка и нижний колонтитул, поэтому я делаю это:

 test2=['aaa','bbb','ccc','ddd','footer','header']

 def rm_hf2(x): return x != 'footer' or x != 'header'

 filter(rm_hf2,test2)

Результаты:

>>>['aaa','bbb','ccc','ddd','footer','header']

Теперь этостранно, он просто выдает оба «нижний колонтитул», «заголовок» вместо того, чтобы их фильтровать?

Что я сделал не так?Я думаю, что моя логика верна ...

Ответы [ 4 ]

7 голосов
/ 26 ноября 2010

Ваша логика верна, потому что вы думаете как человек.Ваш компьютер не делает.Он читает каждый элемент из вашего списка, а затем наталкивается на «нижний колонтитул».«Отличается ли нижний колонтитул от нижнего колонтитула?», Говорит он.«НЕТ! Это та же строка! Она оценивается как ложная. Посмотрим следующее условие»."Разве нижний колонтитул отличается от верхнего? ДА!"Следовательно, условие False or True, которое, очевидно, имеет значение true.

Вы хотите and, а не or:

def rm_hf2(x): return x != 'footer' and x != 'header'

.in ключевое слово, которое более читабельно:

def rm_hf2(x): return x not in ('footer', 'header')

Важно, чтобы вы понимали, что на самом деле происходит с "и" и "или", хотя.И давайте будем честными: если что-то работает не так, как вы думаете, проблема, скорее всего, заключается в вашем собственном коде, а не в самом языке Python.

4 голосов
/ 26 ноября 2010

моя логика верна

На самом деле, нет, это не так, как отмечено в других ответах.

Более точный способ достижения желаемого результата - использовать список пониманий , , а именно :

test = ['aaa', 'bbb', 'ccc', 'ddd', 'footer', 'header']
undesirable = ['footer', 'header']
[_ for _ in test if _ not in undesirable]

С документация :

Обратите внимание, что filter(function, iterable) эквивалентно [item for item in iterable if function(item)], если function не равно None, и [item for item in iterable if item], если function равно None.

Тем не менее, нет времени, как подарок, чтобы освежить вашу булеву логику !

Если бы вы проводили модульное тестирование своего кода, вы быстро обнаружили бы, что ваша вторая функция фильтрации не выполняет то, что вы ожидаете. Вот упрощенный пример:

$ cat 4281875.py 
#!/usr/bin/env python

import unittest

def rm_hf2(x): return x != 'footer' or x != 'header'

class test_rm_hft(unittest.TestCase):

    def test_aaa_is_not_filtered(self):
        self.assertTrue(rm_hf2('aaa'))

    def test_footer_is_filtered_out(self):
        self.assertFalse(rm_hf2('footer'))


if __name__ == '__main__':
    unittest.main()


$ ./4281875.py 
.F
======================================================================
FAIL: test_footer_is_filtered_out (__main__.test_rm_hft)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./4281875.py", line 13, in test_footer_is_filtered_out
    self.assertFalse(rm_hf2('footer'))
AssertionError

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)
2 голосов
/ 26 ноября 2010

То, что сказали все остальные, плюс:

Если у вас есть несколько элементов, которые вы хотите исключить, используйте set вместо цепочки and s или tuple:

# do once
blacklist = set(['header', 'footer'])

# as needed
filter(lambda x: x not in blacklist, some_iterable)

Обоснование: для просмотра tuple требуется время, пропорциональное положению найденного предмета; Отказ занимает то же время, что и последний элемент. Поиск элемента в наборе занимает одинаковое время для всех элементов и для неудачи. Наборы обычно выигрывают за большое количество предметов. Все зависит от вероятности поиска каждого предмета и вероятности отказа. Кортежи могут выиграть даже с большой коллекцией, когда высока вероятность нескольких предметов (они должны быть размещены в передней части кортежа) и низкая вероятность провала.

1 голос
/ 26 ноября 2010

Вы также можете использовать понимание списка вместо фильтра.

test = ['aaa','bbb','ccc','ddd','footer','header']
filtered_test = [x for x in test if x not in ('footer', 'header')]

или выражение генератора (в зависимости от ваших потребностей)

test = ['aaa','bbb','ccc','ddd','footer','header']
filtered_test = (x for x in test if x not in ('footer', 'header'))
...