İlk öğeyi döndürmek için Python deyimi veya Yok


274

Eminim bunu yapmanın daha basit bir yolu var.

Bir liste döndüren bir sürü yöntem arıyorum. Liste boş olabilir. Liste boş değilse, ilk öğeyi döndürmek istiyorum; Aksi takdirde, Hiçbiri dönmek istiyorum. Bu kod çalışır:

my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None

Bana öyle geliyor ki bunu yapmak için tek satırlık basit bir deyim olmalı, ama benim için bunu düşünemiyorum. Var mı?

Düzenle:

Burada tek satırlık bir ifade arıyorum nedeni ben inanılmaz derecede terse kodu gibi değil, ama böyle bir çok kod yazmak zorunda çünkü:

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

Ne yapmak istiyorum kesinlikle bir işlev ile başarılabilir (ve muhtemelen olacak):

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

Soruyu yayınladım çünkü Python'daki basit ifadelerin neler yapabileceğine sık sık şaşırdım ve basit bir ifade varsa hile yapabilen bir işlev yazmanın aptalca bir şey olduğunu düşündüm. Bir işlev gibi Ancak bu cevapları görünce, öyle görünüyor olduğunu basit bir çözüm.


28
btw, Python 2.6 ve üzeri sürümlerde değil next(iter(your_list), None), first_item(your_list)varsaymak yerine kullanabilirsiniz ( ve her zaman yinelenebilir bir değer döndürmesi gerekir). your_listNoneget_first_list()get_second_list()
jfs

1
Sanırım demek istiyorsun next(iter(your_list)), çünkü ikinci bir argüman iterverirseniz, ona ilk argümanın çağrılabilir olduğunu söylüyorsunuz.
Robert Rossney

7
Hayır, Noneişte ikinci parametre next()değil iter(). next()yerine yetiştirme verilirse, ikinci parametre verir StopIteration, eğer your_listboş.
jfs

@JFSebastian tarafından önerilen çözüm yinelenen soru ile mevcut: stackoverflow.com/a/18533669/144408
shahjapan

Yanıtlar:


205

Python 2.6 ve üzeri

next(iter(your_list), None)

Eğer your_listolabilir None:

next(iter(your_list or []), None)

Python 2.4

def get_first(iterable, default=None):
    if iterable:
        for item in iterable:
            return item
    return default

Misal:

x = get_first(get_first_list())
if x:
    ...
y = get_first(get_second_list())
if y:
    ...

Başka bir seçenek yukarıdaki işlevi satır içi yapmaktır:

for x in get_first_list() or []:
    # process x
    break # process at most one item
for y in get_second_list() or []:
    # process y
    break

Bunu önlemek için breakyazabilirsiniz:

for x in yield_first(get_first_list()):
    x # process x
for y in yield_first(get_second_list()):
    y # process y

Nerede:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return

1
Bu son seçenek neredeyse tam olarak aradığım şey: açık, işe yarıyor ve yeni bir işlev tanımlamamı gerektirmiyor. Araya bir şekilde ihtiyaç duyulmamışsa "tam olarak" diyebilirim, çünkü ihmal etme riski önemsiz değildir. Fakat bu yaklaşım gerçeğin yüzüğüne sahiptir.
Robert Rossney

Oh, hiç hoşuma gitmedi. Listedeki herhangi bir öğe Yanlış değerini değerlendirirse, bu değer atılır ve değiştirilir. Listede boş bir dizeniz varsa "", bu atılır ve boş bir listeyle değiştirilir []. 0'ınız varsa, ile de değiştirilir []. Eğer varsa Falseorada da yerini aldı. Vb Belirli bir durumda bununla kurtulabilirsiniz, ancak bu gelişmesi kötü bir alışkanlıktır.
steveha

4
@steveha: bool(lst) Bize len(lst) > 0hangi öğelerin lstiçerdiği hakkında bir şey söylemediğini söyler mi , bool([False]) == True;bu nedenle ifade [False] or []geri döner [False], aynı şey [0] or [] geri döner [0].
jfs

@RobertRossney: İfadeden yield_first()kaçınmak için ekledim break.
jfs

1
@ThomasAhle: ikisi de doğru. Hiçbiri olmayan vaka için çözüm sağlamak için cevabı güncelledim.
jfs

218

En iyi yol şudur:

a = get_list()
return a[0] if a else None

Bunu bir satırda da yapabilirsiniz, ancak programcının okuması çok daha zordur:

return (get_list()[:1] or [None])[0]

Hata. Bu bir Python 2.4 uygulaması olduğunu söylemeliydim.
Robert Rossney

15
@Adam: gelecek kullanabilirnext(iter(your_list), None)
jfs

@JFSebastian aslında next(iter(your_list))yeterli
Brad Johnson

13
@BradleagheJohnson: yanlış. NoneOP'nin istediği gibi dönmek yerine StopIteration'ı yükseltir . Deneyin: next(iter([])).
jfs

72
(get_list() or [None])[0]

Bu işe yaramalı.

BTW Değişkeni kullanmadım list, çünkü bu yerleşik list()fonksiyonun üzerine yazıyor .

Düzenleme: Daha önce burada biraz daha basit, ama yanlış bir sürümü vardı.


4
Bu akıllı ve çalışır, ama efotinis tarafından önerildiği gibi "dönüş foo [0] Başka foo ise Hiçbiri" bakım için takip etmek biraz daha kolay olduğunu düşünüyorum.
Jay

3
Soru tek satırlık bir çözüm istiyordu, bu yüzden bunu dışladım.
özyinelemeli

Get_list () işlevi None değerini döndürürse bu ifadelerin hiçbiri çalışmaz; "TypeError: 'NoneType' nesnesinin aboneliği kaldırılamaz." veya "TypeError: +: 'NoneType' ve 'list' için desteklenmeyen işlenen türleri.
Robert Rossney

4
Soruda, yöntemlerin hiçbiri olmayan listeler döndürdüğü söyleniyor. Bu yüzden gereksinimler göz önüne alındığında, hala ikisinin de çalıştığına inanıyorum.
tekrarlayan

Get_list () artık abone olmadığı veya eklenmediği için None döndürüyorsa şimdi çalışmalıdır.
Robert

32

En python deyimsel yolu, liste yinelenebilir olduğundan bir yineleyicide next () yöntemini kullanmaktır . @JFSebastian'ın 13 Aralık 2011'deki yorumuna söylediği gibi.

next(iter(the_list), None)Boşsa Yok değerini döndürür the_list. bkz yanındaki () Python 2.6+

veya the_listboş olmadığından eminseniz :

iter(the_list).next()bkz. iterator.next () Python 2.2+


1
Bunun avantajı, listenizi listenin return l[0] if l else Nonelibcomp olması gibi bir değişkene atamak zorunda olmamanızdır. Liste aslında bir genexpr olduğunda, listenin tamamını oluşturmak zorunda olmadığınızdan bu daha da iyidirnext(iter(x for x in range(10) if x % 2 == 0), None)
RubenLaguna

Eğer the_listboş olmadığından emin olursanız, yazarsınız the_list[0].
kaya3

12

Kendinizi bir liste kavrayışından ilk şeyi (veya Hiçbiri) koparmaya çalışırken bulursanız, bunu yapmak için bir jeneratöre geçebilirsiniz:

next((x for x in blah if cond), None)

Pro: falan dizine eklenemiyorsa çalışır Con: bu alışılmadık bir sözdizimidir. Etrafında hackleme ve ipython'da bir şeyler filtrelerken faydalıdır.


11

OP'nin çözümü neredeyse orada, daha Pythonic yapmak için sadece birkaç şey var.

Birincisi, listenin uzunluğunu elde etmeye gerek yok. Python'daki boş listeler bir if denetiminde False olarak değerlendirilir. Sadece basitçe söyle

if list:

Ayrıca, ayrılmış kelimelerle çakışan değişkenlere atamak da çok kötü bir fikirdir. "list" Python'da ayrılmış bir kelimedir.

Öyleyse bunu

some_list = get_list()
if some_list:

Burada çok sayıda çözümün kaçırdığı gerçekten önemli bir nokta, tüm Python işlevlerinin / yöntemlerinin varsayılan olarak Yok değerini döndürmesidir . Aşağıdakileri deneyin.

def does_nothing():
    pass

foo = does_nothing()
print foo

Bir işlevi erken sonlandırmak için Yok'u döndürmeniz gerekmedikçe, açıkça Yok'u döndürmek gereksizdir. Oldukça kısa bir süre içinde, varsa, ilk girişi geri getirin.

some_list = get_list()
if some_list:
    return list[0]

Ve son olarak, belki de bu ima edildi, ancak sadece açık olmak için ( açık, örtükten daha iyi olduğu için ), işlevinizin listeyi başka bir işlevden almasını sağlamamalısınız; sadece parametre olarak iletin. Sonuç olarak,

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

Dediğim gibi OP neredeyse oradaydı ve sadece birkaç dokunuş aradığınız Python lezzetini verir.


1
Python'da neden 'liste' ayrılmış bir kelime? Ya da, daha da önemlisi, 'list' in python'da bir koruma sözcüğü olduğunu nereden söylüyor? Daha da önemlisi, 'list' in python'da ayrılmış bir kelime olduğunu nasıl doğruladınız? 'List' adlı bir değişkeni bildirebileceğim düşünüldüğünde, bu açıkça ayrılmış bir sözcük değildir.
Lasse V. Karlsen

2
Alınan nokta. Python'da gerçekten ayrılmış kelimeler burada bulunabilir tinyurl.com/424663 Ancak yerleşik işlevleri ve türleri ayrılmış olarak düşünmek iyi bir fikirdir. list () aslında bir liste oluşturur. Aynı şey
dikte

Son iki satırı birleştiririm, geçici değişkeni ortadan kaldırır (daha fazla my_list'e ihtiyacınız yoksa). Bu, okunabilirliği artırır.
Svante

Bu hemen hemen doğru yöneldiğim cevaptı.
Robert Rossney

1
get_first_itemkendisini varsayılan değer olarak Yok olan bir defaultparametre (like dict.get) kabul etmelidir . Böylece işlev arayüzü my_listboşsa ne olduğunu açıkça bildirir.
jfs

3
for item in get_list():
    return item

Bu özlü ve güzel.
Aralık'ta gotgenes

Trajik bir şekilde, işe yaramıyor; get_list () None döndürürse, "TypeError: 'NoneType' nesnesi yinelenemez" istisnası atar.
Robert Rossney

Düzeltmek için: "get_list () içindeki öğe için veya []:"
itsadok

5
Soru açıkça get_list'in bir liste döndürdüğünü varsayar. Sonuçta len ve getitem denir.
A. Coady

2

Açıkçası, daha iyi bir deyim olduğunu düşünmüyorum: açık ve net - "daha iyi" bir şeye gerek yok. Belki, ama bu gerçekten zevk meselesi, sen değiştirebilir if len(list) > 0:ileif list: False her zaman değerlendirecek boş listeye -.

İlgili bir notta, Python Perl değildir (cinas amaçlanmamıştır!), Mümkün olan en havalı kodu almanız gerekmez.
Aslında, Python gördüğüm en kötü kod da çok güzel :-) ve tamamen sürdürülemez.

Bu arada, burada gördüğüm çözümün çoğu, [0] listesi Yanlış olarak değerlendirildiğinde (örneğin boş dize veya sıfır) dikkate alınmaz - bu durumda, hepsi doğru öğeyi değil, Hiçbiri'ni döndürür.


Python'da yazmak if lst:yerine yazmak için iyi bir stil olarak kabul edilir if len(lst) > 0:. Ayrıca, değişken adları olarak Python anahtar sözcüklerini kullanmayın; gözyaşlarıyla biter. Bir liste için Lveya lstdeğişken adı olarak yaygın olarak kullanılır . Eminim bu durumda aslında listbir değişken adı olarak önermek istememiştiniz , sadece "bir liste" demek istediniz . Ve "lst [0] yanlış olarak değerlendirildiğinde" için +1.
steveha

Evet, daha iyi netlik için sadece aynı tür OP adını koruyordum. Ancak kesinlikle haklısınız: Python anahtar kelimelerini geçersiz kılmamaya her zaman dikkat edilmelidir.
soymak

2

İlk öğeyi döndürmek için Python deyimi veya Yok?

En Pythonic yaklaşım en çok oylanan cevabın gösterdiği şeydir ve soruyu okuduğumda aklıma ilk gelen şey buydu. Öncelikle, olası boş liste bir işleve geçirilirse, nasıl kullanılacağı aşağıda açıklanmıştır:

def get_first(l): 
    return l[0] if l else None

Ve liste bir get_listişlevden döndürülürse :

l = get_list()
return l[0] if l else None

Burada bunu açıklamanın diğer yolları, açıklamalarla

for

Bunu yapmanın akıllıca yollarını düşünmeye başladığımda, düşündüğüm ikinci şey bu:

for item in get_list():
    return item

Bu örtük dönen, burada fonksiyon uçlarını varsaymaktadır Noneeğer get_listgetiri boş bir liste. Aşağıdaki açık kod tam olarak eşdeğerdir:

for item in get_list():
    return item
return None

if some_list

Ayrıca örtük kullanan da önerildi (yanlış değişken adı düzeltildi) None. Bu, gerçekleşmeyebilecek bir yineleme yerine mantıksal denetimi kullandığı için yukarıdakilere tercih edilir. Bunun ne olduğunu hemen anlamak daha kolay olmalı. Ancak, okunabilirlik ve sürdürülebilirlik için yazıyorsak return None, sonuna açık olanı da eklemeliyiz :

some_list = get_list()
if some_list:
    return some_list[0]

dilim or [None]ve sıfırıncı dizin seçin

Bu aynı zamanda en çok oylanan cevapta:

return (get_list()[:1] or [None])[0]

Dilim gereksizdir ve bellekte fazladan bir öğe listesi oluşturur. Aşağıdakiler daha iyi performans göstermelidir. Açıklamak için or, ilk öğe Falseboole bağlamındaysa ikinci öğeyi döndürür , bu nedenle get_listboş bir liste döndürürse, parantez içinde yer alan ifade, daha sonra 0dizin tarafından erişilecek olan 'Yok' içeren bir liste döndürür :

return (get_list() or [None])[0]

Bir sonraki, ilk öğeyi Trueboole bağlamındaysa ve ikinci öğeyi döndürdüğü gerçeğini kullanır ve my_list'e iki kez başvurduğundan, üçlü ifadeden daha iyi değildir (ve teknik olarak bir astar değildir):

my_list = get_list() 
return (my_list and my_list[0]) or None

next

Sonra yerleşik nextve aşağıdaki akıllı kullanımı variter

return next(iter(get_list()), None)

Açıklamak için, iterbir .nextyöntemle bir yineleyici döndürür . ( .__next__Python 3'te) Daha sonra yerleşik nextbu .nextyöntemi çağırır ve yineleyici biterse, verdiğimiz varsayılanı döndürür None.

gereksiz üçlü ifade ( a if b else c) ve geri dönme

Aşağıdakiler önerilmiştir, ancak mantık genellikle negatif yerine pozitifte daha iyi anlaşıldığı için tersi tercih edilir. Yana get_listsonuç bir şekilde memoized sürece iki kez denir, bu kötü performans göstereceğini:

return None if not get_list() else get_list()[0]

Daha iyi ters:

return get_list()[0] if get_list() else None

Daha da iyisi, get_listyalnızca bir kez çağrılacak yerel bir değişken kullanın ve ilk önce önerilen Pythonic çözümüne sahip olursunuz:

l = get_list()
return l[0] if l else None

2

Deyimlerle ilgili olarak, bir itertools tarifi var nth.

Itertools tariflerinden:

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)

Tek gömlek istiyorsanız, bu tarifi sizin için uygulayan bir kütüphane kurmayı düşünün, örneğin more_itertools:

import more_itertools as mit

mit.nth([3, 2, 1], 0)
# 3

mit.nth([], 0)                                             # default is `None`
# None

Yalnızca ilk öğeyi döndüren başka bir araç kullanılabilir more_itertools.first.

mit.first([3, 2, 1])
# 3

mit.first([], default=None)
# None

Bu itertools, yalnızca listeler için değil, tüm yinelenebilirler için genel olarak ölçeklenir.



1

Meraktan iki çözüm üzerinde zamanlamalar yaptım. Bir for döngüsünü erken sona erdirmek için bir return ifadesi kullanan çözüm, Python 2.5.1 ile makinemde biraz daha pahalı, bunun yinelenebilir kurulumu ile ilgili olduğundan şüpheleniyorum.

import random
import timeit

def index_first_item(some_list):
    if some_list:
        return some_list[0]


def return_first_item(some_list):
    for item in some_list:
        return item


empty_lists = []
for i in range(10000):
    empty_lists.append([])

assert empty_lists[0] is not empty_lists[1]

full_lists = []
for i in range(10000):
    full_lists.append(list([random.random() for i in range(10)]))

mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)

if __name__ == '__main__':
    ENV = 'import firstitem'
    test_data = ('empty_lists', 'full_lists', 'mixed_lists')
    funcs = ('index_first_item', 'return_first_item')
    for data in test_data:
        print "%s:" % data
        for func in funcs:
            t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
                func, data), ENV)
            times = t.repeat()
            avg_time = sum(times) / len(times)
            print "  %s:" % func
            for time in times:
                print "    %f seconds" % time
            print "    %f seconds avg." % avg_time

Bunlar aldığım zamanlamalar:

empty_lists:
  index_first_item:
    0.748353 saniye
    0.741086 saniye
    0.741191 saniye
    0.743543 saniye ort.
  return_first_item:
    0.785511 saniye
    0.822178 saniye
    0.782846 saniye
    0.796845 saniye ort.
full_lists:
  index_first_item:
    0.762618 saniye
    0.788040 saniye
    0.786849 saniye
    0.779169 saniye ort.
  return_first_item:
    0.802735 saniye
    0.878706 saniye
    0.808781 saniye
    0.830074 saniye ort.
mixed_lists:
  index_first_item:
    0.791129 saniye
    0.743526 saniye
    0.744441 saniye
    0.759699 saniye ort.
  return_first_item:
    0.784801 saniye
    0.785146 saniye
    0.840193 saniye
    0.803380 saniye ort.

0
try:
    return a[0]
except IndexError:
    return None

1
Akış kontrolü için genellikle istisnalar kullanılmaz.
Matt Green

Bu kodu daha uzun yapma ve istisna işleme ekleyerek tam olarak aradığım deyim olduğunu sanmıyorum.
Robert Rossney

3
Ama bu yaygın bir Python deyimidir! Performans nedenleriyle kullanamazsınız; daha iyi performans elde edersiniz, if len(a) > 0:ancak bu iyi bir stil olarak kabul edilir. Bunun sorunu ne kadar iyi ifade ettiğine dikkat edin: "bir listenin başını çıkarmaya çalışın ve bu işe yaramazsa geri dönün None". Google "EAFP" için arama yapın veya buraya bakın: python.net/~goodger/projects/pycon/2007/idiomatic/…
steveha

@steveha Verilerinize bağlıdır. Len (a) genellikle> 0 ve len (a) <1 nadir bir durumsa, istisnayı yakalamak aslında daha performanslı olacaktır.
limscoder

@limscoder, sana katılıyorum. Performans nedenleriyle ne zaman kullanamayacağınızın ayrıntılarına girmedim; Bunu asla kullanmayacağınızı söylemedim.
steveha

0
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

print head(xrange(42, 1000)  # 42
print head([])               # None

BTW: Genel program akışınızı şu şekilde yeniden işlerdim:

lists = [
    ["first", "list"],
    ["second", "list"],
    ["third", "list"]
]

def do_something(element):
    if not element:
        return
    else:
        # do something
        pass

for li in lists:
    do_something(head(li))

(Mümkün olduğunda tekrardan kaçınma)


0

Buna ne dersin:

(my_list and my_list[0]) or None

Not: Bu, nesne listeleri için iyi çalışmalıdır, ancak aşağıdaki yorumlara göre sayı veya dize listesi olması durumunda yanlış yanıt verebilir.


Ne olmuş ([0,1] and 0) or None!
Kenly

Bu iyi bir nokta ... Mevcut formdaki sayı koleksiyonlarıyla kullanmak için
güvensiz

İle aynı şey (['','A'] and '') or None.
Kenly

Eğer my_list[0]olarak değerlendirilir False, sonucu olmalıdır None.
Kenly

2
my_list[0] if len(my_list) else None
Nicholas Hamilton

0

Bu nasıl pythonic emin değilim ama kütüphanede bir ilk işlevi olana kadar ben bu kaynak dahil:

first = lambda l, default=None: next(iter(l or []), default)

Sadece bir satır (siyaha uygundur) ve bağımlılıklardan kaçınır.



-1

Muhtemelen en hızlı çözüm değil, ama kimse bu seçenekten bahsetmedi:

dict(enumerate(get_list())).get(0)

eğer get_list()dönebilir Nonekullanabilirsiniz:

dict(enumerate(get_list() or [])).get(0)

Avantajları:

-Tek çizgi

-Sadece bir get_list()kez ararsın

-anlaması kolay


-1

Benim kullanım durumum sadece yerel bir değişkenin değerini ayarlamak oldu.

Şahsen denemek buldum ve okumak için stil temizleyici hariç

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

bir listeyi dilimlemek yerine.

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item

-1

Birkaç kişi böyle bir şey yapmayı önerdi:

list = get_list()
return list and list[0] or None

Bu birçok durumda işe yarar, ancak yalnızca liste [0] 0, False veya boş bir dizeye eşit değilse çalışır. [0] listesi 0, False veya boş bir dize ise yöntem yanlış None döndürür.

Bu hatayı kendi kodumda bir çok kez oluşturdum!


3
Bu tek başına bir cevaptan ziyade hatalı cevap (lar) ın altında bir yorum olmalıdır.
IJ Kennedy

-2

Extract Method kullanabilirsiniz . Başka bir deyişle, bu kodu daha sonra çağıracağınız bir yönteme çıkarın.

Daha fazla sıkıştırmaya çalışmam, bir astarın ayrıntılı versiyondan daha zor okunması gibi görünüyor. Ve Extract Method kullanıyorsanız, bu bir astardır;)



-3

deyimsel python C-tarzı üçlü operatörlere eşdeğer değil

cond and true_expr or false_expr

yani.

list = get_list()
return list and list[0] or None

[0], 0 sayısı veya boş dize (veya false olarak değerlendirilen başka bir şey) ise, bu, [0] öğesinin gerçekte olduğu yerine Yok değerini döndürür.
Adam Rosenfield
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.