2Dリストとnumpy配列で少し実験をしていました。このことから、答えを知りたいと思う3つの質問を挙げました。
最初に、2D pythonリストを初期化しました。
_>>> my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
_
次に、タプルを使用してリストのインデックスを作成してみました。
_>>> my_list[:,]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not Tuple
_
インタプリタがTypeError
ではなくSyntaxError
をスローするので、実際にこれを行うことは可能だと思いましたが、pythonはそれをネイティブでサポートしていません。
次に、リストをnumpy
配列に変換して、同じことを試みました。
_>>> np.array(my_list)[:,]
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
_
もちろん問題ありません。私の理解では、__xx__()
メソッドの1つがnumpy
パッケージでオーバーライドおよび実装されています。
Numpyのインデックス作成もリストをサポートしています:
_>>> np.array(my_list)[:,[0, 1]]
array([[1, 2],
[4, 5],
[7, 8]])
_
これにより、いくつかの質問が生じました。
__xx__
_メソッドがnumpyオーバーライド/定義されていますか?(ボーナス質問:なぜ私のタイミングはpython2でのスライスがpython3より遅いことを示しているのですか?)
次の3つの質問があります。
__xx__
メソッドがnumpyオーバーライド/定義されていますか?インデックス付け演算子[]
は、__getitem__
、__setitem__
、および__delitem__
を使用してオーバーライドできます。いくつかの内省を提供する単純なサブクラスを書くのは楽しいかもしれません:
>>> class VerboseList(list):
... def __getitem__(self, key):
... print(key)
... return super().__getitem__(key)
...
最初に空のものを作ってみましょう:
>>> l = VerboseList()
次に、いくつかの値を入力します。 __setitem__
をオーバーライドしていないため、まだ興味深いことは何も起こりません。
>>> l[:] = range(10)
それではアイテムを取得しましょう。インデックス0
は0
になります:
>>> l[0]
0
0
タプルを使用しようとすると、エラーが発生しますが、最初にタプルが表示されます。
>>> l[0, 4]
(0, 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getitem__
TypeError: list indices must be integers or slices, not Tuple
また、pythonがスライスを内部的にどのように表すかを知ることもできます:
>>> l[1:3]
slice(1, 3, None)
[1, 2]
このオブジェクトでできることはもっとたくさんあります-試してみてください!
これは答えるのが難しいです。それについて考える1つの方法は歴史的です:numpy
開発者が最初にそれを考えたからです。
1991年の最初の公開リリースでは、Pythonにはnumpy
ライブラリがなく、多次元リストを作成するには、リスト構造をネストする必要がありました。初期の開発者は、 -特に、Guido van Rossum( GvR )-最初は物事をシンプルに保つことが最善であると感じました。スライスのインデックス作成はすでにかなり強力でした。
しかし、それから間もなく、Python=を科学的コンピューティング言語として使用することに関心が高まりました。1995年から1997年にかけて、多くの開発者が初期の前身であるnumeric
というライブラリで共同作業を行いました。 of numpy
。彼はnumeric
またはnumpy
の主要な貢献者ではありませんでしたが、G [R]はnumeric
開発者と調整し、多次元配列のインデックス付けを行う方法でPythonのスライス構文を拡張しました後で、numeric
の代わりにnumarray
という名前が使用され、2006年に、両方の優れた機能を組み込んだnumpy
が作成されました。
これらのライブラリは強力でしたが、重いc拡張などが必要でした。それらをベースに組み込むと、Python=ディストリビューションはかさばるようになります。GvRはスライス構文を少し拡張しましたが、通常のリストにファンシーインデックスを追加すると、APIが劇的に、そして多少冗長に変更されます。派手なインデックス作成は外部のライブラリで既に可能であったことを考えると、メリットはコストに見合うものではありませんでした。
この物語の一部は、すべての正直において投機的です。1 私は開発者を本当に知りません!しかし、それは私が下したであろう決定と同じです。実際には...
ファンシーインデックスは非常に強力ですが、それがバニラの一部ではないことをうれしく思いますPythonこれは、通常のリストを操作するときにそれほど深く考える必要がないことを意味します。あなたがそれを必要としないタスク、およびそれが課す認知負荷は重要です。
readersとmaintainersに課される負荷について話していることを覚えておいてください。あなたは頭の中で5次元テンソル積を実行できる天才かもしれませんが、他の人はあなたのコードを読まなければなりません。 numpy
のファンシーインデックスを維持することは、人々が正直にそれを必要としない限りそれを使用しないことを意味します。
たぶん。それは明らかに環境に依存しています。私のマシンでも同じ違いは見られません。
1.投機的ではないナラティブの部分は、科学とエンジニアリングにおけるコンピューティングの特別号(2011 vol。13)で語られた 簡単な歴史 から引用されています。
my_list[:,]
はインタプリタによって次のように翻訳されます
my_list.__getitem__((slice(None, None, None),))
*args
で関数を呼び出すようなものですが、:
表記をslice
オブジェクトに変換します。 ,
がなければ、slice
を渡すだけです。 ,
では、タプルを渡します。
エラーが示すように、リスト__getitem__
はタプルを受け入れません。配列__getitem__
が行います。タプルを渡してスライスオブジェクトを作成する機能は、numpy
(またはその前身)の利便性として追加されたと思います。タプル表記はリスト__getitem__
に追加されていません。 (高度なインデックスの形式を許可するoperator.itemgetter
クラスがありますが、内部的には単なるPythonコード反復子です。)
配列を使用すると、タプル表記を直接使用できます。
In [490]: np.arange(6).reshape((2,3))[:,[0,1]]
Out[490]:
array([[0, 1],
[3, 4]])
In [491]: np.arange(6).reshape((2,3))[(slice(None),[0,1])]
Out[491]:
array([[0, 1],
[3, 4]])
In [492]: np.arange(6).reshape((2,3)).__getitem__((slice(None),[0,1]))
Out[492]:
array([[0, 1],
[3, 4]])
numpy/lib/index_tricks.py
ファイルで、__getitem__
でできる楽しいことの例をご覧ください。あなたはファイルを見ることができます
np.source(np.lib.index_tricks)
ネストされたリストでは、サブリストは包含リストから独立しています。コンテナーには、メモリ内の他の場所にあるオブジェクトへのポインターがあります。
In [494]: my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [495]: my_list
Out[495]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [496]: len(my_list)
Out[496]: 3
In [497]: my_list[1]
Out[497]: [4, 5, 6]
In [498]: type(my_list[1])
Out[498]: list
In [499]: my_list[1]='astring'
In [500]: my_list
Out[500]: [[1, 2, 3], 'astring', [7, 8, 9]]
ここで、my_list
の2番目の項目を変更します。リストではなく文字列です。
リストに[:]
を適用すると、浅いコピーが得られます。
In [501]: xlist = my_list[:]
In [502]: xlist[1] = 43
In [503]: my_list # didn't change my_list
Out[503]: [[1, 2, 3], 'astring', [7, 8, 9]]
In [504]: xlist
Out[504]: [[1, 2, 3], 43, [7, 8, 9]]
ただし、xlist
のリストの要素を変更すると、my_list
の対応するサブリストも変更されます。
In [505]: xlist[0][1]=43
In [506]: my_list
Out[506]: [[1, 43, 3], 'astring', [7, 8, 9]]
私にとって、これは(numpy配列に実装されている)n次元のインデックスによる表示は、ネストされたリストでは意味がありません。ネストされたリストは、その内容が許す範囲でのみ多次元です。それらについては、構造的または構文的に多次元的なものは何もありません。
リストで2つの[:]
を使用しても、ディープコピーは作成されず、ネストの途中で機能しません。浅いコピー手順を繰り返すだけです。
In [507]: ylist=my_list[:][:]
In [508]: ylist[0][1]='boo'
In [509]: xlist
Out[509]: [[1, 'boo', 3], 43, [7, 8, 9]]
arr[:,]
は、view
をarr
にします。 view
とcopy
の違いは、基本的なインデックスと高度なインデックスの違いを理解するための一部です。
したがって、alist[:][:]
とarr[:,]
は異なりますが、リストや配列のコピーを作成する基本的な方法です。どちらも何も計算せず、要素全体を反復しません。したがって、タイミングの比較ではあまりわかりません。
ファンシーインデックスを処理するために、どの
__xx__
メソッドがnumpyオーバーライド/定義されていますか?
__getitem__
は取得、__setitem__
は割り当て。削除の場合は__delitem__
になりますが、NumPy配列は削除をサポートしていません。
(ただし、すべてCで記述されているため、Cレベルで実装されたものはmp_subscript
およびmp_ass_subscript
であり、__getitem__
および__setitem__
ラッパーはPyType_Ready
によって提供されました。__delitem__
も__setitem__
と__delitem__
の両方がCレベルでmp_ass_subscript
にマッピングされるため、削除はサポートされていません。
pythonリストがネイティブにファンシーインデックスをサポートしないのはなぜですか?
Pythonリストは基本的に1次元構造ですが、NumPy配列は任意次元です。多次元索引付けは、多次元データ構造に対してのみ意味があります。
[[1, 2], [3, 4]]
のように、リストを要素として持つリストを作成できますが、リストはその要素の構造を認識していないか、気にしていません。リストがl[:, 2]
インデックスをサポートするようにするには、リストが意図されていない方法でリストが多次元構造を認識する必要があります。それはまた、多くの複雑さ、多くのエラー処理、そして多くの余分な設計上の決定を追加します-コピーはどのくらい深くするべきですかl[:, :]
?構造が不規則である、または一貫性のない入れ子の場合はどうなりますか?多次元索引付けはリスト以外の要素に再帰する必要がありますか? del l[1:3, 1:3]
はどうしますか?
NumPyのインデックス作成の実装を見てきましたが、リストの実装全体よりも長くなっています。 これはその一部です。 NumPy配列があなたがそれを必要とする本当に説得力のあるすべてのユースケースを満たすときにリストにそれをする価値はありません。
なぜnumpyの派手なインデックス付けはpython2でとても遅いのですか?このバージョンでは、numpyのネイティブBLASサポートがないためですか?
NumPyのインデックス付けはBLAS操作ではないため、そうではありません。私は できない再現 そのような劇的なタイミングの違い、そして私が見る違いはマイナーのように見えますPython 3つの最適化、おそらく少し効率的タプルまたはスライスの割り当て。表示されているのは、おそらくNumPyのバージョンの違いによるものです。