2013-04-01 2 views
22

Если у меня есть список карт костюмы в произвольном порядке, как так:Быстрый способ вернуть список без определенного элемента в Python

suits = ["h", "c", "d", "s"] 

и я хочу, чтобы вернуть список без 'c'

noclubs = ["h", "d", "s"] 

есть простой способ сделать это?

+0

Есть ли способ сделать это без изменения исходного списка? – AsheKetchum

ответ

25
>>> suits = ["h","c", "d", "s"] 
>>> noclubs = list(suits) 
>>> noclubs.remove("c") 
>>> noclubs 
['h', 'd', 's'] 

Если вам не нужен отдельный noclubs

>>> suits = ["h","c", "d", "s"] 
>>> suits.remove("c") 
24
suits = ["h","c", "d", "s"] 

noclubs = [x for x in suits if x != "c"] 
+0

Я не могу солгать ... Я надеялся, что что-то будет без петли. Это намного короче и интуитивно понятнее на таких языках, как R. –

+0

Я бы избегал этого решения, как мне кажется, совершенно неоптимизированным. Использование .remove() - IMHO намного быстрее. –

+0

@ VisgeanSkeloru Я бы не согласился с тем, что '.remove()' намного быстрее ". Я написал ответ ниже, чтобы ответить на это (его трудно форматировать в окне комментариев). – alukach

0

Если это важно, что вы хотите удалить конкретный элемент (в отличие от просто фильтрации), вы» ll хотите что-то близкое к следующему:

noclubs = [x for i, x in enumerate(suits) if i != suits.index('c')] 

Вы также можете рассмотреть используя set здесь, чтобы быть более семантически правильным, если действительно ваша проблема связана с игральными картами.

+1

Обратите внимание, что этот ответ вычисляет только индекс ** первого ** появления '' c'' и делает это для каждого элемента в исходном списке. Поэтому, если оригинал содержит несколько удаляемых '' c'', то функция не будет работать. И даже если он содержит только один, он будет медленным. Лучше сравнить только значение 'if x! = 'C''. – MSeifert

+0

@MSeifert, я считаю, что это было моим ответом, чтобы создать новый список с удаленным элементом * specific *, а не фильтровать все, что соответствует некоторому предикату. Я согласен, что это может быть более эффективным. –

3

Если заказ не вопроса, операция набора может быть использован:

suits = ["h", "c", "d", "s"] 
noclubs = list(set(suits) - set(["c"])) 
# note no order guarantee, the following is the result here: 
# noclubs -> ['h', 's', 'd'] 
3

вы можете использовать фильтр (или IFilter из itertools)

suits = ["h","c", "d", "s"] 
noclubs = filter(lambda i: i!='c', suits) 

Вы также можете отфильтровать список с помощью строительство

suits = ["h","c", "d", "s"] 
noclubs = [ i for i in suits if i!='c' ] 
18

Этот вопрос был дан ответ, но я wan чтобы использовать комментарий, который использует понимание списков намного медленнее, чем использование .remove().

Некоторые профили с моей машины (с использованием Python 2.7.6).

%%timeit 
x = ['a', 'b', 'c', 'd'] 
y = x[:] # fastest way to copy 
y.remove('c') 

1000000 loops, best of 3: 405 ns per loop 

%%timeit 
x = ['a', 'b', 'c', 'd'] 
y = list(x) # not as fast copy 
y.remove('c') 

1000000 loops, best of 3: 689 ns per loop 

%%timeit 
x = ['a', 'b', 'c', 'd'] 
y = [n for n in x if n != 'c'] # list comprehension 

1000000 loops, best of 3: 544 ns per loop 

Если вы используете самый быстрый способ скопировать список (который не очень читаемый), то будет около 36% быстрее, чем при использовании списка понимания. Но если вы скопируете список, используя класс list() (что гораздо более распространено и Pythonic), то вы будете на 26% медленнее, чем использование списка.

Действительно, все довольно быстро. Я думаю, что можно аргументировать, что .remove() более читабельна, чем перечислить технику понимания списка, но это не обязательно быстрее, если вы не заинтересованы в отказе от чтения в дублировании.

Большое преимущество понимания списка в этом сценарии состоит в том, что оно гораздо более кратким (т. Е. Если у вас была функция, которая по какой-то причине удаляла элемент из данного списка, это можно сделать в 1 строке, в то время как другой метод потребует 3 строки.) Бывают моменты, когда однострочные могут быть очень удобными (хотя они обычно приходят за счет некоторой удобочитаемости). Кроме того, использование понимания списка превосходит в том случае, если вы действительно не знаете, действительно ли элемент, который нужно удалить, в самом начале списка. Пока .remove() выкинет ValueError, понимание списка будет действовать, как ожидалось.

+3

Кроме того, обратите внимание, что решение по распознаванию списка удалит все символы «c», а remove() удалит только первый. – Kresimir

2

Без использования для петель или функций лямбда и сохранение порядка:

suits = ["h","c", "d", "s"] 
noclubs = suits[:suits.index("c")]+suits[suits.index("c")+1:] 

Я знаю, что внутренне он будет по-прежнему использовать циклы, но, по крайней мере, вы не должны использовать их извне.

0

К сожалению, по умолчанию, похоже, не встроено в Python.

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

Реализация:

def without(iterable, remove_indices): 
    """ 
    Returns an iterable for a collection or iterable, which returns all items except the specified indices. 
    """ 
    if not hasattr(remove_indices, '__iter__'): 
     remove_indices = {remove_indices} 
    else: 
     remove_indices = set(remove_indices) 
    for k, item in enumerate(iterable): 
     if k in remove_indices: 
      continue 
     yield item 

Использование:

li = list(range(5)) 
without(li, 3)    
# <generator object without at 0x7f6343b7c150> 
list(without(li, (0, 2))) 
# [1, 3, 4] 
list(without(li, 3))  
# [0, 1, 2, 4] 

Так это является генератором - вам нужно позвонить list или что-то сделать его постоянным.

Если вы хотите удалить только один индекс, вы можете сделать его еще быстрее, используя k == remove_index вместо набора.

0

Одним из возможных вариантов было бы использовать filter:

>>> import operator 
>>> import functools 

>>> suits = ["h", "c", "d", "s"] 

>>> # Python 3.x 
>>> list(filter(functools.partial(operator.ne, 'c'), suits)) 
['h', 'd', 's'] 

>>> # Python 2.x 
>>> filter(functools.partial(operator.ne, 'c'), suits) 
['h', 'd', 's'] 

Вместо partial можно также использовать __ne__ метод 'c' здесь:

>>> list(filter('c'.__ne__, suits)) 
['h', 'd', 's'] 

Однако последний подход не считается очень pythonic (обычно вам не следует использовать специальные методы - начиная с двойных подчеркиваний - напрямую), и он может дает странные результаты, если t он содержит смешанные типы, но он может быть немного быстрее, чем подход partial.

suits = ["h", "c", "d", "s"]*200 # more elements for more stable timings 
%timeit list(filter('c'.__ne__, suits)) 
# 164 µs ± 5.98 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 
%timeit list(filter(functools.partial(operator.ne, 'c'), suits)) 
# 337 µs ± 13.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 
%timeit list(filter(lambda x: x != 'c', suits)) 
# 410 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 
%timeit [x for x in suits if x != "c"] 
181 µs ± 465 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

Python 3.5.2 протестирована с IPythons волшебной %timeit команды.

Смежные вопросы