web-dev-qa-db-ja.com

最初の項目を返すPythonの慣用句またはNone

これを実行するためのもっと簡単な方法があると確信しています。

リストを返す一連のメソッドを呼び出しています。リストが空の可能性があります。リストが空でない場合は、最初の項目を返します。そうでなければ、Noneを返します。このコードは動作します:

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

これを行うには単純な一行の慣用句があるべきだと私には思えますが、私の人生のために私はそれを考えることができません。ある?

編集:

ここで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の単純な式ができることに私はよく驚いています。そして、単純な式があるならば、関数を書くのは愚かなことだと思いました。しかし、これらの答えを見て、それは関数簡単な解決策のようです。

217
Robert Rossney

Python 2.6以降

next(iter(your_list), None)

your_listNoneにすることができる場合:

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
148
jfs

最善の方法はこれです:

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

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

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

それはうまくいくはずです。

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

編集:私は以前よりも少し単純ですが間違ったバージョンを持っていました。

64
recursive

Listは反復可能なので、Pythonで最も慣用的な方法は、反復子にnext()を使用することです。 2011年12月13日に@ J.F.Sebastianがコメントした内容と同じです。

next(iter(the_list), None)the_listが空の場合、Noneを返します。 next()Python 2.6 + を参照してください。

あるいはthe_listが空でないことを確実に知っているならば:

iter(the_list).next()を参照--- iterator.next()Python 2.2 +

31
Devy

OPの解決策はもうすぐそこにあります、それをよりPythonicにするためのいくつかのことだけがあります。

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

if list:

さらに、予約語と重複する変数に割り当てるのは非常に悪い考えです。 "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の味を与えます。

10
gotgenes

リスト内包表記から最初のもの(またはNone)を摘み取ろうとしているのであれば、ジェネレータに切り替えて次のようにすることができます。

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

Pro:blahがインデックス化できない場合に機能しますCon:それはなじみのない構文です。ただし、ipythonでハッキングしたりフィルタリングしたりするときに便利です。

10
Aidan Kane
for item in get_list():
    return item
3
A. Coady

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

最もPython的なアプローチは、最も支持された答えが示したものであり、質問を読んだときに頭に浮かんだ最初のものでした。空の可能性のあるリストが関数に渡される場合の最初の使用方法は次のとおりです。

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

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

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メソッドを持つイテレータを返します。 (Python 3.の[.__next__ 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_listが1回だけ呼び出されるようにローカル変数を使用すると、最初に説明した推奨Pythonicソリューションが得られます。

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

慣用句に関しては、nthと呼ばれる itertoolsレシピ があります。

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は、リストだけでなく、あらゆるイテラブルに対して一般的に拡張されます。

2
pylang

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

これに関連して、Pythonはnot Perlです(駄目なのではありません!)。最もクールなコードを入手する必要はありません。
実際、私がPythonで見た中で最悪のコードもまたとてもクールでした:-)そして完全にメンテナンスできませんでした。

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

2
rob

好奇心から、私は2つの解決策にタイミングをかけました。 forループを時期尚早に終了させるためにreturn文を使用する解決策は、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 __== '__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.741186秒
 0.741191秒
 0.743543秒平均[
] return_first_item :
 0.785511秒
 0.822178秒
 0.782846秒
 0.796845秒平均[
] full_list:
 index_first_item:
 0.762618秒
 0.788040秒
 0.786849秒
 0.779169秒平均[
 return_first_item:
 0.802735秒
 0.878706秒
 ] 0.808781秒[...] 0.830074秒[平均] [[____]混合インデックス:[[____]] index_first_item:[[____]] 0.791129秒[。 。] 0.759699秒平均
 return_first_item:
 0.784801秒
 0.785146秒
 0.840193秒
 0.803380秒平均
1
gotgenes
try:
    return a[0]
except IndexError:
    return None
0
limscoder

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

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

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

私は自分自身のコードでこのバグを何度も作りました!

0
Clint Miller

これはどう:

(my_list and my_list[0]) or None

注:これはオブジェクトのリストに対してはうまく機能するはずですが、以下のコメントに従った数または文字列のリストの場合には間違った答えを返すかもしれません。

0
VitalyB
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
ttepasse