Python dizisinden öğeleri kaldırmak için zarif bir yolu?

oy
51

Python içinde kod yazıyorum, ben sık sık bir liste veya bazı kriterlere dayalı diğer sıra şeklindeki öğeleri kaldırmak gerekir. Ben şu anda kötü yineleme olan bir listeden öğeleri kaldırarak, zarif ve verimli bir çözüm bulamadı. Örneğin, bunu yapamaz:

for name in names:
    if name[-5:] == 'Smith':
        names.remove(name)

Genellikle böyle bir şey yapıyor sonunda:

toremove = []
for name in names:
    if name[-5:] == 'Smith':
        toremove.append(name)
for name in toremove:
    names.remove(name)
del toremove

Bu, innefficient oldukça çirkin ve muhtemelen hatalı olduğunu (birden çok 'John Smith' girdileri nasıl oluyor?). Herkes daha zarif bir çözüm, ya da en azından daha verimli bir yönetim var mı?

Nasıl sözlükleri ile çalışır dersin?

Oluştur 20/08/2008 saat 16:41
kaynak kullanıcı
Diğer dillerde...                            


14 cevaplar

oy
-2

Eh, bu açıkça kullandığınız veri yapısı ile bir konudur. örneğin bir hashtable kullanın. biri en yeni eleman kapalı pop veya hepsini kaldırmak ya Bazı uygulamalar, anahtar başına birden fazla giriş destekler.

Ama bu, ve ne çözüm bulmak için gidiyoruz farklı bir veri yapısı sayesinde zerafet, Algoritma değil, olduğunu. Belki de sıralanabilir falan ama listede yineleme burada tek yöntemdir eğer daha iyisini yapabiliriz.

düzenleme: bir o verimlilik 'istedi ... Tüm bu önerilen yöntemler sadece onun önerdi aynıdır listede, yineleme fark etmez.

Cevap 20/08/2008 saat 16:46
kaynak kullanıcı

oy
2
names = filter(lambda x: x[-5:] != "Smith", names);
Cevap 20/08/2008 saat 16:48
kaynak kullanıcı

oy
3

Filtre bunun için harika olurdu. Basit bir örnek:

names = ['mike', 'dave', 'jim']
filter(lambda x: x != 'mike', names)
['dave', 'jim']

Düzenleme: Corey liste anlama çok harika.

Cevap 20/08/2008 saat 16:49
kaynak kullanıcı

oy
10

Kullanılması bir liste anlama

list = [x for x in list if x[-5:] != "smith"]
Cevap 20/08/2008 saat 16:49
kaynak kullanıcı

oy
53

Sadece filtreleme gerçekleştirmek için iki kolay yolu vardır:

  1. Kullanımı filter:

    names = filter(lambda name: name[-5:] != "Smith", names)

  2. Liste comprehensions kullanma:

    names = [name for name in names if name[-5:] != "Smith"]

Her iki olguda yüklem işlevi değerlendirdiği değerlerini tutmak unutmayın TrueEğer mantığı ters zorunda, (yani soyadı olan kişileri kaldırmak yerine" nin "soyadını Smith yok insanları tutmak" demek Sağlam ").

Düzenleme Komik ... iki kişi bireysel olarak ben kendiminkini gönderim gibi cevap önerilerini her iki yayınlanmıştır.

Cevap 20/08/2008 saat 16:50
kaynak kullanıcı

oy
2

Her iki çözüm de, filtre ve anlama yeni bir liste oluşturma gerektirir. Emin olmak için Python iç yapıları yeterince bilmiyorum ama bence daha geleneksel (ama daha az zarif) bir yaklaşım daha verimli olabileceğini:

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

Neyse, kısa listelerin, daha önce teklif edilen iki çözümlerden birini sopa ile.

Cevap 20/08/2008 saat 17:20
kaynak kullanıcı

oy
1

Filtre ve liste comprehensions senin örneğin Tamam, ama birkaç sorun vardır:

  • Bunlar listesinin bir kopyasını yapmak ve yeni bir tane dönün ve orijinal liste gerçekten büyük olduğunda verimsiz olacaktır
  • daha karmaşıktır veya birkaç koşulları vardır: kriterlerinin (== 'Smith' adı [-5] eğer, sizin durumunuzda) öğelerini almak için zaman onlar gerçekten külfetli olabilir.

Orijinal çözüm biz daha çirkin olduğuna katılacaktır bile aslında çok büyük listeler için daha verimlidir. Eğer birden 'John Smith' olabilir endişe Ama eğer pozisyona değil değerine dayalı silme düzeltilebilir:

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

Biz listenin boyutunu dikkate almadan bir çözüm seçemiyorum, ama büyük listeleri için ben filtre ya da listeler comprehensions yerine 2-pass çözümü tercih ediyorum

Cevap 02/10/2008 saat 17:44
kaynak kullanıcı

oy
4

çalışmıyor (filtre veya bir liste anlama kullanarak ya) filtreleme zamanlar vardır. diğer bazı nesne düzenlediğiniz listesine bir referansa sahip olan ve yerinde listesinde değişiklik gerektiğinde bu gerçekleşir.

for name in names[:]:
    if name[-5:] == 'Smith':
        names.remove(name)

Orijinal kodu tek farkı kullanılmasıdır names[:]yerine namesdöngüsü içinde. Bu şekilde listenin bir (sığ) kopyalama ve kaldırma çalışmaları üzerinde kod dolaşır beklendiği gibi. Liste kopyalama sığ olduğu için, oldukça hızlı.

Cevap 05/10/2008 saat 10:48
kaynak kullanıcı

oy
2

Sözlükleri ile çalışma hakkında sorunuza gelince, Python 3.0 içerecektir unutmamalıdır dict comprehensions :

>>> {i : chr(65+i) for i in range(4)}

Bu süre içinde, bir yarı-dict anlama bu şekilde yapabilirsiniz:

>>> dict([(i, chr(65+i)) for i in range(4)])

Ya daha doğrudan bir cevap olarak:

dict([(key, name) for key, name in some_dictionary.iteritems if name[-5:] != 'Smith'])
Cevap 07/10/2008 saat 13:33
kaynak kullanıcı

oy
37

Ayrıca liste üzerinde geriye yineleme yapabilirsiniz:

for name in reversed(names):
    if name[-5:] == 'Smith':
        names.remove(name)

Bu (gibi yeni bir liste oluşturmaz avantajı filterbir liste anlama ya) ve bir liste kopyası (gibi yerine bir yineleme kullanır [:]).

geriye yineleme güvenli iken elemanlarının alınmasına rağmen, yerleştirmeden biraz daha zordur unutmayın.

Cevap 08/10/2008 saat 00:24
kaynak kullanıcı

oy
1

bir dizi halinde.

toRemove = set([])  
for item in mySet:  
    if item is unwelcome:  
        toRemove.add(item)  
mySets = mySet - toRemove 
Cevap 07/12/2009 saat 03:01
kaynak kullanıcı

oy
28

açık cevap John ve birkaç kişi yani verdi biridir:

>>> names = [name for name in names if name[-5:] != "Smith"]       # <-- slower

Ama bu oldukça orijinal nesneyi yeniden yerine, yeni bir liste nesnesi oluşturur dezavantajı vardır. Bazı profil ve deneme, ben ise ile geldi en verimli yöntem yaptı:

>>> names[:] = (name for name in names if name[-5:] != "Smith")    # <-- faster

için atama "[:] adlarıyla" temelde anlamına gelen "şu değere sahip isimleri listenin içeriğini değiştirmek". Bu sadece yeni bir liste nesnesi yaratmaz o, adlarına atama farklı. için sağ taraftaki jeneratör ekspresyon (yerine köşeli parantez daha parantez kullanımına dikkat) 'dir. Bu listeyi yineleme için Python neden olur.

Bazı hızlı profilleme bu liste anlama yaklaşımından daha yaklaşık% 30 daha hızlı ve filtre yaklaşımından daha yaklaşık% 40 daha hızlı olduğunu göstermektedir.

Uyarı : Bu çözüm bariz çözümü daha hızlı iken, daha karanlık olduğunu ve daha gelişmiş Python tekniklerini kullanır. Kullanmaya yaparsanız, ben bir açıklama ile beraberindeki önerilir. Bu gerçekten (ne olursa olsun oldukça hızlı olan) bu özel operasyon performansı önem durumlarda kullanarak muhtemelen sadece değer. (Bu kullanılan durumda, A * kiriş arama yaparak ve arama kiriş arama noktaları kaldırmak için bu kullanılmıştır.)

Cevap 09/01/2011 saat 13:41
kaynak kullanıcı

oy
2

ilgili hesaplama karmaşıklığı O olduğu, uygun olabilir, liste yerinde filtre edilmelidir ve liste boyutu, daha sonra list.remove () dayalıdır önceki cevaplar, oldukça büyük bahsedilen algoritma ise (n ^ 2) . Bu durumda aşağıdaki no-öylesine pythonic işlevini kullanabilirsiniz:

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

Düzenleme: Aslında en çözelti https://stackoverflow.com/a/4639748/274937 maden çözümü daha üstündür. Daha pythonic ve daha hızlı çalışır. Yani, burada yeni bir filter_inplace () uygulamasıdır:

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)]
Cevap 02/04/2012 saat 15:20
kaynak kullanıcı

oy
1

İşte benim olduğu filter_inplaceyerinde bir listeden öğeleri filtrelemek için kullanılabilecek uygulanması, bağımsız olarak bu sayfayı bulmadan başıma bu geldi. Bu PabloG yayınlanmıştır ne sadece yerinde listelerini filtrelemek için kullanabilirsiniz böylece daha genel yapılmış aynı algoritma, aynı zamanda dayalı listeden kaldırmak mümkün comparisonFuncayarlanırsa ters eğer True; ters filtrenin bir tür-of eğer olacak.

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
Cevap 15/03/2013 saat 12:12
kaynak kullanıcı

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more