len, random.randrange, timeit.Timerの使い方と引数の意味について


お疲れ様です。pythonに余り触れたことのない初心者です。  
下記のプログラムについて、幾つか不明な点があるため質問させて頂きます。

  1. `randrange(len(x) - 1 )` とはどこまでの範囲なのでしょうか？  
`len(x)`とは何でしょう？-1の理由がいまいち分かりません...

  2. `random_index`とは`del_dict_items`という名の関数に`randrange`の範囲でランダムな要素を入れるという理解でよろしいのでしょうか？
  3. `except KeyError` で`setdefault`で`index`に`None`という要素を入れた時、なぜ`KeyError`が実行されるようになるのでしょうか？  
`None`を要素を入れたら辞書型の`del`は実行できる、という理解でよろしいのしょうか。

  4. `timeit.Timer` の `（）` の中の `from __main__ import random` とは何でしょう？また `t.dict` の`timeit.timer` の `（）` の `del_dict_items(x)` と `del_dict_items` の違いは何でしょう？

以上が私が理解できなかった部分です。  
お手数ですが、手助け頂ければ幸いです。

[code]

    import timeit
    import random
    
    def del_dict_items(x):
        random_index = random.randrange(len(x) - 1)
        try:
            del x[random_index]
        except KeyError:
            x.setdefault(random_index, None)
            del x[random_index]
    
    print("i\t\tlist_del_time\t\tdict_del_time")
    for i in range(100000, 1000001, 20000):
        t_list = timeit.Timer("del x[random.randrange(len(x)-1)]", "from __main__ import random, x")
        t_dict = timeit.Timer("del_dict_items(x)", "from __main__ import random, x, del_dict_items")
        x = list(range(i))
        list_del_time = t_list.timeit(number=1000)
        x = {j:None for j in range(i)}
        dict_del_time = t_dict.timeit(number=1000)
        print("%d %10.3f %20.3f" %(i, list_del_time, dict_del_time))
    
[/code]




質問する前にGoogleで検索したり、ドキュメントを読んだりした上で、何が分かって何が分からないのかを記載することをおすすめします。

# `randrange(len(x) - 1 )` とは

[random.randrange](https://docs.python.org/ja/3/library/random.html#random.randrange)
のドキュメントによると、最大の値が引数、ここでは `len(x) - 1` になるようなランダムな整数を返します。

また、 [組み込み関数の
`len(x)`](https://docs.python.org/ja/3/library/functions.html#len)は、引数の要素数を返します。

ここで、 `del_dict_items` という関数では、引数として与えられた `dict` 型のランダムな要素を削除しようとしているようです。このため、
`random_index` に要素数-1のランダムな値をインデックスとして代入しているようです。

# random_indexとはdel_dic_itemsという名の関数にrandrangeの範囲でランダムな要素を入れるのか

いいえ。

コメントしたように正しくコードを記載されていないので正しく回答できませんが、少なくともこの関数の目的は要素を挿入することではなく、要素をランダムに削除することです。

# except Keyerror でsetdefaultでindexにNoneという要素を入れた時、なぜKeyerrorが実行されるようになるのか

認識されている順序が逆です。

「setdefaultでindexにNoneという要素を入れた時にKeyErrorが発生する」ではなく、「KeyErrorが発生したときにsetdefaultでindexにNoneという要素を入れる」が正しいです。

# None要素を入れたら辞書型のdelは実行できるか

はい。 `dict.setdefault` は 第一引数に対応する値があればそれを、さもなくば第二引数を値としてセットしたあとにそれを返す関数です。  
[dict.setdefault](https://docs.python.org/ja/3/library/stdtypes.html#dict.setdefault)

_そもそも`del` するからには分岐は不要で、 `x[random_index] = None` などしておけばtry-catchは不要ですが……_

# timeit.Timer の （） の中の from **main** import random とは

`__main__` は現在実行中のモジュールを指します。おそらく、このスクリプト自体を指して、それを `timeit.Timer`
から呼び出しているようです。

# t.dict のtimeit.timer の （） の del_dict_items(x) と del_deict_items の違い

`del_deict_items` という記載はなさそうです。typoでしょうか？

