Python演習ドリル200 基礎練習61〜70

Pythonプログラムを学び始めた方向けの演習ドリルです。

今回は、基礎練習の61〜70です。

jupyterとjupyter notebook

本演習ではjupyter notebookを使用します。 インストールされているか確認してください。 インストールされていない場合、jupyterjupyter notebookのインストール方法については、レッスン33を参照してください。

演習前の基礎学習

実際にjupyter notebookを使用して演習を行う前に、演習の中で使用するPythonの機能について学習しましょう。

  • リスト(配列)つづき

    • スライス

      スライスはレッスン125の文字列で学んだ文字列のスライスとほぼ同じですが、復習として説明します。

      スライスでは選択範囲の開始位置startと終了位置stopリスト[start:stop]のように書きます。 すると、start <= x < stopの範囲が選択されます。 start番目の値は含まれますが、stop番目の値は含まれないことに注意してください。

      l = [0, 10, 20, 30, 40, 50, 60]
      print(l[2:5])
      >> [20, 30, 40]
      

      スライスで指定する位置(インデックス)は以下のとおりです。

       +---+---+---+---+---+---+---+
       | 0 | 10| 20| 30| 40| 50| 60|
       +---+---+---+---+---+---+---+
       0   1   2   3   4   5   6   7
      -7  -6  -5  -4  -3  -2  -1
      

      開始位置startを省略すると先頭(最初)から、終了位置stopを省略すると末尾(最後)までが選択されます。 両方とも省略するとすべての要素が選択されます。

      l = [0, 10, 20, 30, 40, 50, 60]
      print(l[:3])
      >> [0, 10, 20]
      
      print(l[3:])
      >> [30, 40, 50, 60]
      
      print(l[:])
      >> [0, 10, 20, 30, 40, 50, 60]
      
      • 範囲外を指定した場合

        要素数を超える位置を指定してもエラーにならず無視されます。

        l = [0, 10, 20, 30, 40, 50, 60]
        print(l[2:10])
        >> [20, 30, 40, 50, 60]
        
      • 空のリストが返る場合

        どの要素も選択されないstartstopを指定してもエラーにはならず、空のリストが返ります。

        l = [0, 10, 20, 30, 40, 50, 60]
        print(l[5:2])
        >> []
        
        print(l[2:2])
        >> []
        
        print(l[10:20])
        >> []
        
      • 増分stepを指定

        startstopに加えて、増分stepも指定可能です。 [start:stop:step]のように書きます。

        例えば、step2とした場合、奇数個目または偶数個目の要素のみを取得できます。

        l = [0, 10, 20, 30, 40, 50, 60]
        print(l[::2])
        >> [0, 20, 40, 60]
        
        print(l[1::2])
        >> [10, 30, 50]
        

        その他の例です。

        l = [0, 10, 20, 30, 40, 50, 60]
        print(l[::3])
        >> [0, 30, 60]
        
        print(l[2:5:2])
        >> [20, 40]
        

        なお、stepを省略した場合はstep=1となります。

      • マイナスの値で後ろから指定

        • 開始位置startと終了位置stopをマイナスで指定

          開始位置startと終了位置stopを負の値で指定すると、末尾からの位置になります。 先ほど説明したスライスで指定する位置(インデックス)の表を参照してください。

          l = [0, 10, 20, 30, 40, 50, 60]
          print(l[3:-1])
          >> [30, 40, 50]
          
          print(l[-2:])
          >> [50, 60]
          
          print(l[-5:-2])
          >> [20, 30, 40]
          
        • 増分stepをマイナスで指定

          増分stepを負の値で指定すると、後ろから逆順で要素を取得します。

          startから逆順に値を取得していくため、startのほうがstopより後ろの位置を示していないと空のリストを返すので注意してください。

          l = [0, 10, 20, 30, 40, 50, 60]
          print(l[5:2:-1])
          >> [50, 40, 30]
          
          print(l[2:5:-1])
          >> []
          

          その他の例です。

          l = [0, 10, 20, 30, 40, 50, 60]
          print(l[-2:-5:-1])
          >> [50, 40, 30]
          
          print(l[-2:2:-1])
          >> [50, 40, 30]
          
          print(l[5:2:-2])
          >> [50, 30]
          

          stepが負の値の場合、startを省略すると末尾から、stopを省略すると先頭までが選択されます。 両方とも省略するとすべての要素が選択されます。

          startstopを省略し、step-1とすることでもとのリストの順番を逆転したリストを取得することができます。

          l = [0, 10, 20, 30, 40, 50, 60]
          print(l[:3:-1])
          >> [60, 50, 40]
          
          print(l[3::-1])
          >> [30, 20, 10, 0]
          
          print(l[::-1])
          >> [60, 50, 40, 30, 20, 10, 0]
          
    • indexメソッド

      リスト.index(要素)のようにindexメソッドを実行すると、先頭から最初に要素に一致した位置(インデックス)を返します。 存在しない要素を指定するとエラーになります。

      nums = [1, 1, 2, 3, 3]
      print(nums.index(1))
      >> 0
      
      print(nums.index(2))
      >> 2
      
      print(nums.index(3))
      >> 3
      
      print(nums.index(0))
      >> ValueError: 0 is not in list
      
    • 累算代入演算子

      +=演算子を使用すると、リストにリストを追加します。 リスト1 += リスト2 でリスト1の要素の後ろにリスト2の要素がコピーされて追加されます。 なお、リスト2の値は変わりません。

      l1 = ['a', 'b', 'c']
      l2 = ['d', 'e', 'f']
      l1 += l2
      print(l1)
      >> ['a', 'b', 'c', 'd', 'e', 'f']
      
      print(l2)
      >> ['d', 'e', 'f']
      

      l1 = l1 + l2のように書いても同じ結果が得られます。

      追加される要素は元のリストからコピーされるので、要素がリストや辞書などの場合、追加された要素の内容を変更すると元のリストの要素の内容も変更されてしまうので注意が必要です。 (逆に、元のリストの要素の内容を変更すると、追加された要素の内容も変更されます)。

    • insertメソッド

      リスト.insert(インデックス、要素)のようにinsertメソッドを実行すると、リストのインデックスの位置に要素を挿入します。

      インデックスの先頭は0、負の値の場合はスライス同様で、-1が末尾(最後)の1つ前になります。

      l = ['a', 'b', 'c']
      l.insert(1, 100)
      print(l)
      >> ['a', 100, 'b', 'c']
      
      l.insert(0, 200)
      print(l)
      >> [200, 'a', 100, 'b', 'c']
      
      l.insert(-1, 300)
      print(l)
      >> [200, 'a', 100, 'b', 300, 'c']
      
      l.insert(-2, 400)
      print(l)
      >> [200, 'a', 100, 'b', 400, 300, 'c']		
      

      なお、インデックスに範囲外の値を指定してもエラーにはならず、先頭より前の値を指定すると先頭に、末尾より後の値を指定すると末尾に値が挿入されます。

      l = ['a', 'b', 'c']
      l.insert(100, 100)
      print(l)
      >> ['a', 'b', 'c', 100]
      
      l.insert(-100, 200)
      print(l)
      >> [200, 'a', 'b', 'c', 100]	
      
    • removeメソッド

      リスト.remove(要素)のようにremoveメソッドを実行すると、指定した要素と同じ値の要素を検索し、最初に検索された要素を削除します。 指定した要素と一致する要素がリストに複数含まれる場合は、一番前にある要素1つだけ削除されます。

      なお、指定した要素と同じ値の要素が存在しない場合は、エラーになります。

      l = ['Alice', 'Bob', 'Charlie', 'Bob', 'Dave']
      l.remove('Alice')
      print(l)
      >> ['Bob', 'Charlie', 'Bob', 'Dave']
      
      l.remove('Bob')
      print(l)
      >> ['Charlie', 'Bob', 'Dave']
      
      l.remove('Eric')
      >> ValueError: lis.remove(x): x not in list
      
    • popメソッド

      リスト.pop(インデックス)のようにpopメソッドを実行すると、指定したインデックスの位置の要素を削除し、その値を取得します。 インデックスの先頭(最初)は0、負の値を指定するとスライス同様末尾からの位置を指定できます。 末尾(最後)は-1です。

      引数を省略してリスト.pop()とすると、末尾(最後)の要素を削除します。

      なお、存在しない位置を指定するとエラーになります。

      l = [0, 10, 20, 30, 40, 50]
      popped_item = l.pop(0)
      print(popped_item, l)
      >> 0 [10, 20, 30, 40, 50]
      
      popped_item = l.pop(3)
      print(popped_item, l)
      >> 40 [10, 20, 30, 50]
      
      popped_item = l.pop(-2)
      print(popped_item, l)
      >> 30 [10, 20, 50]
      
      popped_item = l.pop(100)
      >> IndexError: pop index out of range
      
    • del文

      del文を使い、インデックス・スライスで位置・範囲を指定してリストの要素を削除することができます。

      del リスト[インデックス]のように削除したい要素をインデックスで指定します。 インデックスは先頭(最初)は0、負の値は末尾(最後)が-1になります。

      l = [0, 10, 20, 30, 40, 50]
      del l[0]
      print(l)
      >> [10, 20, 30, 40, 50]
      
      del l[3]
      print(l)
      >> [10, 20, 30, 50]
      
      del l[-1]
      print(l)
      >> [10, 20, 30]
      
      del l[-2]
      print(l)
      >> [10, 30]
      

      del リスト[スライス]のようにスライスを使用すると、選択した範囲の要素を一括で削除できます。 全範囲を指定してすべての要素を削除することも可能ですし、増分stepを指定して、飛び飛びの複数の要素を一括で削除することもできます。 スライスの使い方については、すでに説明済みのスライスの項目を参照してください。

      l = [0, 10, 20, 30, 40, 50]
      del l[2:5]
      print(l)
      >> [0, 10, 50]
      
      l = [0, 10, 20, 30, 40, 50]
      del l[:3]
      print(l)
      >> [30, 40, 50]
      
      l = [0, 10, 20, 30, 40, 50]
      del l[-2:]
      print(l)
      >> [0, 10, 20, 30]
      
      
      l = [0, 10, 20, 30, 40, 50]
      del l[:]
      print(l)
      >> []
      
      l = [0, 10, 20, 30, 40, 50]
      del l[::2]
      print(l)
      >> [10, 30, 50]
      
    • 内包表記

      リスト内包表記は以下のように書きます。

      [式 for 任意の変数名 in イテラブルオブジェクト]
      

      リストやタプル、rangeなどのイテラブルオブジェクトの各要素を任意の変数名で取り出し、で評価し、その結果を要素とする新たなリストを作成します。

      例えば、rangeの要素(連番)を取り出し式で2乗すると以下のようなリストを作成することができます。

      square = [i**2 for i in range(5)]
      print(square)
      >> [0, 1, 4, 9, 16]
      

      内包表記を使わずに同じことをするには、以下のような処理が必要になります。

      square = []
      for i in range(5):
      	squares.append(i**2)
      
      print(square)
      >> [0, 1, 4, 9, 16]		
      
      • ifで条件分岐した内包表記

        ifで条件分岐することも可能です。以下のようにifを後に付けて書きます。

        [式 for 任意の変数名 in イテラブルオブジェクト if 条件式]
        

        条件式Trueとなるイテラブルオブジェクトの要素のみで評価され、その結果が要素となる新たなリストが返されます。 Falseとなる要素は無視されます。

        条件式の中でも任意の変数名を使用することができます。

        例えば、rangeの要素(連番)から奇数のみを取り出してリストを作成してみます。

        odds = [i for i in range(10) if i % 2 == 1]
        print(odds)
        >> [1, 3, 5, 7, 9]
        

        内包表記を使わずに同じことをするには、以下のような処理が必要になります。

        odds = []
        for i in range(10):
        	if i % 2 == 1:
        		odds.append(i)
        
        print(odds)
        >> [1, 3, 5, 7, 9]		
        
      • 三項演算子(if else)との組み合わせ

        条件分岐では、条件を満たす要素のみが処理され、条件を満たさない要素は除外されます。

        if else のように条件によって処理を切り替えたい場合は以下の三項演算子を使用します。

        Trueのときの値 if 条件式 else Falseのときの値
        

        これを以下のように内包表記の式の部分に使用します。

        [Trueのときの値 if 条件式 else Falseのときの値 for 任意の変数名 in イテラブルオブジェクト]
        

        以下の例では、rangeの要素(連番)を取り出し、奇数は'odd'、偶数は'even'に置き換えます。

        odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
        print(even_odd)
        >> ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
        

        内包表記を使わずに同じことをするには、以下のような処理が必要になります。

        odd_even = []
        for i in range(10):
        	if i % 2 == 1:
        		odd_even.append('odd')
        	else:
        		odd_even.append('even')
        
        print(even_odd)
        >> ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
        

        Trueのときの値Falseのときの値任意の変数名を使った式を記述することもできます。

      • zip関数、enumerate関数との組み合わせ

        今後のレッスンで説明する複数のイテラブルをまとめるzip関数や、以前のレッスンで説明したインデックスと値をペアで返すenumerate()などと組み合わせることも可能です。

        zip関数の例です。

        l1 = ['a', 'b', 'c']
        l2 = [10, 20, 30]
        
        l = [(s1, s2) for s1, s2 in zip(l1, l2)]
        print(l)
        >> [('a', 10), ('b', 20), ('c', 30)]
        
        enumerate関数の例です
        
        ```python
        l1 = ['a', 'b', 'c']
        l = [(i, s) for i, s in enumerate(l1)]
        print(l)
        >> [(0, 'a'), (1, 'b'), (2, 'c')]
        

        さらにこれらにifを付けることもできます。

      • ネスト(階層化)した内包表記(多重ループ)

        forループの中にforループがある多重ループのように、内包表記も同様の多重ループが使えます。

        [式 for 変数名1 in イテラブルオブジェクト1
        	for 変数名2 in イテラブルオブジェクト2
        		for 変数名3 in イテラブルオブジェクト3...]
        

        便宜上、改行とインデントを加えているが、1行で続けて書いても構いません。

        range関数を使って二重ループで内包表記を行う例です。

        l = [(x, -y) for y in range(3) for x in range(3)]
        print(l)
        >> [(0, 0), (1, 0), (2, 0), (0, -1), (1, -1), (2, -1), (0, -2), (1, -2), (2, -2)]
        
      • 多次元配列の作成

        配列の*演算子のところで説明したように、多次元配列を*演算子で作成しようとすると意図しない結果になります。

        l = [[0] * 3] * 3
        print(l)
        >> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        
        l[0][0] = 1
        print(l)
        >> [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
        

        上記のように配列の中に同じ配列のコピーが3個できるので独立の2次元配列にはなりません。

        このような場合、内包表記を使うことでこの問題を解決できます。

        l = [[0] * 3 for _ in range(3)]
        print(l)
        >> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        
        l[0][0] = 1
        print(l)
        >> [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
        

        このように、内包表記を使うと簡単に独立の多次元配列を作成することができます。

演習

それでは実際にjupyter notebookを使用して、演習を行います。

コマンドラインからgit pullを実行してmcc1フォルダに最新のファイルをダウンロードしてください。

ターミナルのコマンドラインからcd mcc1/code/jupyterを実行して、jupyterフォルダに移動し、jupyter notebookを実行してください。

すると、ブラウザが起動し、jupyterのホーム画面が表示されます。

その中からPython演習ドリル200基礎練習61〜70.ipynbをクリックしてください。

そうするとPython演習ドリル200基礎練習61〜70というタイトルの演習問題が表示されます。

課題61から課題70までありますので、課題の内容にしたがって、In[]:の部分にPythonプログラムを書いてください。 書き終わったら、画面上の ▶| Run というアイコンをクリックすると、今書いたPythonプログラムが実行されます。

出力結果が課題の(実行画面)と一致するか確認してください。

どうしても正解がわからないときは、▶プログラム例 をクリックすると、正解例のPythonプログラムが表示されます。

まとめ

今回はPython演習として、リストの続きを行いました。

今後も新しい演習問題を追加していきますので、演習を通じてPythonを学んでいきましょう。