Varsa listedeki bir öğeyi nasıl silebilirim?


260

new_tagForm metin alanından self.response.get("new_tag")ve selected_tagsonay kutusu alanlarından alıyorum

self.response.get_all("selected_tags")

Onları şu şekilde birleştiriyorum:

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

( f1.striplistlistedeki dizelerin içindeki beyaz boşlukları ayıran bir işlevdir.)

Ama durumda tag_listboş (yeni etiketler girilir) ancak bazı vardır selected_tags, new_tag_listboş bir dize içerir " ".

Örneğin logging.info:

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

Boş dizeden nasıl kurtulabilirim?

Listede boş bir dize varsa:

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

Ancak boş bir dize yoksa:

>>> 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"

Ancak bu şunları verir:

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

Bu neden oluyor ve nasıl çalışıyorum?

Yanıtlar:


720

1) Neredeyse İngilizce tarzı:

inOperatörü kullanarak varlığını test edin , ardından removeyöntemi uygulayın .

if thing in some_list: some_list.remove(thing)

removeYöntem sadece ilk geçtiği kaldıracaktır thingkullanabileceğiniz tüm oluşumları çıkarmak için, whileyerine if.

while thing in some_list: some_list.remove(thing)    
  • Yeterince basit, muhtemelen seçimim. Küçük listeler için (tek satırlara dayanamaz)

2) Ördek türü , EAFP tarzı:

Bu sürgün-ilk-soru-son-tavrı Python'da yaygındır. Nesnenin uygun olup olmadığını önceden test etmek yerine, sadece işlemi gerçekleştirin ve ilgili İstisnaları yakalayın:

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!")

Tabii ki, yukarıdaki örnekte yer alan maddeler hariç, sadece şüpheli bir mizah değil, tamamen gereksizdir (amaç, konsepte aşina olmayan insanlar için ördek tipini göstermekti).

Bir şeyin birden fazla tekrarlanmasını bekliyorsanız:

while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • bu özel kullanım durumu için biraz ayrıntılı, ancak Python'da çok deyimsel.
  • bu # 1'den daha iyi performans gösterir
  • PEP 463 , burada kullanışlı olacak basit kullanım dışında, deneme / kullanım için daha kısa bir sözdizimi önerdi, ancak onaylanmadı.

Ancak, contextlib'in suppress () contextmanager (python 3.4'te tanıtıldı) ile yukarıdaki kod basitleştirilebilir:

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

Yine, birden fazla şey olmasını bekliyorsanız:

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

3) fonksiyonel tarzı:

Yaklaşık 1993, Python var lambda, reduce(), filter()ve map(), bir nezaket Lisp onları cevapsız korsan ve gönderilen çalışma yamalar *. filterÖğeleri listeden kaldırmak için kullanabilirsiniz :

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

Durumunuz için yararlı olabilecek bir kısayol var: boş öğeleri filtrelemek istiyorsanız (aslında , sıfır, boş dizeler veya diğer boş koleksiyonlar bool(item) == Falsegibi öğeler None), ilk argüman olarak Yok'u iletebilirsiniz:

cleaned_list = filter(None, some_list)
  • [güncelleme] : Python 2.x içinde, eskisine filter(function, iterable)eşdeğerdi [item for item in iterable if function(item)](ya [item for item in iterable if item]da ilk argüman ise None); Python 3.x sürümünde, artık buna eşdeğerdir (item for item in iterable if function(item)). En küçük fark, bir listeyi döndürmek için kullanılan filtredir, şimdi bir jeneratör ifadesi gibi çalışır - bu sadece temizlenmiş listeyi yineliyor ve atarsanız sorun değil, ancak gerçekten bir listeye ihtiyacınız varsa, filter()aramayı kapsamalısınız ile list()yapıcısı.
  • * Bu Lispy aromalı yapılar Python'da biraz yabancı olarak kabul edilir. 2005 civarında, Guido düşme hakkında bile konuşuyordufilter - yoldaşlarla birlikte mapve reduce(henüz gitmediler , ancak yüksek dereceli fonksiyonları seviyorsanız bir göz atmaya değer functools modülüne reducetaşındılar ).

4) Matematiksel stil:

Liste kavrayışları , PEP 202 tarafından 2.0 sürümünde tanıtıldığından beri Python'da liste manipülasyonu için tercih edilen stil oldu . Bunun ardındaki mantık Liste comprehensions nerede durumlarda listeleri oluşturmak için daha kısa bir yol sağlamak olduğunu map()ve filter()ve / veya iç içe döngüler anda kullanılacaktır.

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

Jeneratör ifadeleri PEP 289 tarafından 2.4 sürümünde tanıtılmıştır . Bir jeneratör ifadesi, bellekte tam bir listenin oluşturulmasına gerçekten ihtiyaç duymadığınız (veya istemediğiniz) durumlar için daha iyidir - tıpkı öğeler üzerinde birer birer yineleme yapmak istediğinizde olduğu gibi. Yalnızca liste üzerinde yineleme yapıyorsanız, bir jeneratör ifadesini tembel olarak değerlendirilen bir liste kavraması olarak düşünebilirsiniz :

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

notlar

  1. !=yerine eşitsizlik operatörünü kullanmak isteyebilirsiniz is not( fark önemlidir )
  2. liste kopyasını ima eden yöntem eleştirmenleri için: popüler inanışın aksine, üreteç ifadeleri liste anlamalarından her zaman daha etkili değildir - lütfen şikayet etmeden önce profil oluşturun

3
(2) 'de AttributeError işlemesini atlamayı önerebilir miyim? Dikkat dağıtıcıdır ve diğer bölümlerde (veya aynı bölümün diğer bölümlerinde) ele alınmamıştır. Daha da kötüsü, birisi bu kodu istisnaları aşırı agresif bir şekilde bastırdıklarını fark etmeden kopyalayabilir. Orijinal soru bir liste varsayar, cevap da olmalıdır.
Jason R. Coombs

1
Süper kapsamlı cevap! "Stil" ile farklı bölümlere ayrılması harika. Teşekkürler!
halloleo

En hızlı hangisi?
Sheshank S.12

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

Bunun, boş dizenin yalnızca bir örneğini listenizden kaldıracağını unutmayın (kodunuzda olduğu gibi). Listeniz birden fazla içerebilir mi?


5

Eğer indexdize aranır bulamazsa, bu atar ValueErrorsen görme. Ya ValueError'ı yakalayın:

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

veyafind bu durumda -1 döndüren kullanın .

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

Find () bir liste özelliği midir? Ben alıyorum:>>> 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'
Zeynel

2
Time Pietscker'ın remove()yaklaşımı çok daha doğrudan: doğrudan kodun ne anlama geldiğini gösterir (gerçekten bir ara endekse gerek yoktur i).
Eric O Lebigot

1
@Zeynel hayır, her Python'da olmalı, bkz. Docs.python.org/library/string.html#string.find . Ancak EOL'nin işaret ettiği gibi, sadece kaldırmayı kullanmak daha iyi olur.
phihag

4

Bu cevabı tamlık için eklemekle birlikte, sadece belirli koşullar altında kullanılabilir.

Çok büyük listeleriniz varsa, listenin sonundan çıkarılması, listeyi memmoveyeniden sipariş edebileceğiniz durumlar için CPython içlerinin zorunlu olmasını önler . Listenin sonundan kaldırmak için bir performans kazancı verir, çünkü kaldırdıktan sonraki memmove her öğeye bir adım geri gerek kalmaz (1) .
Bir kerelik kaldırmalar için performans farkı kabul edilebilir, ancak büyük bir listeniz varsa ve birçok öğeyi kaldırmanız gerekiyorsa - büyük olasılıkla bir performans isabeti fark edeceksiniz.

Kuşkusuz, bu gibi durumlarda, öğeler çoğunlukla listenin önünde yer almadıkça, tam liste araması yapmak da bir performans darboğazı olacaktır.

Bu yöntem
, listenin yeniden sıralanması kabul edilebilir olduğu sürece daha verimli bir şekilde kaldırmak için kullanılabilir . (2)

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

itemListede bulunmadığında bir hata oluşturmaktan kaçınmak isteyebilirsiniz .

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. Bunu CPython ile test ederken, diğer Python uygulamalarının çoğu / büyük olasılıkla listeleri dahili olarak saklamak için bir dizi kullanır. Dolayısıyla, verimli liste yeniden boyutlandırma için tasarlanmış gelişmiş bir veri yapısı kullanmadığı sürece, muhtemelen aynı performans özelliklerine sahiptirler.

Bunu test etmenin basit bir yolu, listenin önünden kaldırmayla son öğeyi kaldırarak hız farkını karşılaştırın:

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

İle:

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

(ikinci örneğin CPython ve PyPy ile daha hızlı olduğu bir büyüklük hızı farkı sırası verir).

  1. Bu durumda set, özellikle liste kopyaları saklamak istemiyorsa, a kullanmayı düşünebilirsiniz .
    Uygulamada, a eklenemeyen değiştirilebilir verileri depolamanız gerekebilir set. Ayrıca verinin sipariş edilip edilemeyeceğini btree'nin kontrol edin.

3

Eek, karmaşık bir şey yapmayın :)

Sadece filter()etiketleriniz. boş dizeler için bool()döner False,

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

yazmalısın

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

ya da daha iyisi, bu mantığı striplist()en başta boş dizeleri döndürmemek için içine yerleştirin.


Teşekkürler! Bütün iyi cevaplar ama sanırım bunu kullanacağım. Bu benim striplistfonksiyonum, çözümünüzü nasıl dahil edebilirim: def striplist (l): "" "l" "" return (x in l için [x.strip ()] listesindeki dizelerden boşlukları ayırır
Zeynel

1
@Zeynel: tabi. Ya böyle Listenizi anlama içine bir test bırakabilir: [x.strip() for x in l if x.strip()]ya kullanmak Python en yerleşik içinde mapve filterbunun gibi işlevleri: filter(bool, map(str.strip, l)). Bunu test etmek istiyorsanız, interaktif tercüman bu değerlendirir: filter(bool, map(str.strip, [' a', 'b ', ' c ', '', ' '])).
dfichter

Filtrenin bu durum için bir kısayolu vardır (öğeyi Boole bağlamında değerlendirmek için): ilk bağımsız değişken Noneyerine kullanmak boolyeterlidir.
Paulo Scardine

2

İşte oraya atmak için başka bir tek katmanlı yaklaşım:

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

Liste kopyası oluşturmaz, listeden birden fazla geçiş yapmaz, ek istisna işleme gerektirmez ve eşleşen nesneyi veya eşleşme yoksa Yok'u döndürür. Tek sorun, uzun bir açıklama yapmasıdır.

Genel olarak, istisnaları atmayan tek satırlı bir çözüm ararken, next () yolu, çünkü varsayılan bir argümanı destekleyen birkaç Python işlevinden biridir.


1

Tek yapmanız gereken bu

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

ancak bu yöntemin bir sorunu vardır. Bunu buldum dışında bu yerde bir şey koymak zorunda:

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

3
Yerleşik listenin üzerine yazmamalısınız . Ve ikinci pasajda bir dizeye dönüştürmeye gerek yoktur.
Robert Caspary
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.