リストに辞書のような安全な「取得」メソッドがないのはなぜですか?


264

リストに辞書のような安全な「取得」メソッドがないのはなぜですか?

>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'

>>> l = [1]
>>> l[10]
IndexError: list index out of range

1
リストは、辞書とは異なる目的で使用されます。get()は、リストの一般的な使用例では必要ありません。ただし、辞書の場合、get()は非常に便利です。
mgronber '26

42
l[10:11]代わりにl[10]、たとえばの代わりにスライスを要求した場合、IndexErrorを発生させずにリストから常に空のサブリストを取得できます。それが存在する場合は()のThサブリスト)は、所望の要素を有することになる
jsbueno

56
ここのいくつかに反して、私は金庫の考えを支持します.get。これはと同等ですl[i] if i < len(l) else defaultが、より読みやすく、簡潔で、i再計算することなく式になることができます
Paul Draper

6
今日私はこれが存在することを望みました。私はリストを返す高価な関数を使用していますが、最初のアイテムが必要Noneか、存在しない場合のみです。言うのは良かったx = expensive().get(0, None)ので、高価なものの無駄な戻りを一時変数に入れる必要はありませんでした。
Ryan Hiebert 2014年

2
@ライアン私の答えはあなたを助けるかもしれませんstackoverflow.com/a/23003811/246265
ジェイク

回答:


112

結局のところ.getdictは連想コレクション(値は名前に関連付けられている)であるため、安全なメソッドはおそらくないでしょう。リストの要素にアクセスする例外を回避するため(lenメソッドが非常に高速であるため)。この.getメソッドを使用すると、名前に関連付けられた値をクエリできます。ディクショナリ内の37番目の項目に直接アクセスするのではありません(これは、リストに対して要求しているものに似ています)。

もちろん、これは自分で簡単に実装できます。

def safe_list_get (l, idx, default):
  try:
    return l[idx]
  except IndexError:
    return default

それをの__builtins__.listコンストラクタにモンキーパッチすることもできますが__main__、ほとんどのコードはそれを使用しないため、それほど広範囲に及ぶ変更にはなりません。独自のコードで作成したリストでこれを使用したいだけの場合は、単純にサブクラス化listしてgetメソッドを追加できます。


24
Pythonでは、組み込み型のサルパッチングを許可していませんlist
Imran

7
@CSZ:.getリストにない問題を解決します-存在しない可能性のあるデータを取得するときに例外を回避する効率的な方法。有効なリストインデックスが何であるかを知ることは非常に簡単で非常に効率的ですが、ディクショナリのキー値に対してこれを行うための特に良い方法はありません。
Nick Bastin、2011

10
これは効率性についてはまったく考えていません。キーがディクショナリに存在するかどうかを確認したり、アイテムを返したりしていますO(1)。生の面ではチェックほど高速ではありませんがlen、複雑さの観点からはすべて同じですO(1)。正しい答えは、典型的な使用法/セマンティクスの1つです...
Mark Longair

3
@Mark:すべてのO(1)が等しく作成されるわけではありません。また、dictすべてのケースではなく、最良のケースのO(1)のみです。
Nick Bastin、2011

4
ここは要点を逃していると思います。議論は効率についてであってはなりません。時期尚早の最適化で停止してください。プログラムが遅すぎる場合は、悪用しているか.get()、コード(または環境)の他の場所に問題があります。このような方法を使用するポイントは、コードの読みやすさです。「バニラ」手法では、これを実行する必要があるすべての場所に4行のコードが必要です。この.get()手法は1つしか必要とせず、後続のメソッド呼び出し(などmy_list.get(2, '').uppercase())と簡単にチェーンできます。
タイラークロンプトン2017

67

これは、最初の要素が必要な場合に機能します。 my_list.get(0)

>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'

それはあなたが求めていたものとは正確には違いますが、他の人を助けるかもしれません。


7
関数型プログラミングよりもPythonicが少ない
Eric

next(iter(my_list[index:index+1]), 'fail')0だけでなく、任意のインデックスを使用できます。FPは少なくなりますが、おそらくPythonicicであり、ほぼ確実に読みやすくなりますmy_list[index] if index < len(my_list) else 'fail'
alphaasoup

47

おそらく、リストのセマンティクスではあまり意味がなかったためです。ただし、サブクラス化することで簡単に独自のものを作成できます。

class safelist(list):
    def get(self, index, default=None):
        try:
            return self.__getitem__(index)
        except IndexError:
            return default

def _test():
    l = safelist(range(10))
    print l.get(20, "oops")

if __name__ == "__main__":
    _test()

5
これは圧倒的に、OPに対する最もパイソン的な回答です。Pythonで安全な操作であるサブリストを抽出することもできます。mylist = [1、2、3]の場合、例外を発生させることなく、mylist [8:9]を使用して9番目の要素を抽出できます。次に、リストが空かどうかをテストし、空でない場合は、返されたリストから単一の要素を抽出します。
jose.angel.jimenez

1
これは、特に辞書との対称性を維持するため、他のPython以外のワンライナーハックではなく、受け入れられる答えになるはずです。
エリック

1
素敵なgetメソッドが必要だからといって、独自のリストをサブクラス化するのにpythonicはありません。読みやすさが重要です。また、不要なクラスを追加するたびに読みやすさが低下します。try / exceptサブクラスを作成せずにアプローチを使用してください。
Jeyekomon、

@Jeyekomonサブクラス化によって定型文を削減することは完全にPythonicです。
キース、

42

.getを使用する代わりに、このように使用してもリストは問題ありません。使い方の違いだけです。

>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'

15
-1で最新の要素を取得しようとすると、失敗します。
pretobomba

これは循環リンクリストオブジェクトでは機能しないことに注意してください。さらに、構文により、「スキャンブロック」と呼ばれるものが発生します。コードをスキャンしてそれが何をするかを確認するとき、これは私を少し遅くする行です。
タイラークロンプトン2017

インラインif / elseが2.6などの古いpythonでは機能しない(または2.5ですか?)
Eric

3
@TylerCrompton:Pythonには循環リンクリストはありません。自分で作成した場合は、.getメソッドを自由に実装できます(ただし、この場合にインデックスが何を意味するのか、またはなぜ失敗するのかを説明する方法がわからない場合を除きます)。
Nick Bastin 2017年

範囲外の負のインデックスを処理する別の方法は次のようになりますlst[i] if -len(lst) <= i < len(l) else 'fail'
マイク

17

これを試して:

>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'

1
私はこれが好きですが、最初に新しいサブリストを作成する必要があります。
リックはモニカ

15

jose.angel.jimenezへのクレジット


「ワンライナー」ファンのために…


リストの最初の要素が必要な場合、またはリストが空の場合にデフォルト値が必要な場合は、以下を試してください。

liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)

戻り値 a

そして

liste = []
value = (liste[0:1] or ('default',))[0]
print(value)

戻り値 default


他の要素の例…

liste = ['a', 'b', 'c']
print(liste[0:1])  # returns ['a']
print(liste[1:2])  # returns ['b']
print(liste[2:3])  # returns ['c']

デフォルトのフォールバック…

liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0])  # returns a
print((liste[1:2] or ('default',))[0])  # returns b
print((liste[2:3] or ('default',))[0])  # returns c

テスト済み Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)


1
短い代替:value, = liste[:1] or ('default',)。括弧が必要なようです。
qräbnö

13

あなたができる最善のことは、リストをdictに変換してから、getメソッドでそれにアクセスすることです:

>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')

20
妥当な回避策ですが、「できる最善のこと」はほとんどありません。
tripleee 2014年

3
しかし、非常に非効率的です。注:その代わりに、次のzip range lenように使用することもできますdict(enumerate(my_list))
Marian

3
これは最高のことではなく、あなたができる最悪のことです。
erikbwork

3
パフォーマンスを考えると最悪です...パフォーマンスを重視する場合は、Pythonなどのインタプリタ言語でコーディングしないでください。私はこの解決策を、かなりエレガントで強力なpythonic辞書を使用して見つけます。とにかく、初期の最適化は悪ですので、口述して、後でそれがボトルネックであることを確認しましょう。
エリック

7

それで、私はこれについてさらに調査しましたが、これに固有のものは何もないことがわかりました。list.index(value)を見つけたときは興奮しました。指定されたアイテムのインデックスを返しますが、特定のインデックスの値を取得するためのものは何もありません。したがって、safe_list_getソリューションを使用したくない場合は、かなり良いと思います。シナリオに応じて、仕事をやり遂げることができるifステートメントの1つを次に示します。

>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'

'No'の代わりにNoneを使用することもできます。

>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None

また、リストの最初または最後のアイテムだけを取得する場合も、これは機能します

end_el = x[-1] if x else None

これらを関数にすることもできますが、それでもIndexError例外ソリューションが気に入りました。ダミーバージョンのsafe_list_getソリューションを試してみて、少しシンプルにした(デフォルトなし):

def list_get(l, i):
    try:
        return l[i]
    except IndexError:
        return None

何が最も速いかを確認するためのベンチマークを行っていません。


1
本当にpythonicではありません。
エリック

@エリックどのスニペット?もう一度見てみるのが一番理にかなっている以外は、試してみると思います。
radtek、2017

スタンドアロン関数はpythonicではありません。例外は確かに少しpythonicですが、プログラミング言語では一般的なパターンであるため、それほど多くはありません。さらにpythonicは、組み込み型listをサブクラス化して拡張する新しいオブジェクトです。このようにして、コンストラクターはlist、リストのように動作するa または何かを取得でき、新しいインスタンスはのように動作しますlist。以下のキースの回答をご覧ください。
エリック

1
@Eric私は質問をOOP固有のものとしてではなく、「なぜリストがdict.get()キャッチするのではなく、リストインデックス参照からデフォルト値を返すのに類推がないのIndexErrorですか?それで、実際には言語/ライブラリ機能(そしてOOPではありません) )FPコンテキスト対さらに、1はおそらく多分WWGDとして「神託」のご利用を修飾する必要があります(FP Pythonのための彼の軽蔑はよく知られているように)、必ずしもちょうどPEP8 / 20を満足しない。。
cowbert

1
el = x[4] if len(x) == 4 else 'No'–どういう意味len(x) > 4ですか?x[4]は範囲外ですlen(x) == 4
マイク

4

辞書は検索用です。エントリが存在するかどうかを尋ねることは理にかなっています。リストは通常​​反復されます。L [10]が存在するかどうかを尋ねることは一般的ではなく、Lの長さが11かどうかを尋ねます。


はい、同意します。しかし、ページ "/ group / Page_name"の相対URLを解析しただけです。「/」で分割し、PageNameが特定のページと等しいかどうかを確認したい。長さを追加でチェックしたり、例外をキャッチしたり、独自の関数を記述したりする代わりに、[url.split( '/')。get_from_index(2、None)== "lalala"]のように書く方が快適です。おそらくあなたは正しいです、それは単に珍しいと考えられています。とにかく私はまだこれに同意しません=)
CSZ

@ニックバスティン:何も悪いことではありません。コーディングのシンプルさとスピードがすべてです。
CSZ

キーが連続した整数である場合に、よりスペース効率の良い辞書としてリストを使用したい場合にも役立ちます。もちろん、ネガティブインデックスの存在はすでにそれを止めています。
アンチモン2012年

-1

ユースケースは基本的に、固定長の配列と行列を実行する場合にのみ関連するため、事前にそれらの長さがわかります。その場合は通常、Noneまたは0を手動で埋める前に作成するため、実際に使用するインデックスはすでに存在しています。

あなたはこれを言うことができます:私はかなり頻繁に辞書で.get()を必要とします。フルタイムのプログラマーとして10年経ちましたが、リストに掲載する必要があったとは思いません。:)


コメントの私の例はどうですか?よりシンプルで読みやすいものは何ですか?(url.split( '/')。getFromIndex(2)== "lalala")OR(result = url.split(); len(result)> 2 and result [2] == "lalala")。そして、はい、私は自分でそのような関数を書くことができることを知っています=)しかし、そのような関数が組み込まれていないことに驚きました。
CSZ

1
あなたの場合、あなたはそれを間違っていると言っています。URL処理は、ルート(パターンマッチング)またはオブジェクトトラバーサルのいずれかで行う必要があります。しかし、あなたの特定のケースにお答えします'lalala' in url.split('/')[2:]。ただし、ここでのソリューションの問題は、2番目の要素のみを確認することです。URLが「/ monkeybonkey / lalala」の場合はどうなりますか?TrueURLが無効でもを取得します。
Lennart Regebro、2011

私は2番目の要素だけが必要だったので、2番目の要素だけを取った。しかし、そうです、スライスは適切に機能する代替手段のようです
CSZ

@CSZ:ただし、最初の要素は無視され、その場合はスキップできます。:)意味を参照してください、例は実際の生活ではうまく機能しません。
Lennart Regebro、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.