応用プログラミング及び実習 2019年度 第6回 Notebook

Python の基礎 (2)

この資料中に説明なくページ番号が出てくる場合,教科書のページ番号を表しています.

これまでの復習+α

復習として,次の問題をやってみましょう: 「以下の3つのセルを上から順に実行すると,リストdataの最小値・最大値とそれぞれの要素番号が正しく出力されるように,上の方のセルに関数を定義しなさい」

In [ ]:
# このセルに関数の定義(どちらか一方)を書く
In [ ]:
# もう一方の関数の定義を書く
In [ ]:
data = [ -5, -9, -6, -3, -4, -6, -4, -9 ]
# 上記の関数たちを呼ぶ(このセルの内容は変更不可)
i = myargmin(data)
j = myargmax(data)
print("最小値は{2}{0}番目),最大値は{3}{1}番目)です".format(i, j, data[i], data[j]))
#  ↑ この print文の書き方についてはあとで触れるので,ここでは気にしないでよい

リスト型,タプル型を使いこなす (pp.182-189)

前回の課題Bでは,教科書の「Chap04-03 文字列型を使いこなす」(pp.167-176)の内容を自習しました. ここでは,「Chap04-05」の内容を学習しましょう(教科書と順序変えてます).

スライスの話(pp.186,187)

In [ ]:
L = [ 0, "位置", "荷", "酸", "詞", "碁", "禄", "質", "蜂", "句", "beast"]
L

上記のリスト L に対して,教科書に載ってる「リストからスライスで要素を取り出す」を試してみよう.

In [ ]:
 
In [ ]:
 
In [ ]:
 

同様に,「要素の追加」,「要素の削除」をやってみよう.

In [ ]:
 
In [ ]:
 

リストで利用できるメソッド(pp.187-189)

In [ ]:
L = [ 0, "位置", "荷", "酸", "詞", "碁", "禄", "質", "蜂", "句", "beast"]
L

上記のリスト L に対して,教科書に載っているリスト型のメソッドをいろいろ使ってみよう.セルの実行順序に注意.リストの内容を変更するメソッドを複数実行する場合,その順序で結果が変わりますね.元の L に対して動作確認したいなら,「↑のセルを実行」→「直後に対象のセルを実行」というのもよいでしょう.

In [ ]:
 
In [ ]:
 
In [ ]:
 

リストをソートする(pp.182-185)

In [ ]:
monk_fish_team = [158, 157, 163, 157, 145]
monk_fish_team

上記のリストを昇順/降順にソートしよう.

In [ ]:
 
In [ ]:
 

「ソート順をカスタマイズする」はスキップします.

アンパック代入 (pp.185,186 および pp.201,202)

pp.185, 186を読んで,以下を実行してみましょう.

In [ ]:
x = 4649
y = 5963
y, x = x, y
print(x, y)

上記の3行目のように複数のものを , 区切りならべ,まとめて代入する方法を アンパック代入 といいます.ここでは二つの変数をならべてますが,もちろん3つ以上ならべることもできます.

C言語なら第3の変数を用意しないといけないところですね.

int x = 4649, y = 5963
int w;

w = x;
x = y;
y = w;

実はこの仕組みはタプルを利用しています.たとえば,変数 hoge を次のようなタプルとしてみると...

In [ ]:
hoge = ("椀", "通", "スリ")

次のような代入が可能です.

In [ ]:
one, two, three = hoge
print(one, two, three)

Python では,明示的に (, ) で囲まなくても, , 区切りで複数の要素をならべるとタプルと解釈してくれるのです.

アンパック代入と同様のことが,関数の戻り値の受け渡しでもできます.

In [ ]:
def f(a, b):
    return a+b, a-b, a*b, a//b
In [ ]:
s, t, u, v = f(2, 6)
print(s, t, u, v)

次のように引数をまとめて受け取ってみると,戻り値がタプルになっていることがわかります.

In [ ]:
stuv = f(2, 6)
stuv

復習のところで myargmin()myargmax() を定義していれば,次のような関数を定義できますね.

In [ ]:
def myargminmax(x):
    return myargmin(x), myargmax(x)
In [ ]:
data = [ -5, -9, -6, -3, -4, -6, -4, -9 ]
# 上記の関数たちを呼ぶ(このセルの内容は変更不可)
i, j = myargminmax(data)
print("最小値は{2}{0}番目),最大値は{3}{1}番目)です".format(i, j, data[i], data[j]))
#  ↑ この print文の書き方についてはあとで触れるので,ここでは気にしないでよい

アンパック代入の話とは別ですが,最小値最大値の要素番号を同時に求めたいなら,1つの関数内で1つのてループで処理した方が効率良さそうですね.

というわけで,以下に最小値最大値の要素番号を返す関数 myargminmax2() の定義を書いて,上記の関数呼び出しをそちらを呼ぶように書き換えて動作確認してみましょう.

In [ ]:
# 関数 myargminmax2() の定義

文字列のフォーマット(pp.177-181)

文字列に対する format() メソッドを使うと,C言語の printf() における書式指定文字列("%d" みたいなの)でできることと似たことができます.ここでは簡単に紹介しますが,たくさん機能があって便利です.詳しくは教科書やウェブリファレンスへ.

In [ ]:
S = "そいつぁ{}だねぇ"
print(S.format("ロック"))
print(S.format("ほげ"))
print(S.format("あれ"))
In [ ]:
S2 = "「{}」だって? そいつぁ{}だねぇ"
for s in ["ロック", "ほげ", "あれ"]:
    print(S2.format("ほげほげ", s))  # format() の引数の順に {} が置換される.数が合わないとエラー
In [ ]:
S3 = "{2}:「{1}のくせになまいきだ」\n{0}:「{1}くん,きみはじつにばかだな」"
SS = S3.format("ドラえもん", "のび太", "ジャイアン")  # format() の引数順に {0}, {1}, {2} が置換される
print(SS)
In [ ]:
import math
print("{0:05d}   {1:.1f}   {1:.3f}   {1:.5f}".format(123, math.pi))

ディクショナリ型を使いこなす (pp.191-195)

上記ページ範囲の内容については,授業では説明しません.必要に応じて教科書やウェブリファレンスを頼りましょう.

Pythonのファイル処理(pp.217-228)

Python でファイルを扱う方法をちょっとだけ学びます.

準備,ファイル読み書きの手順

準備として,エディタに以下の内容をコピペして(JupyterLabでは,Shiftキー押しながら右クリックするとよいでしょう),いつもの場所に06constants.txt というファイル名のテキストファイルとして保存しましょう.

円周率   3.1415926535
自然対数の底    2.7182818284
3次元最密充填の平均密度   0.74048
チャンパーノウン定数   0.12345678910
コープランド–エルデシュ定数    0.2357111317

(よだんだよん) これらの定数の値はいずれも正確ではありません.適当な桁数で切り捨て/四捨五入した近似値です.

自分の作ったプログラム等でファイルを扱う場合,多くのプログラミング言語では,

  1. ファイルを開く
  2. ファイルの内容を読み書きする
  3. ファイルを閉じる

という手順を踏んだプログラムを書きます.Python でも同様です.

ファイルから読み込む(ファイルからの入力)

ファイルを開くには,組み込み関数 open() を使います( https://docs.python.jp/3/library/functions.html#open ).

例えば,次のようにすると,カレントディレクトリ(この Jupyter Notebook が動いている現在地(ディレクトリ))内のファイル 06constants.txt読み込みモード で開きます(モードの詳細については教科書参照).

(よだんだよん) テキストファイルを扱う際には,その文字コードや改行の種類(改行の仕方は環境によって複数あるのです)を気にすることが必要になる場合があります.この授業ではその辺りについては触れません.興味のある人は教科書で学んでください.

In [ ]:
# 1. ファイルを開く
f = open("06constants.txt", "r")

open() は,ファイルを開くことに成功した場合,戻り値として開いたファイルを指す ファイルオブジェクト というものを返します. 読み書き等は,このファイルオブジェクトのメソッドを利用して行います.試しに readline()メソッド(p.224)使ってみましょう.

In [ ]:
# 2. ファイルの内容を読み書きする
s = f.readline()
print(s)
s = f.readline()
print(s)

readline() は,1行文を読み込んでその内容を戻り値として返してくれます.その文字列には文末の改行文字も含まれているので,print()関数を使って出力すると上記のようになります(文字列中の改行文字を出力した後でprint()も改行を出力するので).

読み込みはここまでとして,ファイルを閉じることにしましょう. ファイルオブジェクトに対する close() メソッドを使います.

In [ ]:
# 3. ファイルを閉じる
f.close()

さて,いったん閉じたファイルを再度開くとどうなるか,#1. ファイルを開く から順にもう一度実行してみましょう.

結果は変わりませんね? モード "r" (読み込みモード)でファイルを open() すると,常にファイルの先頭から読み込みをはじめることになります.

テキストファイルを読み込むには上記のように readline() メソッドを使ってもよいのですが,次のようにファイルオブジェクトを直接 for文に渡してループさせる方が簡単です.

In [ ]:
f = open("06constants.txt", "r")
for line in f:   #  f が指すファイルから1行ずつ読み込んだ内容を変数 line に代入してループ
    print(line)
f.close()

ファイルへ書き込む(ファイルへの出力)

次は,文字列をテキストファイルに書き込んでみましょう.

In [ ]:
S1 = "hoge   12345  fuga   "
S2 = "ほげ〜ほげ〜   ほげほげ〜"

新しく "06hoge.txt" というファイルを作って上記の2つの文字列を1行ずつ書き込むことにします.

まず,open()関数の引数に "w" を指定して,書き込みモードでファイルを開きます.

In [ ]:
f = open("06hoge.txt", "w")

書き込みモードで開く場合,ファイルが存在しなければ作成されます.存在していた場合,開いた瞬間に空になります. ファイル名の間違いやモードの間違いに気をつけましょう.

読み込み同様,書き込みにもファイルオブジェクトに対するメソッドを使います. 例えば,write() メソッドを使うと,文字列をファイルに書き込めます.

In [ ]:
f.write(S1 + "\n")
f.write(S2 + "\n")
f.close()

この例では文字列 S1S2 の末尾に改行がありませんので,末尾に連結してから write() メソッドに渡してます.

ちゃんとファイルができたかどうか,lsless で確認してみましょう.

$ ls  06hoge.txt
$ less 06hoge.txt
(lessの終了は `q` (アルファベットのキュー))

練習として,このファイル 06hoge.txt を読み込んでその内容を1行ずつ表示するプログラムを書きましょう.

In [ ]:
# ここに,06hoge.txt を読み込んで1行ずつ表示するコードを書く

for line  in :
    print(line, end="")   # こうすると,print() 自身が最後に改行を出力するのを抑制できる
f.close()

with 文の使用

上記の例は,with文というものを使って次のように書くこともできます.

In [ ]:
with open("06constants.txt", "r") as f:
    for line in f:
        print(line, end="")

with文については,p.340に載っています.f.close()がありませんが,書き忘れではなく,必要ありません.この書き方では,open()が成功した場合,with文のインデントブロックの実行を終えるタイミングで自動的にファイルが閉じられます.

with文についてちゃんと説明するためには,例外処理というものを学ぶ必要があるのですが,この授業ではそこへは立ち入りません.ただし,実用的なプログラムを書くときには大事な話です(例えば,ファイルを開く際には,「(読み込みモードで開こうとしたが)ファイルが存在しない」とか「ファイルを作れない」といった場合への対処が必要). 興味のあるひとは,教科書p.335からのChapter10を参照してください.

例は載せませんが,書き込みの場合も同様に書くことができます.

補足いろいろ

  • パスの話
  • バイナリモード
  • print でファイル出力

(1) これまでの例では open() の第1引数にファイル名を指定していましたが,実際にはディレクトリ名を含む パス (path) を書くことができます. 例えば, Linux や macOS で /(ルートディレクトリ)の中の hoge というディレクトリの中の fuga というディレクトリの中の hena というファイルを指定するなら,

open("/hoge/fuga/hena", "r")

のように書けばok(こういうファイルのありかの指定は絶対パスというのでした).もちろん,

open("../hoge/hena", "r")

のような指定の仕方(相対パス)も可能です.

(2) ここでは open() で指定するモードとして「読み込み("r")」 と「書き込み("w")」の2種類のみ説明しましたが,他にもいろいろあります(p.221参照). 画像や音声のようなデータを扱う際は,バイナリモードというもののお世話になるかもしれません. ちなみに,p.220 や https://docs.python.jp/3/library/functions.html#open を読むとわかりますが,open() に第1引数のみ指定した場合, "r"を指定したのと同じになります.

(3) ここではファイルへ書き込む処理としてファイルオブジェクトの write() メソッドを使う例を示しましたが,print()関数のキーワード引数 file にファイルオブジェクトを指定する方法もあります(ついでに with文使う例にもしました).

cf. https://docs.python.jp/3/library/functions.html#print

In [ ]:
with open("06hoge.txt", "w") as f:
    print(S1, file=f)
    print(S2, file=f)

チェック

TAさんに課題Aのチェックを受ける際は,以下のセルを実行して「OK!」が出ているようにしてください.

In [ ]:
# 以下のコードは,この notebook を(それなりに)ちゃんと実行したかどうか確認するためのものです.
# 中身の理解は不要です.

fnpy = '/roes/sample/takataka/aprog/aprogCheck.py'
import os
if not os.path.isfile(fnpy):
    print('この環境では確認できないようです')
else:
    import sys
    sys.path.append('/roes/sample/takataka/aprog/')
    import aprogCheck
    aprogCheck.check('06note', In)