Подтвердить что ты не робот

Быстрый способ возврата списка без определенного элемента в Python

Если у меня есть список карточных костюмов в произвольном порядке:

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

На этот вопрос ответили, но я хотел ответить на комментарий, что использование понимания списка намного медленнее, чем использование .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

%%timeit
x = ['a', 'b', 'c', 'd']
i = x.index('c')
y = x[:i] + x[i + 1:]

1000000 loops, best of 3: 656 ns per loop

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

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

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

Ответ 4

Если порядок не имеет значения, можно использовать операцию установки:

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']

Ответ 5

вы можете использовать фильтр (или ifilter from 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' ]

Ответ 6

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

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

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

Ответ 7

Одна из возможностей заключается в использовании 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 (обычно вы не должны использовать специальные методы - начиная с двойных подчеркиваний - напрямую), и это может дать странные результаты, если список содержит смешанные типы, но он может быть немного быстрее, чем подход 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 проверен с помощью команды magicythation %timeit с помощью IPythons.

Ответ 8

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

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

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

Ответ 9

По-видимому, похоже, что это не встроено в 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 вместо набора.

Ответ 10

Один из способов сделать это неразрушающим, это использовать наборы:

suits = set(["h", "c", "d", "s"])
for s in suits:
    print suits - set([s])
... 
set(['c', 's', 'd'])
set(['h', 's', 'd'])
set(['h', 'c', 'd'])
set(['h', 'c', 's'])