「プログラミング演習 Python」の気づき

サマリ

動機

Pythonの基礎を固めておくため。詰将棋に近い感覚で確実にできるように。

感想

  • コンピュータの処理の基本から丁寧に書かれているので独学でも入りやすい。
  • 簡単なことを、簡単に(違和感なく)説明してくれているのがありがたい。

学んだこと

コンピュータとプログラミング

コンピュータが一度にできる動作は単純なもので、情報処理の複雑な仕事は、単純な動作の組み合わせをプログラムとして示すことで実現している

ハードウェアの主要な構成要素はCPUとメモリ。

プログラミング言語とは大きく2つの考え方で成り立っている

  • 数式などの人間にわかりやすい形でプログラムを書くルールを定める(仕様)
  • ルールに従ったプログラムを実行するプログラムを作成する(処理)

プログラミング言語の処理系は大きく

  1. コンパイラ方式:一旦機械語に翻訳してから実行する
  2. インタープリタ方式:ソースコードを逐次的に解釈し、実行する
  3. 中間コード方式:CPU用の機械語ではなく、言語用の仮装計算機の機械語コンパイラし、それをインタープリタ方式で実行する

カナヅチとノコギリを使える事と、家を建てられることは別。家の構成を理解しないと使えない。

「理解して実行」ではなく、「実行を通じて理解」する

変数と演算、代入

上から順にプログラムを実行することを「順次実行」という。特に指定がなければ順次実行を行う。

情報は変数に代入されるため、情報の流れは実行の流れと比べて分かりにくくなっている。

Pythonには標準で定数はサポートされていないようだ。

予約語を変数名に使用した時は以下のようにエラー表示される。

while = 0
=> SyntaxError: invalid syntax

float型におけるE8は*108のこと

a = 1E4
b = 1.3E-3
print(a,b) # 10000.0 0.0013

イミュータブルなintはidが同じオブジェクトを指しているが、リストは新しく生成されることがわかる。

a = 1
b = 1
c = [1,2,3]
d = [1,2,3]
print(id(a) == id(b)) # True
print(id(c) == id(d)) # False
print(id(c[0]) == id(d[0])) # True

優先順位の強い*や/はスペースを空けず、+や-を離すように書くとコードを読みやすい。

制御構造

繰り返しを行う場合は、初期設定を固定させたい、変更したいのかによって繰り返しの内外どちらで設定するかを判断する。(この場合2周目以降でr1を更新したいので、r1=xのような固定定義ではダメなことがわかる。)

x = 2
rmean = x
for i in range(10):
    r1 = rmean
    r2 = x/r1
    rmean = (r1 + r2)/2
    print(i,"回目: ",r1,r2,rmean)

Pythonはブロックをインデントで表記する。

rangeをリスト型にするには、listを呼び出す必要がある

list(range(0,10,2)) # [0, 2, 4, 6, 8] 3つ目の引数を入れると、ステップ幅を指定できる

list(range(0,0)) # []
list (range(0,1)) # [0]
list(range(1,10,-1)) # [] (エラーにはならない。)
list(range(0.5)) # TypeError

whileは評価の結果がいくつ以上などの指定の仕方をしたい時に便利。(forだと何回繰り返すのようにプロセスにフォーカスがいく)

strは配列と似たような操作が行える。例えば、要素を含むかのチェックを行える

"a" in "abc"

浮動小数点数は多くの場合近似値を扱うため、==ではなく近似値で<>を使用するのが無難

0.1+0.1+0.1 == 0.3 # False

演算の優先順位は 算術演算(-,+など) > 比較演算(<,>など) > 論理演算(and, orなど)

inputの入力結果はstrになるので、数値を入力して扱いたい時はintやfloatを重ねておく

a = int(input("write: "))

try: ~ except:~で、エラーとその場合の対応を指定できる。 例外が発生しなかった場合のみ行う処理はelse、どちらにしても行う処理はfinally

https://www.sejuku.net/blog/23044

,を使わずにformatメソッドで後から変数を入れる表記。特に表示フォーマットの指定がなければ{}の中はいらない。

print("ken is {0:10.8g} years old,wil is {1} years".format(14.232,1.3))

関数を使った処理のカプセル化

処理をまとめておくメリットは主に以下3点 - 呼び出す側のプログラムが簡潔になる - 同じ処理を再利用しやすい - 定義の修正がしやすい

関数は最初にdocstringを書いておくと理解がしやすい。docstringの確認にはhelpの引数にメソッドを入れる。 https://qiita.com/simonritchie/items/49e0813508cad4876b5a

関数を呼び出す側の引数を実引数、呼び出される側の引数を仮引数と呼ぶ

メソッドの返り値を複数にすることも可能。この場合、一連の値はタプル形式で格納される。

def sq(n):

    return rnew,r1,r2
 
x = sq(3) # xにタプルとして格納される

グローバル変数に関数内でglobal宣言をすると、書き換えが可能になる。

a = 10
b = 5
def f():
    global b
    c = a * a
    b = c
f()
print(a,b) # 10 100になる

intはメソッドの内外でスコープが別(aは違う状態)だが、リストは共通で書き換えも可能なことがわかる。

a = 1
list = [6,7,8]
def c(x):
    list.append(x)
    a = 3
    x += a
    return a,x

print(a,c(10),a,list) # 1 (3, 13) 1 [6, 7, 8, 10]

メソッドはそれが呼び出される時に中身が評価される。cを呼び出した前後でglobal変数に指定したbの値が変化していることがわかる。

a = 1
b = 2
def c(x):
    global b
    b += 1
    x += a
    return a,b,x

print(a,b,c(10),b) # 1 2 (1, 3, 11) 3

副作用ってのは、元のオブジェクトを破壊的に変更するやりとりってことね https://python.ms/side-effect/#_1-%E5%89%AF%E4%BD%9C%E7%94%A8%E3%81%A8%E3%81%AF

()なしでメソッドをオブジェクトとして渡すと、別のメソッドの中で実行させるみたいなことができる

def f():
    print("hello")
    
def f_next(x):
    print("next")
    x()
    
f_next(f)

# next
# hello

Turtleで遊ぶ

クラス型のオブジェクトを生成する関数を「コンストラクタ」と呼ぶ。

オブジェクトに対して呼び出す関数を特にメソッドと読んでいる。

関数の引数として関数を指定して書くことができる

onscreenclick(come)

GUIアプリケーション

操作の対象となるオブジェクトと、その操作をまとめたものがモデル、モデルの結果をユーザーに表示するのがビュー、モデルの操作を行うのがコントロール

クラス

クラス:独自の”状態”や”メソッド”を持つオブジェクトを生成するための型。

Pythonでのクラスの初期化。クラスの場合には、インスタンスメソッドを定義する際仮引数にselfを明示しておく必要がある(呼び出す際には必要ない) https://techacademy.jp/magazine/27996

def __init__(self):

Pythonではクラス変数を直接書き換えることもできる。__を変数の先頭につけると、クラスの外からは操作できない。

class MyClass():
    a = "マイクラす"
    __b = 0
    
MyClass.a = 1
MyClass.a # 1 書き換えられた
MyClass.__b # AttributeError: type object 'MyClass' has no attribute '__b'

モジュールとしてインポートしたときには実行されないようにしておく

if __name__ == "__main__":

クラス(型)から設計するのではなく、具体的なインスタンスから考えるとスムーズ。オブジェクトに入れるべきデータとそのメソッドを先に考えるということ。

リスト

list()メソッドによりリストの初期化ができる。以下のようにリスト内包表記と組み合わせることできる。

x = [list(range(3)) for i in range(3)] # [[0, 1, 2], [0, 1, 2], [0, 1, 2]]

文字列もlistでリスト化できる

k = list("abcd")
k

内包表記ではない通常のforの時、引数には値が与えられるが、引数を変化させても元のリストは変化しない。リストの値を変更する場合はからのリストを作ってそこに格納していく形になる(だったら内包表記で良いわけだが)

a = [1,2,3]
for d in a:
    d += 1
print(a, d) # [1, 2, 3] 4

リストのスライスは後ろの数字の1つ手前までなので注意。(この場合k[1]とk[2] だけ)

k = list("abcd")
k[1:3]

リスト同士の結合。(appendにすると、最後の1つの要素としてリストが結合されてしまう。)

k = list("abcd")
k.extend(e)
k

リストの内包表記。最終的に行いたい計算を最初に書く。

a = [i*i for i in range(5)]
a

コピーの際、参照を同じにしたくない場合はcopy()を使ってデータの実態を複製する

a = [i*i for i in range(5)]
b = a.copy()
b[1] = 100
print(a,b)

数字や文字列はイミュータブル(値の変更ができない)なため、b+1を行った時に元の1を変更するのではなく、新たに2を生成する。だからa,bをprintした時に同じにならない。

a = 1
b = a
b = b+1
print(a,b)

要素がリストの時に、浅いコピーだと共通の参照元を新しく作ったリストから参照する。深いコピー(deepcopy())だと、要素がリストの時も要素ごとまるっと新しく生成する。

ファイル入出力

csvなどの形式は、出力は簡単だが、入力は手間がかかる可能性があることを抑えておく。

OSモジュールによるカレントディレクトリの表示。

import os
os.getcwd()

ファイルを開き、書き込み、閉じる一連の流れ。open関数でファイルオブジェクトを取得し、readやwriteなどのメソッドで処理をする。

f = open('日本語ファイル.txt','w')
f.write('日本語 \n 日本語 \n 日本語')
f.close()

f = open('日本語ファイル.txt','r')
s = f.read()
f.close()
print(s)

ファイルオブジェクトも1行ずつイテレータとして機能するためforを使って1行ずつ処理を行うことができる

withを使った形にし、ブロックの中で処理を行う場合close()を省略できる。何らかのエラーでcloseが実行されないことを避ける

with open('日本語ファイル.txt') as f:
    s = f.read()
print(s)

テストについては、「パスしなかったプログラムに誤りがある」ことは保証しても、「パスしたプログラムが全ての場合に適切に動作する」ことを保証しているわけではない。

ソフトウェア開発のVモデル:要件から徐々に設計に落としていき、実装することで製品にしていく流れを三角形に例えたもの。上流の要件ほど認識のズレが確認できるまでのラグが大きい。

Pythonの学術利用

2*2、要素0の配列を作る。

np.zeros((2,2))

基本的な検索機能

arr2.ndim # 次元
arr2.dtype # データ型
arr2.shape # 大きさ

の代わりに、カンマを使って多重配列の場所を指定できる

arr2[1,2]

スカラー値との演算は、すべての要素に同じ演算が実行される。

arr1 + 1

上記の特徴を使うと、arrayではforを使用しなくても柔軟な値操作ができる。

import numpy as np
arr = np.array([[1,2,3],[4,5,6]])
arr[0:2,0:2] = 9
arr # array([[9, 9, 3],[9, 9, 6]])

list = [[1,2,3],[4,5,6]]
list[0] = 9
list # [9, [4, 5, 6]] 単純な代入では9という1つの要素に置き換えられてしまう。

行列の転置

arr2.T

よく使いそうな乱数

np.random.randint(10, size=10) # 0~10未満の整数乱数を10こ
np.random.rand(10) # 0~1までの浮動小数点数乱数を10こ

基本的なグラフの書き方。

import matplotlib.pyplot as plt
plt.plot([1,2,3], 'b--o') # 青破線、oのマーカー
plt.plot([2,3,4], 'k-') # 黒の実践
plt.title('Title')
plt.xlabel('x')
plt.ylabel('y')

散布図の描写。

datax = np.random.randn(100)
datay = datax + np.random.randn(100)*0.3

plt.scatter(datax,datay,label='scatter1')

datax = np.random.randn(100)
datay = datax + np.random.randn(100)

plt.scatter(datax,datay, color='red',label='scatter2')