最初の項目またはなしを返す Python イディオム
-
21-08-2019 - |
質問
もっと簡単な方法があると思いますが、私には思いつきませんでした。
リストを返す多数のメソッドを呼び出しています。リストは空である可能性があります。リストが空でない場合は、最初の項目を返します。それ以外の場合は、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 の単純な式で何ができるかに頻繁に驚かされるためであり、単純な式で機能するのであれば、関数を書くのは愚かなことだと思ったからです。しかし、これらの回答を見ると、それは関数のように思えます は シンプルな解決策。
解決
は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
他のヒント
最善の方法はこれです:
a = get_list()
return a[0] if a else None
また、1つのラインでそれを行うことができますが、それはプログラマが読むことのためにはるかに困難です。
return (get_list()[:1] or [None])[0]
(get_list() or [None])[0]
これは動作するはずです。
それは、組み込みlist
機能が上書きされるため、ところで、私は、変数list()
を使用していませんでした。
編集:私は少し単純だったが、ここでは、以前の間違ったバージョン
ほとんどのpython慣用的な方法は、リストには、の反復可能なのであるから、イテレータ上)(次を使用することです。ただJ.F.Sebastian @ 2011年12月13日にコメントを入れどのような。
next(iter(the_list), None)
the_list
が空の場合、これはNoneを返します。参照次の()はPython 2.6+ の
the_list
のために知っていれば、または空ではありません。
OP の解決策はほぼ完成しています。OP をより Python らしくするためのことがいくつかあります。
まず、リストの長さを取得する必要はありません。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 の風味が得られます。
あなたは次のようにそれを行うには発電機に切り替えることができます
next((x for x in blah if cond), None)
プロ:何とかは、刃先交換式コンでない場合は動作します:それはなじみのない構文です。周りのハッキングといえipythonで何かをフィルタリングしながら、それは便利です。
for item in get_list():
return item
率直に言って、私はより良いイディオムがあるとは思わない:あなたは明確かつ簡潔である - 何のための必要はありません「よりよいです」。たぶん、これは本当に好みの問題です、あなたはif len(list) > 0:
でif list:
を変えることができる - 空のリストは常にfalseと評価されます。
関連ノートで、Pythonは にはないのPerl(しゃれが意図していない!)で、あなたは、可能なクールなコードを取得する必要はありません。
実は、私はPythonで見てきた最悪のコードは、また、非常にクール:-)と完全にunmaintainableだっます。
ちなみに、私はここで見てきたソリューションのほとんどは考慮していない - この場合には、それらはすべて、なしではなく、正しい要素を返しませんます。
最初の項目を返す 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
これは関数がここで終了し、暗黙的に戻り値を返すことを前提としています。 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
最初の要素が次の場合、2 番目の要素を返します。 False
ブール値のコンテキストでは、次の場合 get_list
空のリストを返す場合、括弧内に含まれる式は「なし」を含むリストを返します。このリストには、 0
索引:
return (get_list() or [None])[0]
次の関数は、最初の項目が次の場合に 2 番目の項目を返します。 True
ブール値コンテキストでは、my_list を 2 回参照しているため、三項式と同等です (技術的にはワンライナーではありません)。
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
.
冗長な三項式 (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 回だけ呼び出され、最初に説明した推奨される Python ソリューションが得られます。
l = get_list()
return l[0] if l else None
イディオムについては、 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
もう1つのツールは、 more_itertools.first
呼ばれ、最初のアイテムを返すことが利用可能です>。
mit.first([3, 2, 1])
# 3
mit.first([], default=None)
# None
これらのitertoolsだけではなくリストに、任意の反復可能なため、一般的にスケールます。
好奇心から、私は解決策の2にタイミングを走りました。途中でループのために少し高価なのPython 2.5.1と私のマシン上で終了するreturnステートメントを使用するソリューションは、私はこれは反復可能の設定に関係している疑います。
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 seconds 0.741086 seconds 0.741191 seconds 0.743543 seconds avg. return_first_item: 0.785511 seconds 0.822178 seconds 0.782846 seconds 0.796845 seconds avg. full_lists: index_first_item: 0.762618 seconds 0.788040 seconds 0.786849 seconds 0.779169 seconds avg. return_first_item: 0.802735 seconds 0.878706 seconds 0.808781 seconds 0.830074 seconds avg. mixed_lists: index_first_item: 0.791129 seconds 0.743526 seconds 0.744441 seconds 0.759699 seconds avg. return_first_item: 0.784801 seconds 0.785146 seconds 0.840193 seconds 0.803380 seconds avg.
try:
return a[0]
except IndexError:
return None
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))
(繰り返し可能な限り回避)
これはどうます:
(my_list and my_list[0]) or None
注:これは、オブジェクトのリストのために正常に動作する必要がありますが、それは以下のコメントあたり数または文字列リストの場合には間違った答えを返すことがあります。
。あなたはメソッドを抽出を使用することができます。つまり、あなたが呼びたいメソッドにそのコードを抽出します。
私はずっとそれを圧縮しようとしないだろう、1ライナーは、冗長バージョンよりも読みにくいようです。そして、あなたは抽出方式を使用している場合、それは1つのライナーです;)
と-やトリックを使用します:
a = get_list()
return a and a[0] or None
そして、何について:next(iter(get_list()), None)
?
ここでは最速の一つであるが、標準的な(のPython 2.6から開始)と簡潔でない可能性があります。
おそらくない最速のソリューションが、誰も、このオプションを言及していない。
dict(enumerate(get_list())).get(0)
get_list()
はNone
を返すことができる場合に使用することができます:
dict(enumerate(get_list() or [])).get(0)
長所ます:
- オンライン
- あなたは一度だけget_list()
を呼び出す
を理解する - 簡単
私のユースケースは、ローカル変数の値を設定することだけだった。
個人的に私がしようとすると、スタイルクリーナー以外は読むことを見つけます。
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
if mylist != []:
print(mylist[0])
else:
print(None)
何人かがこのような何かをやって提案しています
list = get_list()
return list and list[0] or None
多くのケースで動作しますが、[0]のリストは0、FALSE、または空の文字列と等しくない場合にのみ動作すること。 [0]リストは0、FALSE、または空の文字列である場合は、この方法が誤ってNoneを返します。
私は自分のコードで1つのあまりにも多くの時間をこのバグを作成しました!
C-スタイル三項演算子に慣用のpythonと同等ではありません
cond and true_expr or false_expr
すなわち。
list = get_list()
return list and list[0] or None