Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Элегантный способ удаления элементов из последовательности в Python?
Когда я пишу код в Python, мне часто нужно удалить элементы из списка или другого типа последовательности на основе некоторых критериев. Я не нашел решения, которое было бы элегантным и эффективным, так как удаление элементов из списка, который вы сейчас просматриваете, плохо. Например, вы не можете этого сделать:
for name in names:
if name[-5:] == 'Smith':
names.remove(name)
Я обычно заканчиваю тем, что делаю что-то вроде этого:
toremove = []
for name in names:
if name[-5:] == 'Smith':
toremove.append(name)
for name in toremove:
names.remove(name)
del toremove
Это неэффективно, довольно уродливо и, возможно, глючно (как он обрабатывает несколько записей 'John Smith'?). Есть ли у кого-нибудь более элегантное решение или, по крайней мере, более эффективное?
Как насчет того, что работает со словарями?
Есть два простых способа выполнить только фильтрацию:
Использование
filter:names = filter(lambda name: name[-5:] != "Smith", names)Используя списочные включения :
names = [name for name in names if name[-5:] != "Smith"]
Обратите внимание , что оба случая сохраняют значения, для которых функция предиката вычисляет значение True, поэтому вам нужно изменить логику (т. е. вы говорите "keep the people who do not have the last name Smith" вместо "удалить людей, у которых есть фамилия Смит").
Редактировать Смешно... два человека индивидуально разместили оба ответа, которые я предложил, когда я размещал свой.
Кроме того, вы можете выполнять обратные итерации по списку:
for name in reversed(names):
if name[-5:] == 'Smith':
names.remove(name)
Это имеет то преимущество, что он не создает новый список (например, filter или понимание списка) и использует итератор вместо копии списка (например, [:] ).
Обратите внимание, что хотя удаление элементов во время итерации назад безопасно, вставка их несколько сложнее.
Очевидный ответ-Это тот, который дал Джон и еще несколько человек, а именно::
>>> names = [name for name in names if name[-5:] != "Smith"] # <-- slower
Но это имеет тот недостаток, что он создает новый объект списка, а не повторно использует исходный объект. Я провел некоторые профилирования и эксперименты, и самый эффективный метод, который я придумал, - это:
>>> names[:] = (name for name in names if name[-5:] != "Smith") # <-- faster
Присвоение "names[:]" в основном означает "replace the contents of the names list with the following value". Он отличается от простого присвоения имен тем, что не создает новый объект списка. Правая сторона присваивания является генераторным выражением (обратите внимание на использование круглых скобок, а не квадратных скобок). Это приведет к тому, что Python будет повторяться по всему списку.
Некоторые быстрые профилирования предполагают, что это примерно на 30% быстрее, чем подход к пониманию списка, и примерно на 40% быстрее, чем подход к фильтру.
Предостережение: хотя это решение быстрее, чем очевидное решение, оно более неясно и опирается на более продвинутые методы Python. Если вы используете его, я рекомендую сопроводить его комментарием. Это, вероятно, стоит использовать только в тех случаях, когда вы действительно заботитесь о выполнении этой конкретной операции (которая довольно быстра, несмотря ни на что). (В случае, когда я использовал это, я выполнял поиск по лучу A* и использовал это для удаления точек поиска из поискового луча.)
Бывают случаи, когда фильтрация (либо с помощью фильтра, либо с помощью понимания списка) не работает. Это происходит, когда какой-то другой объект содержит ссылку на изменяемый список, и вам нужно изменить список на месте.
for name in names[:]:
if name[-5:] == 'Smith':
names.remove(name)
Единственное отличие от исходного кода - это использование names[:] вместо names в цикле for. Таким образом, код повторяется над (неглубокой) копией списка, и удаления работают должным образом. Поскольку копирование списка неглубокое, это довольно быстро.
Оба решения, фильтр и понимание требуют построения нового списка. Я не знаю достаточно о внутреннем устройстве Python, чтобы быть уверенным, но я думаю , что более традиционный (но менее элегантный) подход может быть более эффективным:
names = ['Jones', 'Vai', 'Smith', 'Perez']
item = 0
while item <> len(names):
name = names [item]
if name=='Smith':
names.remove(name)
else:
item += 1
print names
Во всяком случае, для коротких списков я придерживаюсь любого из двух предложенных ранее решений.
Если список должен быть отфильтрован на месте и размер списка достаточно велик, то алгоритмы, упомянутые в предыдущих ответах, которые основаны на list.remove(), могут быть непригодны, поскольку их вычислительная сложность составляет O(n^2). В этом случае можно использовать следующие данные функции no-so :
def filter_inplace(func, original_list):
""" Filters the original_list in-place.
Removes elements from the original_list for which func() returns False.
Algrithm's computational complexity is O(N), where N is the size
of the original_list.
"""
# Compact the list in-place.
new_list_size = 0
for item in original_list:
if func(item):
original_list[new_list_size] = item
new_list_size += 1
# Remove trailing items from the list.
tail_size = len(original_list) - new_list_size
while tail_size:
original_list.pop()
tail_size -= 1
a = [1, 2, 3, 4, 5, 6, 7]
# Remove even numbers from a in-place.
filter_inplace(lambda x: x & 1, a)
# Prints [1, 3, 5, 7]
print a
Редактировать: На самом деле, решение на уровне https://stackoverflow.com/a/4639748/274937 превосходит мое решение. Он более пифонический и работает быстрее. Итак, вот новая реализация filter_inplace():
def filter_inplace(func, original_list):
""" Filters the original_list inplace.
Removes elements from the original_list for which function returns False.
Algrithm's computational complexity is O(N), where N is the size
of the original_list.
"""
original_list[:] = [item for item in original_list if func(item)]
Чтобы ответить на ваш вопрос о работе со словарями, обратите внимание, что Python 3.0 будет включать в себя диктанты :
>>> {i : chr(65+i) for i in range(4)}
В то же время, вы можете сделать квазидикт понимание таким образом:
>>> dict([(i, chr(65+i)) for i in range(4)])
Или как более прямой ответ:
dict([(key, name) for key, name in some_dictionary.iteritems if name[-5:] != 'Smith'])
Вот моя реализация filter_inplace , которая может быть использована для фильтрации элементов из списка на месте, я придумал это самостоятельно, прежде чем найти эту страницу. Это тот же алгоритм, что и то, что было опубликовано PabloG, просто сделано более общим, чтобы вы могли использовать его для фильтрации списков на месте, он также может удалить из списка на основе comparisonFunc , если реверсированный установлен True ; своего рода обратный фильтр, если вы хотите.
def filter_inplace(conditionFunc, list, reversed=False):
index = 0
while index < len(list):
item = list[index]
shouldRemove = not conditionFunc(item)
if reversed: shouldRemove = not shouldRemove
if shouldRemove:
list.remove(item)
else:
index += 1
Фильтр и список понятны для вашего примера, но у них есть пара проблем:
- Они делают копию вашего списка и возвращают новый, и это будет неэффективно, когда исходный список действительно большой
- Они могут быть действительно громоздкими, когда критерии выбора элементов (в вашем случае, если name[-5:] == 'Smith') более сложны или имеют несколько условий.
Ваше оригинальное решение на самом деле более эффективно для очень больших списков, даже если мы можем согласиться, что оно уродливее. Но если вы беспокоитесь, что у вас может быть несколько 'John Smith', это можно исправить, удалив на основе позиции, а не значения:
names = ['Jones', 'Vai', 'Smith', 'Perez', 'Smith']
toremove = []
for pos, name in enumerate(names):
if name[-5:] == 'Smith':
toremove.append(pos)
for pos in sorted(toremove, reverse=True):
del(names[pos])
print names
Мы не можем выбрать решение, не учитывая размер списка, но для больших списков я бы предпочел ваше 2-проходное решение вместо фильтра или списков понимания
Ну, это явно проблема со структурой данных, которую вы используете. Например, используйте хэш-таблицу. Некоторые реализации поддерживают несколько записей на ключ, поэтому можно либо удалить самый новый элемент, либо удалить их все.
Но это так, и то, что вы собираетесь найти решение, это элегантность через другую структуру данных, а не алгоритм. Может быть, вы можете сделать лучше, если он отсортирован или что-то еще, но итерация в списке-это ваш единственный метод здесь.
edit: один действительно понимает, что он просил 'efficiency'... все эти предложенные методы просто повторяют список, который совпадает с тем, что он предложил.