以下の内容はhttps://shikaku-sh.hatenablog.com/entry/python-practice-data-structより取得しました。


python データ構造 学習

Python チュートリアルを読んで気になったところをメモ。

データ構造1

squares = list(map(lambda x: x**2, range(10)))
or
squares = [x**2 for x in range(10)]
or
# 冗長な例
squares = [(lambda x: x**2)(x) for x in range(10)]

処理の流れ1:

  • lambda x: x**2 を評価、無名関数のオブジェクトを作る
  • range(10) を評価、range 0-9 を作る(要素は未生成)
  • map(<lambda>, <range>) を評価、map iterator を作る(まだ未計算)
  • list(<map-iterator>) を評価、反復を開始
    • map が range 要素から次の値 x を1つずつ取り出す
    • 取り出した x に無名関数を適用 = x**2 を計算
    • 結果の要素を [] の list object に変換
  • range が終わると反復終了

処理の流れ2: squares = [x**2 for x in range(10)] - range(10) を評価、range 0-9 を作る(要素は未生成) - [] の内包表記で空の list を作成して range 要素から次の値 x を1つずつ取り出す - for 反復ステップ - x を1つずつ(0~9)まで取り出す - x**2 を計算し、結果を list に追加 - x を処理し終えると、出来上がった list がそのまま完成

list()[] の書き方は、簡易的な書き方になっているだけではない。list() は map を評価する働きがあるが [] はそのまま格納する。

なので、以下の書き方だと [] は map をそのまま格納して、処理は終わり。

squares = [map(lambda x: x**2, range(10))]
[<map object at 0x7fd6fb1c3130>]

データ構造2

[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]

or

combs = []
for x in [1,2,3]:
    for y in [3,1,4]:
        if x != y:
            combs.append((x, y))

普段は、1行でいくつもの処理を(意図してたくさん)まとめた書き方をしないので、処理の流れを追いづらかった。

個人的には、わかりやすい下の書き方を選択しがち。シンプルな内容なら内包表記も悪くないと思う。

処理の流れ:

  1. 外側のループ
  2. 最初に for x in [1,2,3] が動く
  3. x は順番に 1,2,3
  4. 内側のループ
  5. x に対して for y in [3,1,4] が動く
  6. y は順番に 3,1,4
  7. if 条件のチェック
  8. (x, y) リストに追加する前に if x != y を評価
  9. True ならリストに入れる

<式> の「最終的にリストに追加する要素」を表す部分が最初にくる。そのあと for で値を処理する。内包表記は、最初に最終的なデータ型のゴールが示されている点は留意したほうがよい。

[ <式>  for 変数 in イテラブル  (if 条件 …) ]

データ構造3

matrix = [
  [1,2,3,4],
  [5,6,7,8],
  [9,10,11,12],
]

[[row[i] for row in matrix] for i in range(4)]

<式> にあたるのは [第2処理] なので、とりあえず配列を得ることがわかる。

そのほかのところは単純で for i in range(4) なので i = 0~3 の繰り返しをするだけ。あとは [第2処理] の中がどうなっているのかを考える。

処理 i = 0:

  • 第2処理のループで row[i] = row[0] を集める:
    • row = [1,2,3,4] → row[0] = 1
    • row = [5,6,7,8] → row[0] = 5
    • row = [9,10,11,12] → row[0] = 9 [] はそのままリスト化するので [1,5,9] になる

あとも流れは同じ:

  • [1,5,9]
  • [2,6,10]
  • [3,7,11]
  • [4,8,12]

それぞれの list を外側の list に追加する。

tuple

私の tuple の理解に、すこし間違っているところがあった。

empty = ()

空の tuple を作るときは上のようにするから、1つの tuple を作るときも下のようにしていた。

single = ('data', )
or
single = 'data',

しかし、実際は丸括弧を必要としていない。0個の tuple を作るときが特別に括弧を必要とするのであって、以降は丸括弧を必要としていない。(丸括弧は、処理のグループ化をサポートしているので、全く無意味ではないと思う)

補足:C# ではデータが1個の tuple というのは存在しないはず。0個は作れる。python だと unpacking の都合もあって、一貫した書き方ができたほうが C# よりも都合がよい。

dict

ループのテクニックの整理:

  • .key() キーだけ
  • .values() 値だけ
  • .items() (key, value) の tuple

同じく enumerate() も index を割り当てした iterator を返却する。この時点で tuple のリストに即変換はされていない。中身を見たいときは list() で展開を指示しないとダメ。(通常は iterable なものを受け入れ可能な箇所に対して、そのまま enumerate object は代入できる)

list(enumerate(['tic','tac','toe']))
# => [(0, 'tic'), (1, 'tac'), (2, 'toe')]

内包表記の整理

内包表記は日本語の表記ですが、もともとは comprehension の「含有」の意味です。

  • list comprehension []
    • 結果は list
  • set comprehension {}
    • 結果は set
  • dictionary comprehension {}
    • 結果は dict
  • generator expression ()
    • ※結果は 遅延評価をする iterator

前の3つは、その時にコレクションをそれぞれ構築してしまうけど generator だけは、遅延評価をする。(なので、大きなデータを作るときは処理的に有利)

gen = (x**2 for x in range(1000000))
next(gen)  # 0
next(gen)  # 1

set と dict は同じような表記をしてしまう。歴史的には dict が先にあって、あとから set が用意されているはず。

理由として、空データを用意する方法を見る。空集合だけは書き方を区別できないので、先に用意されていた dict の書き方がシンプルになっている。(わずかな部分だけど、一貫性にケチがついているようにも見える)

li = ()
se = set()
di = {}

参考




以上の内容はhttps://shikaku-sh.hatenablog.com/entry/python-practice-data-structより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14