最初のアイテムを返すPythonイディオムまたはNone


274

これを行う簡単な方法が、私には起こらないと確信しています。

リストを返す一連のメソッドを呼び出しています。リストは空の場合があります。リストが空でない場合は、最初のアイテムを返します。それ以外の場合は、Noneを返します。このコードは機能します:

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

これを行うための簡単な1行のイディオムがあるべきだと私には思われますが、私の人生では、それを考えることはできません。ある?

編集:

ここで1行の式を探しているのは、信じられないほど簡潔なコードが好きなのではなく、次のようなコードをたくさん書かなければならないからです。

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

私がしたいことは確かに関数で達成できます(おそらくそうなるでしょう):

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

質問を投稿したのは、Pythonの単純な式で何ができるかに驚かされることが多かったためです。単純な式があれば、関数を書くのはばかげたことだと思いました。しかし、これらの答えを見ると、関数単純な解決策のようです。


28
ところで、Pythonの2.6+であなたが使用できるnext(iter(your_list), None)代わりにfirst_item(your_list)仮定your_listされていないNoneget_first_list()get_second_list()いつものiterableを返さなければなりません)。
jfs

1
私はあなたの意味だと思いnext(iter(your_list))ますが、2番目の引数を指定した場合以来、iterあなたは最初の引数は呼び出し可能であること、それを言っています。
Robert Rossney、2011

7
いや、Noneここのための二番目のパラメータであるnext()、ではありませんiter()。空の場合は発生next()せずに、2番目のパラメータが指定された場合はそれを返します StopIterationyour_list
jfs

@JFSebastianによって提案された解決策は、質問が重複して存在します:stackoverflow.com/a/18533669/144408
shahjapan

回答:


205

Python 2.6以降

next(iter(your_list), None)

もしyour_list可能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

例:

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

別のオプションは、上記の関数をインライン化することです:

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

breakあなたが避けるために書くことができます:

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

どこ:

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

1
その最後のオプションはほぼ正確に私が探しているものです。それは明確で、機能し、新しい関数を定義する必要がありません。休憩がなんらかの理由で不要になった場合は、「正確に」と言います。休憩を省略するリスクは重要ではないからです。しかし、このアプローチには真実の輪があります。
ロバートロスニー2008

ああ、私はこれがまったく好きではありません。リスト内のいずれかの項目がFalseと評価された場合、その値は破棄されて置き換えられます。""リストに空の文字列がある場合、その文字列は破棄され、空のリストに置き換えられます[]。0の場合は、でも置き換えられ[]ます。Falseそこにあれば、交換も。特定のケースではこれでうまくいくかもしれませんが、これは発展するのに悪い習慣です。
steveha 2009年

4
@steveha:たとえば、含まれるアイテムについて何も通知しないbool(lst) かどうかをlen(lst) > 0通知します。lstたとえば、bool([False]) == True;式はを返します。同じことは、が[False] or []返す[False]場合も同じです。[0] or [] [0]
jfs 2009年

@RobertRossney:声明yield_first()を避けるために追加しましたbreak
jfs 2012年

1
@ThomasAhle:どちらも正しい。回答を更新して、None以外のケースのソリューションを提供しました。
jfs

218

最良の方法はこれです:

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

1行で行うこともできますが、プログラマが読むのははるかに困難です。

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

おっとっと。これはPython 2.4アプリです。
Robert Rossney、2008

15
@Adam:将来使用する可能性がありますnext(iter(your_list), None)
jfs 14

@JFSebastianは実際にnext(iter(your_list))は十分です
Brad Johnson、

13
@BradleagheJohnson:間違っています。NoneOPの要求に応じて戻るのではなく、StopIterationを発生させます。試してみてください:next(iter([]))
jfs

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

うまくいくはずです。

ところでlist、組み込みlist()関数を上書きするため、変数を使用しませんでした。

編集:私は少しシンプルですが、以前のバージョンは間違っていました。


4
これは巧妙で機能しますが、efotinisによって提案されている「foo [0]を返す場合はfoo else 0を返さない」の方が、メンテナンスが少し簡単です。
ジェイ

3
問題は1行の解決策を求めていたので、除外しました。
再帰的

get_list()がNoneを返す場合、これらの式はどちらも機能しません。"TypeError: 'NoneType' object is unsubscriptable。"と表示されます。または「TypeError:サポートされていないオペランドタイプ+: 'NoneType'および 'list'。」
ロバートロスニー2008

4
質問では、メソッドはリストを返しますが、Noneはリストではありません。したがって、要件を考えると、どちらも機能すると私はまだ信じています。
再帰的

get_list()が添え字付きまたは追加されなくなったためにNoneを返した場合、動作するはずです。
ロバート

32

リストは反復可能であるため、最もPythonの慣用的な方法は、イテレータでnext()を使用することです。2011年12月13日に@JFSebastianがコメントに書き込んだのと同じです。

next(iter(the_list), None)the_list空の場合、これはNoneを返します。next()Python 2.6以降を参照

またはあなたが確かにthe_list空ではないことがわかっている場合:

iter(the_list).next()iterator.next()を参照してくださいPython 2.2以降


1
これには、return l[0] if l else Noneリストがlibcompの場合のように、リストを変数に割り当てる必要がないという利点があります。リストはgenexprが実際にある場合、これはあなたがのようなリスト全体を構築する必要はありませんので、さらに良いですnext(iter(x for x in range(10) if x % 2 == 0), None)
RubenLaguna

が確実にthe_list空でないことがわかっている場合は、と書きthe_list[0]ます。
kaya3

12

リスト内包から最初のもの(またはNone)を引き抜こうとしていることに気付いた場合は、ジェネレーターに切り替えて次のようにすることができます。

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

Pro:blahがインデックス可能でない場合に機能しますCon:慣れていない構文です。ipythonでハッキングやフィルタリングを行うときにも役立ちます。


11

OPのソリューションはもうすぐそこにあります。それをよりPythonicにするためのいくつかの事柄があります。

まず、リストの長さを取得する必要はありません。Pythonの空のリストは、ifチェックでFalseと評価されます。ただ言うだけ

if list:

さらに、予約語と重複する変数に割り当てるのは非常に悪い考えです。「リスト」はPythonの予約語です。

それを変更してみましょう

some_list = get_list()
if some_list:

ここで多くのソリューションが見落としている本当に重要な点は、すべてのPython関数/メソッドがデフォルトでNoneを返すことです。以下をお試しください。

def does_nothing():
    pass

foo = does_nothing()
print foo

関数を早期に終了するためにNoneを返す必要がない限り、明示的にNoneを返す必要はありません。かなり簡潔に言えば、最初のエントリが存在する場合はそれを返します。

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

最後に、おそらくこれは暗黙のうちに含まれていましたが、明示的にするため明示的が暗黙的よりも優れているため)、関数に別の関数からリストを取得させないでください。パラメータとして渡すだけです。したがって、最終結果は

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

my_list = get_list()
first_item = get_first_item(my_list)

私が言ったように、OPはもうすぐそこにあり、ほんの少しのタッチであなたが探しているPythonの味を与えます。


1
なぜ「リスト」はPythonの予約語ですか?または、もっと要点を言えば、「リスト」はpythonの予約語であるとどこに言っていますか?または、もっと要点を言えば、「リスト」がPythonの予約語であることをどのように確認しましたか?「list」という名前の変数を宣言できることを考えると、それは明らかに予約語ではありません。
Lasse V. Karlsen、

2
ポイントを取る。Pythonで本当に予約されている単語は、ここtinyurl.com/424663にありますただし、組み込みの関数と型を予約済みと見なすことをお勧めします。list()は実際にリストを生成します。dictやrangeなども同様です
gotgenes 2008

最後の2行を結合して、一時変数を削除します(my_listがさらに必要でない場合)。読みやすさが向上します。
Svante

これは、私が引き寄せていた答えのほとんどです。
ロバートロスニー2008

1
get_first_itemデフォルトでそれ自体がNoneであるdefaultパラメータ(などdict.get)を受け入れる必要があります。したがって、関数インターフェースは、my_listが空の場合にどうなるかを明示的に伝えます。
jfs 2008

3
for item in get_list():
    return item

これは簡潔で美しいです。
gotgenes 2008

悲劇的なことに、それは機能しません。get_list()がNoneを返すと、「TypeError: 'NoneType'オブジェクトは反復可能ではありません」例外がスローされます。
ロバートロスニー2008

修正するには:「get_list()または[]:の項目の場合」
itsadok

5
この質問では、get_listがリストを返すと明確に想定しています。結果に対してlenとgetitemが呼び出されます。
A. Coady

2

率直に言って、私はもっと良いイディオムがあるとは思いません。あなたは明確で簡潔です-「より良い」ものは必要ありません。たぶん、しかし、これは本当に好みの問題であり、変更if len(list) > 0:することができますif list:-空のリストは常にFalseと評価されます。

関連する注意として、PythonはPerlではありません(しゃれはありません!)。可能な限りクールなコードを入手する必要はありません。
実際、私がPythonで見た最悪のコードも非常にクールで、:-)完全にメンテナンスできませんでした。

ちなみに、私がここで見たほとんどの解決策は、list [0]がFalse(たとえば、空の文字列、またはゼロ)と評価されるときに考慮に入れていません。この場合、それらはすべてNoneを返し、正しい要素ではありません。


Pythonでは、で書くif lst:よりも書くのが良いスタイルであると考えられていif len(lst) > 0:ます。また、Pythonキーワードを変数名として使用しないでください。それは涙で終わります。リストの変数名として、Lまたはを使用するのが一般的lstです。この場合list、変数名として実際に使用することを提案するつもりはなかったと思いますが、これは単に「いくつかのリスト」を意味しているだけです。また、「lst [0]がFalseと評価された場合」の+1。
steveha 2009年

はい、わかりやすくするために、OPと同じ種類の名前を維持していました。しかし、あなたは間違いなく正しいです。Pythonのキーワードを上書きしないように常に注意する必要があります。
ロブ・

2

最初のアイテムを返すPythonイディオムまたはNone?

最もPythonicなアプローチは、最も賛成された回答が示したものであり、質問を読んだときに最初に頭に浮かんだのはそれでした。空のリストが関数に渡された場合の使用方法は次のとおりです。

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

そして、リストがget_list関数から返された場合:

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

ここでこれを行うために説明された他の方法と説明

for

これを行うための賢い方法を考え始めたとき、これは私が考えた2番目のことです。

for item in get_list():
    return item

これは、関数がここで終了し、空のリストが返されたNone場合に暗黙的に戻ることを前提としていますget_list。以下の明示的なコードはまったく同じです。

for item in get_list():
    return item
return None

if some_list

以下も提案されました(私は不正な変数名を修正しました)。これも暗黙のを使用していNoneます。これは、起こり得ない反復の代わりに論理チェックを使用するため、上記よりも望ましいでしょう。これは、何が起こっているのかをすぐに理解するのが容易になります。ただし、読みやすさと保守性を重視して作成する場合はreturn None、末尾に明示を追加する必要があります。

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

or [None]0番目のインデックスをスライスして選択する

これは最も投票された回答にもあります:

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

スライスは不要であり、メモリ内に追加の1アイテムリストを作成します。次の方がパフォーマンスが高いはずです。説明するとor、最初の要素Falseがブールコンテキストにある場合、2番目の要素を返します。したがってget_list、空のリストを返す場合、括弧に含まれる式は 'None'のリストを返し、0インデックスによってアクセスされます。

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

次の例は、最初の項目Trueがブールコンテキストにある場合に2番目の項目を返すという事実を使用します。これはmy_listを2回参照するため、3項式(技術的には1行ではない)よりも優れていません。

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

next

次に、組み込みの次の賢い使い方がnextあります。iter

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

説明のために、メソッドをiter持つイテレータを返します.next。(.__next__Python 3の場合)次に、組み込み関数nextがその.nextメソッドを呼び出し、イテレータが使い果たされた場合は、指定したデフォルトを返しますNone

冗長な3項式(a if b else c)と循環

以下が提案されましたが、論理は通常負ではなく正で理解されるため、逆が望ましいでしょう。get_listが2回呼び出されるため、結果が何らかの方法でメモされない限り、これはパフォーマンスが低下します。

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

より良い逆:

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

さらに良いのは、get_list1回だけ呼び出されるようにローカル変数を使用し、推奨されるPythonicソリューションについて最初に説明しました。

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

2

イディオムについては、と呼ばれるitertoolsレシピがありnthます。

itertoolsレシピから:

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

ワンライナーが必要な場合は、このレシピを実装するライブラリをインストールすることを検討してください。例more_itertools

import more_itertools as mit

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

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

と呼ばれる最初のアイテムのみを返す別のツールが利用可能ですmore_itertools.first

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

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

これらのitertoolsは、リストだけでなく、あらゆる反復可能オブジェクトに対して一般的にスケーリングします。



1

好奇心から、私は2つのソリューションでタイミングを計りました。returnステートメントを使用してforループを途中で終了させるソリューションは、Python 2.5.1を使用するマシンでは少しコストがかかります。これは、イテラブルの設定に関係していると思います。

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

これらは私が得たタイミングです:

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

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

1
例外は通常、フロー制御には使用されません。
マットグリーン

このコードを長くして例外処理を追加することは、まさに私が探していたイディオムだとは思いません。
ロバートロスニー2008

3
しかし、これは一般的なPythonイディオムです!パフォーマンス上の理由から、使用しない場合があります。あなたはより良いパフォーマンスを得るでしょうif len(a) > 0:が、これは良いスタイルと考えられています。これが問題をどの程度うまく表現しているかに注意してください。「リストの先頭を抽出してみてくださいNone。うまくいかない場合は、戻ります」。Googleの「EAFP」を検索したりここを見て:python.net/~goodger/projects/pycon/2007/idiomatic/...
steveha

@stevehaデータによって異なります。len(a)が通常> 0であり、len(a)<1がまれな状態である場合、例外のキャッチは実際によりパフォーマンスが高くなります。
limscoder

@limscoder、私はあなたに同意します。パフォーマンス上の理由から、使用しない場合の詳細については説明ませんでした。あなたがこれを決して使用しないとは言わなかった。
steveha

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

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

ところで:私はあなたの一般的なプログラムの流れを次のようなものに作り直します:

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

(可能な限り繰り返しを避ける)


0

これはどう:

(my_list and my_list[0]) or None

注:これはオブジェクトのリストでは正常に機能するはずですが、以下のコメントに従って数値または文字列のリストの場合は、正しくない応答を返す可能性があります。


どうだ([0,1] and 0) or None
ケンリー2016年

それは良い点です...現在の形式の数値コレクションで使用するのは安全ではありません:(
VitalyB

と同じこと(['','A'] and '') or None
ケンリー2016年

場合my_list[0]のように評価されFalse、結果がでなければなりませんNone
ケンリー2016年

2
my_list[0] if len(my_list) else None
ニコラスハミルトン

0

これがどれほどPythonicかはわかりませんが、ライブラリに最初の関数があるまで、これをソースに含めます。

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

これは1行(黒に準拠)であり、依存関係を回避します。



-1

おそらく最速のソリューションではありませんが、このオプションについては誰も言及していません:

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

場合はget_list()返すことができNoneます使用することができます。

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

利点:

-1行

- get_list()一度電話するだけ

-理解しやすい


-1

私の使用例は、ローカル変数の値を設定することだけでした。

個人的に私は試してみて、除外するスタイルクリーナーを見つけました

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

リストをスライスするよりも。

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

-1

何人かの人々はこのようなことをすることを提案しました:

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

これは多くの場合に機能しますが、list [0]が0、False、または空の文字列に等しくない場合にのみ機能します。list [0]が0、False、または空の文字列の場合、メソッドは誤ってNoneを返します。

このバグを自分のコードで何度も作成しました。


3
これは、独立した回答ではなく、欠陥のある回答に基づくコメントにする必要があります。
IJケネディ

-2

あなたは抽出方法を使用することができます。言い換えると、そのコードをメソッドに抽出して、それを呼び出します。

私はそれをはるかに圧縮しようとはしません、ワンライナーは詳細バージョンよりも読みにくいようです。そして、Extractメソッドを使用する場合、それは1つのライナーです;)



-3

Cスタイルの三項演算子に相当する慣用的なpythonではありません

cond and true_expr or false_expr

すなわち。

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

a [0]が数値0または空の文字列(またはfalseと評価されるその他のもの)の場合、これは実際のa [0]の代わりにNoneを返します。
Adam Rosenfield
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.