목록에서 항목을 삭제하는 방법은 무엇입니까?


258

나는 점점 오전 new_tag과 양식 텍스트 필드에서 self.response.get("new_tag")selected_tags와 체크 박스 필드에서

self.response.get_all("selected_tags")

나는 이것을 다음과 같이 결합합니다.

tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

( f1.striplist목록에서 문자열 내부의 공백을 제거하는 기능입니다.)

그러나 경우에 tag_list비어 (새로운 태그가 입력되지 않습니다)하지만 일부가 selected_tags, new_tag_list빈 문자열을 포함합니다 " ".

예를 들면 다음과 logging.info같습니다.

new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

빈 문자열을 어떻게 제거합니까?

목록에 빈 문자열이있는 경우 :

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

그러나 빈 문자열이 없으면 :

>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
        i = s.index("")
        del s[i]
    else:
        print "new_tag_list has no empty string"

그러나 이것은 다음을 제공합니다.

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    if new_tag_list.index(""):
        ValueError: list.index(x): x not in list

왜 이런 일이 발생하며 어떻게 해결합니까?

답변:


718

1) 거의 영어 스타일 :

in연산자를 사용하여 존재 여부를 테스트 한 다음 remove방법 을 적용하십시오 .

if thing in some_list: some_list.remove(thing)

remove방법은 대신 thing사용할 수있는 모든 항목을 제거하기 위해 첫 번째 항목 만 제거합니다 .whileif

while thing in some_list: some_list.remove(thing)    
  • 충분히 간단하고 아마도 내 선택. 작은 목록 (한 줄짜리 저항 할 수 없음)

2) 오리 형 , EAFP 스타일 :

이 첫 번째 질문은 마지막 태도는 파이썬에서 일반적입니다. 물체가 적합한 지 미리 테스트하는 대신 작업을 수행하고 관련 예외를 포착하십시오.

try:
    some_list.remove(thing)
except ValueError:
    pass # or scream: thing not in some_list!
except AttributeError:
    call_security("some_list not quacking like a list!")

물론 위의 예에서 두 번째 예외 조항은 의심스러운 유머뿐만 아니라 완전히 불필요합니다 (요점은 개념에 익숙하지 않은 사람들에게 오리 타이핑을 설명하는 것이 었습니다).

여러 번 나타날 것으로 예상되는 경우 :

while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • 이 특정 사용 사례에 대해서는 조금 장황하지만 파이썬에서는 매우 관용적입니다.
  • 이것은 # 1보다 잘 수행됩니다.
  • PEP 463 은 여기서는 편리한 간단한 사용법을 제외하고 시도하기위한 더 짧은 구문을 제안했지만 승인되지 않았습니다.

그러나 contextlib의 suppress () contextmanager (파이썬 3.4에서 도입)를 사용하면 위 코드를 다음과 같이 단순화 할 수 있습니다.

with suppress(ValueError, AttributeError):
    some_list.remove(thing)

다시 말하지만 여러 번 나타날 것으로 예상되는 경우 :

with suppress(ValueError):
    while True:
        some_list.remove(thing)

3) 기능적 스타일 :

약 1993 파이썬 가지고 lambda, reduce(), filter()map()하는 의례 리스프 를 놓친 해커 및 제출 작업 패치 *. filter목록에서 요소를 제거 하는 데 사용할 수 있습니다 .

is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)

빈 항목 (여기서 0, 빈 문자열 또는 다른 빈 컬렉션 bool(item) == False과 같은 항목)을 필터링하려는 경우 None첫 번째 인수로 없음을 전달할 수 있습니다.

cleaned_list = filter(None, some_list)
  • [update] : Python 2.x에서는 이전 filter(function, iterable)과 같 [item for item in iterable if function(item)]거나 [item for item in iterable if item]첫 번째 인수가이면 None; Python 3.x에서는 이제와 같습니다 (item for item in iterable if function(item)). 미묘한 차이점은 필터가 목록을 반환하는 데 사용된다는 것입니다. 이제는 생성기 표현식처럼 작동합니다. 정리 된 목록을 반복하고 삭제하는 경우에만 괜찮지 만 목록이 실제로 필요한 경우 filter()호출 을 묶어야합니다. list()생성자 와 함께 .
  • *이 Lispy 맛 구조는 Python에서 약간 외계인으로 간주됩니다. 2005 년쯤 귀도는 낙하에 대해 이야기하고있었습니다.filter 동료들 map과 함께 하고 있었고 reduce(아직 사라지지 않았지만 functools 모듈 reduce로 옮겨졌습니다 .이 기능은 고차 함수 를 좋아한다면 볼만한 가치가 있습니다 ).

4) 수학적 스타일 :

목록 이해PEP 202에 의해 버전 2.0에서 도입 된 이후 파이썬에서리스트 조작에 선호되는 스타일이되었습니다 . 그 뒤에 근거는 목록 함축 어디 상황에서 목록을 만들 수있는 더 간결한 방식으로 제공하는 것입니다 map()filter()및 / 또는 중첩 루프가 현재 사용되는 것입니다.

cleaned_list = [ x for x in some_list if x is not thing ]

제네레이터 표현식은 버전 2.4에서 PEP 289는 . 생성자 표현식은 한 번에 하나씩 요소를 반복하려는 경우와 같이 메모리에 전체 목록을 만들 필요가 없거나 원하지 않는 상황에 더 좋습니다. 목록을 반복하는 경우 생성기 표현식을 지연된 평가 된 목록 이해 로 생각할 수 있습니다 .

for item in (x for x in some_list if x is not thing):
    do_your_thing_with(item)

노트

  1. 부등식 연산자를 사용할 수 있습니다 !=대신is not ( 차이가 중요합니다 ).
  2. 리스트 카피를 암시하는 방법에 대한 비평가들 : 대중적 신념과는 달리, 생성기 표현이리스트 이해보다 항상 효율적인 것은 아닙니다. 불평하기 전에 프로파일 링하십시오

3
(2)에서 AttributeError 처리를 생략 할 것을 제안 할 수 있습니까? 다른 섹션 (또는 동일한 섹션의 다른 부분)에서 산만하고 처리되지 않습니다. 더 나쁜 것은 누군가가 지나치게 공격적으로 예외를 억제하고 있다는 사실을 깨닫지 못하고 해당 코드를 복사 할 수 있다는 것입니다. 원래 질문은 목록을 가정하고 답변도해야합니다.
Jason R. Coombs

1
매우 포괄적 인 답변! "스타일"에 의해 다른 섹션으로 나누는 것이 좋습니다. 감사!
halloleo

어느 것이 가장 빠릅니까?
Sheshank S.

12
try:
    s.remove("")
except ValueError:
    print "new_tag_list has no empty string"

이렇게하면 코드에서와 같이 목록에서 빈 문자열의 인스턴스 하나만 제거됩니다. 목록에 둘 이상을 포함 할 수 있습니까?


5

경우 index(가) 문자열을 검색 찾을 수없는, 그것은 던졌습니다 ValueError당신에게있는 거 보는합니다. ValueError를 포착하십시오.

try:
    i = s.index("")
    del s[i]
except ValueError:
    print "new_tag_list has no empty string"

또는find 이 경우 -1을 반환하는을 사용하십시오 .

i = s.find("")
if i >= 0:
    del s[i]
else:
    print "new_tag_list has no empty string"

find ()가 목록 속성입니까? >>> s [u'Hello', u'Cool', u'Glam'] >>> i = s.find("") Traceback (most recent call last): File "<pyshell#42>", line 1, in <module> i = s.find("") AttributeError: 'list' object has no attribute 'find'
다음을

2
Time Pietscker의 remove()접근 방식은 훨씬 더 직접적입니다. 코드가 수행하려는 작업을 직접 보여줍니다 (실제로 중간 색인이 필요하지 않음 i).
Eric O Lebigot

1
@Zeynel 아니오, 모든 Python에 있어야합니다 ( docs.python.org/library/string.html#string.find 참조) . 그러나 EOL이 지적했듯이 단순히 remove를 사용하는 것이 좋습니다.
phihag

4

완전성을 위해이 답변을 추가하지만 특정 조건에서만 사용할 수 있습니다.

목록이 매우 큰 경우 목록의 끝에서 제거하면 목록 memmove을 다시 정렬 할 수있는 상황에서 CPython 내부 에서해야 할 일이 없습니다. 목록의 끝에서 제거하면 성능이 향상됩니다. 제거 후 한 단계 후에 memmove 모든 항목이 필요하지 않기 때문입니다 (1) .
일회성 제거의 경우 성능 차이가 허용 될 수 있지만 큰 목록이 있고 많은 항목을 제거해야하는 경우 성능이 저하 될 수 있습니다.

물론,이 경우 전체 목록 검색을 수행하면 항목이 목록의 맨 앞에 있지 않는 한 성능 병목 현상이 발생할 수 있습니다.

이 방법은
목록을 다시 정렬하는 것이 허용되는 한보다 효율적인 제거를 위해 사용될 수 있습니다 . (2)

def remove_unordered(ls, item):
    i = ls.index(item)
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()

item가 목록에 없을 때 오류가 발생하지 않도록 할 수 있습니다 .

def remove_unordered_test(ls, item):
    try:
        i = ls.index(item)
    except ValueError:
        return False
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()
    return True

  1. CPython으로 이것을 테스트했지만 대부분의 다른 모든 Python 구현은 배열을 사용하여 목록을 내부적으로 저장합니다. 따라서 효율적인 목록 크기 조정을 위해 설계된 정교한 데이터 구조를 사용하지 않으면 동일한 성능 특성을 가질 수 있습니다.

이것을 테스트하는 간단한 방법은 목록 앞면에서 제거하는 것과 속도 요소를 마지막 요소 제거와 비교하십시오.

python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'

와:

python -m timeit 'a = [0] * 100000' 'while a: a.pop()'

(CPython 및 PyPy를 사용하여 두 번째 예가 더 빠른 경우 차수의 속도 차이를 나타냅니다).

  1. 이 경우 set특히 목록이 중복을 저장하지 않는 경우을 사용하는 것이 좋습니다.
    실제로에 추가 할 수없는 가변 데이터를 저장해야 할 수도 있습니다 set. 또한 데이터를 주문할 수 있는지 btree를 확인하십시오.

3

Eek, 복잡한 일을하지 마십시오 :)

filter()태그 만 빈 문자열을 bool()반환 False하므로

new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

당신은 작성해야합니다

new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))

또는 더 나은 방법으로,이 논리를 내부에 넣으면 striplist()처음에 빈 문자열이 반환되지 않습니다.


감사! 모든 좋은 대답이지만 나는 이것을 사용할 것이라고 생각합니다. 이것은 내 striplist함수이며 솔루션을 어떻게 통합합니까? def striplist (l) : "" "목록의 문자열에서 공백을 제거합니다." ""return ([x.strip () for x in l])
Zeynel

1
@Zeynel : 물론입니다. 당신도이 같은 목록의 이해 내부 테스트를 둘 수 있었다 : [x.strip() for x in l if x.strip()]또는 사용 파이썬의 내장에서 mapfilter같은 기능 : filter(bool, map(str.strip, l)). 테스트하려면 대화식 인터프리터에서이를 평가하십시오 filter(bool, map(str.strip, [' a', 'b ', ' c ', '', ' '])).
dfichter

필터에는이 경우에 대한 바로 가기가 있습니다 (부울 컨텍스트에서 요소 평가). 첫 번째 인수 None대신 대신 사용 bool하면 충분합니다.
Paulo Scardine

2

여기에 버리는 또 다른 한 줄짜리 접근법이 있습니다.

next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)

목록 복사본을 만들지 않고 목록을 여러 번 통과하지 않고 추가 예외 처리가 필요하지 않으며 일치하는 개체를 반환하거나 일치하지 않는 경우 없음을 반환합니다. 유일한 문제는 그것이 긴 진술을한다는 것입니다.

일반적으로 예외를 발생시키지 않는 한 줄짜리 솔루션을 찾을 때 next ()는 기본 인수를 지원하는 몇 가지 Python 함수 중 하나이기 때문에 갈 길입니다.


1

당신이해야 할 일은 이것입니다

list = ["a", "b", "c"]
    try:
        list.remove("a")
    except:
        print("meow")

그러나 그 방법에는 문제가 있습니다. 내가 이것을 발견했을 때 제외 장소에 무언가를 넣어야합니다.

list = ["a", "b", "c"]
if "a" in str(list):
    list.remove("a")

3
내장 목록을 덮어 쓰면 안됩니다 . 그리고 두 번째 스 니펫에서는 문자열로 변환 할 필요가 없습니다.
Robert Caspary 2012 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.