Python 3 の覚え書き

初版作成  2016.2.5
最終更新  2024.04.05


◆◆ 役に立つサイト ◆◆


Python 入門
Python 基礎文法最速マスター
Python 学習サイト
Python-izm
JavaDrive

◆◆ ファイル先頭 ◆◆


#!/usr/bin/python
# -*- coding: utf-8 -*-

python2 は 2 行目が必須
python3 は utf-8 の場合 2 行目は不要

漢字コードは utf-8 以外に

euc-jp, shift_jis がある。

FreeBSD, Linux の python で、改行記号が 0d 0a の
ファイルを実行しようとすると、

   filename.py: Command not found.

というエラーが出る。

Linux で開発したものを Windows に ftp で持ってくると
改行記号が 0d 0a になる。それを Linux で使おうとすると
上記のエラーがが発生する。

◆◆ プログラムの名前 ◆◆


import するモジュールと同じ名前を付けてしまうと、
カレントディレクトリにある自分自身を import し、
使いたいモジュールを import することができない。

ゆえに json.py のようなプログラム名で import json すると
メソッドがないというエラーがでる。


◆◆ コメント ◆◆


#  より右側  行の途中も可   C++ の // に相当

""" と """ で囲んだ範囲   C++ の /*  */ に相当


◆◆ インデント ◆◆


python はインデントで if, for などの範囲を表す。
インデントの手前の文は右端に : が付く

<python2>

tab はスペース 8 個と等価と見なされる。
エディタで tab 幅を 4 に設定している場合、
スペース 4 個と tab 1 個は、見かけは同じインデントレベルだが
python にとっては異なるインデントレベルとして認識される。

エディタの tab 幅を 4 に設定する場合、
インデントは全て tab にするか、全てスペースにするか、
統一する必要がある。

<python3>

tab とスペースの混在は禁止
スペースの使用が推奨されている
スペースは 4 個が適切


◆◆ 変数 ◆◆


宣言不要

文字と数値は区別される。"  " あるいは '  ' で囲むと文字列。

(注意!)
a = 1
b = "1"
のとき、a == b は成立しない。perl とは異なる。

print で + で文字と数値を連結しようとすると
エラーとなる。型変換の関数 str(  ) が必要。

変数の型

print(type(a))

◆◆ 代入 ◆◆


以下のような書き方もできる

a, b = 10, 20

関数の戻り値を 2 個以上返したいとき、

関数側
  return(a, b)
呼び出し元
  a, b = func()

という書き方ができる。

以下のようにリストを使った方がよいかも知れない。

関数側
  return([a, b])
呼び出し元
  [a, b] = func()

◆◆ グローバル変数 ◆◆


・メインプログラムの中で使用した変数はグローバル変数

python は変数宣言を必要としない。
print(a) のように変数を参照したとき、未定義の場合
エラーが発生する。

・def の中では、以下のようにややこしい

    def gcheck():
        print(a)    a は未定義なのでグローバル変数

    def gcheck():
        a = 20      a はローカル変数
        print(a)

    def gcheck():
        global a    global 宣言したので a はグローバル変数
        a = 20

値を利用するだけなら宣言不要
値を代入するときは global 宣言が必要
次の変数のスコープの項も読んでおくこと。

混乱を避けるため、値を利用するだけであっても、
グローバル変数は全て global をつけることを
習慣とした方がよいと思われる。

・いくつかの def の中でだけ使用し、
 メインプログラムの中で使用しないグローバル変数を使う場合
  def の中で global 宣言を行う。
  fortran の common 宣言と似ている。

スレッドの中で使う変数は全てローカル変数と見なされる。
グローバル変数を使う場合は、必ず global 宣言が必要。


◆◆ 変数のスコープ ◆◆


メインプログラム中で使用した変数はグローバル
変数である。def の中からでも使える。

def func1():
    print(const)

const = 3
func1()

def func1(): の中の const はグローバル変数
とみなされ、実行すると 3 と表示される。
しかし、以下のプログラムではエラーがでる

def func1():
    print(const)
    const = 4

const = 3
func1()

その理由は func1() の中で const はローカル変数
とみなされ、print(const) の時点では値が定まって
ないので、エラーとなる。

以下のようにすればエラーが出なくなり、
func1() の中で const の値を書き換える
ことができる。

def func1():
    global const
    print(const)
    const = 4

const = 3
func1()
print(const)


◆◆ 代入 ◆◆


以下の代入文が可能
a, b = c, d        # a = c, b = d と等価     2 つ以上の変数に一気に代入可能
a, b = b, a        # テンポラリ変数なしで、値の入れ替えが可能

2 個以上の変数への代入は
以下のようなパターンで使われる。

for i, element in enumerate(list):

i は 0, 1, 2, 3 ....
element は list[0], list[1], list[2] ... が入る。


◆◆ 文字列 ◆◆


連結  str = str1 + str2
分解  list = line.split(',')    list は配列
結合  line = ' '.join(list)
長さ  len(str)
部分文字列  substr = str[1:3]   2,3 文字目

leng = len(str)
last_cha = str[leng-1:leng]    最後の 1 文字
prev_str = str[0:leng-1]       その手前の文字列

line = line.strip()     行頭と行末の空白 (改行記号を含む) を除去する
line = line.lstrip()    行頭の空白を除去する
line = line.rstrip()    行末の空白 (改行記号を含む) を除去する

"  を出力したいときは \"
\\ を出力したいときは \\

◆◆ 文字列の分解 ◆◆


空白(2 個以上の空白が連続する場合を含む)で区切られた文字列 line を
分解するときは

list = line.split()

と書く。split の引数を省略すると
区切り文字(スペース, タブ)と解釈する。

list = line.split(' ')

ではダメ。空白が n 個連続している場合、
n-1 個の長さなしの文字列が発生する。

区切り文字に正規表現を用いることができる。
re を使って以下のように書く方法がある。
しかし、以下の方法では行頭に空白がある場合、list[0] は
長さなしの文字列となってしまう。

line = line.rstrip()            line.strip() も可
list = re.split('\s+',line)     '\s*' でも可
for a in list:
    print(a)


◆◆ 文字列と数値の変換 ◆◆


moji = str(a)
a = int(moji)
b = float(moji)

◆◆ ascii コードと文字の変換 ◆◆


ord("a")    cha ---> ascii
chr(13)     ascii ---> cha

◆◆ 指数表現 ◆◆


1e-6    10^(-6)
1e3     10^3

◆◆ 演算 ◆◆


a % b    a ÷ b の余り
a ** b    a^b
5 / 2    2.5 (結果は常に浮動小数点 python2 では 整数/整数=整数)
5 // 2.0   2  小数点以下切り捨て


◆◆ 表示 ◆◆


i = 10
s = "10"

print("hello world")     " と ' の違いはない   改行される
print('hello world')

print("abc", end = "")   改行なし
print("abc","def")       , で区切ると半角スペースが 1 個入る  abc def となる

print("abc",i,"def")            abc 10 def と出力される
print("abc" + s + "def")        abc10def と出力される

print("abc" + str(i) + "def")   str( ) を怠るとエラーになる

" を出力するときは \"
ただし、'abc"def'  のように '   ' で囲めばエスケープ不要

ヒアドキュメント(""" で囲む)

print("""line1
line2
line3""")


◆◆ format 付き表示 ◆◆


3 種類の方法がある。最後の方法は python 3.6 以降

a = 1234.56789
s = "abcd"

○ C ライク   sprintf と同じ
print("<%05d> <%5d> <%8.2f> <%s>" % (a, a, a, s))

結果
<01234> < 1234> < 1234.57> <1234.57> <abcd>

○ python 独自
print("<{:05d}> <{:5d}> <{:8.2f}> <{:.2f}> <{}>".format(int(a),int(a),a,a,s))

{} の順番に入る。{} だけでもよい
{5} は半角 5 個分のスペースを確保
: を付けるとフォーマット指定

○ 3.6 以降に使える

a = 123
b = 123.456

print('{:5d} and {:8.3f}',format(a,b)) と下は同じ
print(f'{a:5d} and {b:8.3f}')


◆◆ if ◆◆


if a == b:
    print("a = b")
elif a == c:
    print("a = c")
else:
    print("else")

if a == b:
    pass         # 何もしないときは pass が必要。怠るとエラー
else:
    何らかの処理

条件を複数記述するときは  and   or

比較演算子は C と同一

==   !=   <   >   <=  >=



◆◆ while ◆◆


num = 0
while num < 2:
    print("num = " + str(num))
    num = num + 1

while 1:       無限ループ
while True:    無限ループ

    break      ループ脱出
    continue   ループ先頭へ

◆◆ for ◆◆


list = [1, 3, 2]

for num in list:
    print("num = " + str(num))

for i in range(0,4):      0,1,2,3

for i in range(5,2,-1):   5,4,3

◆◆ for #2 ◆◆


list = ['a', 'b', 'c']
for char in list:
    print(char)

'a'
'b'
'c'

for i, char in enumerate(list):
    print(i, char)

0 'a'
1 'b'
2 'c'


◆◆ 配列(リスト) ◆◆


list = [ 1, 30, 50, "abc"]     数値, 文字列  混在可
list[0]     1 つめの要素
len(list)   要素数

一部の取り出し
sub_list = list[2:4]     [list[2], list[3]]
sub_list = list[3:]      list[3] とそれ以降
sub_list = list[:2]      list[1] とそれまで

For との組み合わせ(2 通りの方法)

for item in list:
    print(item)

for i in range(0, len(list)):
    print(list[i])

連結・追加

list1 = [1, 2, 3]
list2 = [4, 5, 6]

list1.extend(list2)     list1 = [1, 2, 3, 4, 5, 6]
list1.append(list2)     list1 = [1, 2, 3, [4, 5, 6]]

list3 = list1 + list2   list3 = [1, 2, 3, 4, 5, 6]

extend は既存のリストの更新、
+ は新しいリストの作成

str = "abc"
str2 = "123"

list = []         <---- 空のリスト
list.append(str)
print(list)             ['abc']
list.append(str2)
print(list)             ['abc', '123']
list.extend([str2])
print(list)             ['abc', '123', '123']

削除

a = list.pop(2)

list[2] (3 番目の要素) を削除し、
削除した値を a に代入する

list.remove(element)

値が element である最初の要素を削除する

for a in list: のループ中で
list.remove を使うのは危険である。

list = [1, 2, 3, 4]
for element in list:
    print(element)
    if element == 2:
        list.remove(element)

このプログラムでは 1, 2, 4 と表示され、
3 に対する処理が行われない。

その理由は 2 回目のループで 2 が削除された結果、
リストが [1, 3, 4] となり、その次のループは
3 回目のループであるが、3 番目の要素である 4 に対して
処理が行われる。


◆◆ 2次元配列 ◆◆


a = [[1, 2], [3, 4], [5, 6]]
print(a)

a = []
b = [1, 2]
c = [3, 4]
d = [5, 6]
a.append(b)
a.append(c)
a.append(d)
print(a)

for i in range(len(a)):
    print(a[i][0], a[i][1])


◆◆ 配列(連想配列) ◆◆


hash_list  = {"a": 10, "b": 20, "c": 30}
hash_list2 = dict(aa = 100, bb = 200, cc = 300)  # 別の書き方

hash_list["d"] = 40    # 追加
del(hash_list["a"])    # 削除

list = hash_list.keys()
list2 = sorted(hash_list.keys())

print(list)
print(list2)

for name in hash_list.keys():
    print(name, hash_list[name])

for name in sorted(hash_list2.keys()):
    print(name, hash_list2[name])

◆◆ 配列のコピー ◆◆


a = [1, 2, 3]
b = a          同じオブジェクトを参照するので
               a を変更すると b の内容も変わる

c = copy.copy(a)

2 次元配列の場合は要注意

a = [[1, 2], [3, 4]]
b = copy.copy(a)
c = copy.deepcopy(a)

a[1].append(5)

print(b)   [[1, 2], [3, 4, 5]]
print(c)   [[1, 2], [3, 4]]

2 次元配列は、各要素が別のオブジェクトを参照している。
b が作成されたとき [3, 4] は a の 2 個目要素と同じ要素を
参照している。

◆◆ 配列(タプル) ◆◆


変更ができないリスト

a = (255, 255, 0)


◆◆ 継続行 ◆◆


2 つも方法がある
(1)  \    バックスラッシュ
(2) ( ) で囲んだ部分の中では改行してもよい


◆◆ ファイル読み込み(1 行ずつ) ◆◆


with open を使う

with open("a.txt", "r", encoding = "utf-8") as f:
    for line in f:
    print(line, end = "")

インデントが終わった瞬間 close する

open を使う

f = open("a.txt", "r", encoding = "utf-8")   encoding は省略可  "shift-jis"
for line in f:
    print(line, end = "")
f.close()

close() が必要

エラー処理付き

try:
    f = open("filename", "r")
except:
    print("cannot find file.")
    sys.exit(1)

1 行ずつ読み込みの命令は 2 つの系統がある。

1. for, next を使う
2. readline を使う

一度どちらかを使うと、その後は同系統の命令を
使わないとエラーになる。

1. for, next 系

for line in f:                  # line の末尾には \n (0a) が入っている
    line = line.rstrip("\n")  # 行末の改行記号を除去
                              #    空白も除去するときは line.rstrip()
    print(line)

next の使い方

next(f)

next を使って読み込むとき、ファイルエンドでエラーが発生
するので、次の try  except の構文が必要

try:
    while True:
        line = next(f)
        line = line.rstrip("\n")
        print(line)
except:
    pass

2. readline 系

while True:
    line = f.readline()     # 1 行読み込む
    if not line:            # if line == "":  でも同じ
        break
    line = line.rstrip("\n")
    print(line)

  ------------

f.seek(0)  ファイルの先頭へ
           seek を実行した後は for,next  or  readline のいずれも可

◆◆ ファイル読み込み(ファイル全て) ◆◆


2 つの命令がある

1. 読み込んだファイルは 1 つの文字型変数に格納される

str = f.read()
print(f)

2. 読み込んだファイルを 1 行ずつリストにする

lines = f.readlines()
print(f)
for i in range(len(lines)):
    lines[i] = lines[i].rstrip("\n")
    print("|" + lines[i] + "|")

◆◆ ファイル読み込みの注意 ◆◆


python ではファイルから読み込んだものは全て
「文字列」であり、「数値」ではない。
ファイルから変数 a に 12 を読み込んだとする。

if a == 12:

は成立しない。

if a == "12":
if int(a) == 12:
if float(a) == 12:

のいずれかなら、成立する。

例:
1 行読み込み、その行には 2 個の数値が
入っており、それぞれ a, b 代入する。

line = line.strip()
list = line.split()
a = float(list[0])
b = float(list[1])


◆◆ ファイル書き出し ◆◆


with open("out.txt", "w", newline = "\n") as f:    "w":上書き   "a":追記

f = open("out.txt", "w", newline = "\n")   "w" : 上書き     "a" : 追記

newline は省略可
省略時:Linux は "\n", windows は "\r\n"

print の引数は文字列、数値両方あり

print("abcde", file = f)            # 改行あり
print("abcde", file = f, end = "")  # 改行なし

write の引数は文字列

f.write(line)          # 改行しない
f.write(line + "\n")   # 改行する

f.write("\n".join(lines))  # リストの各要素の末尾に "\n" を挿入して連結

writelines の引数はリスト

f.writelines(lines)    # 改行は挿入されず、連結される


f.close()


◆◆ 終了 ◆◆


sys.exit()      終了コードは 0
sys.exit(1)     終了コードは 1

sys.exit(-1)    終了コードは 255

◆◆ 関数 ◆◆


呼び出し方は C と同じ
関数中で引数の値を変更しても、呼び出し元の変数は不変
複数の値を返したいときは

ret = [1, 2, 3]  あるいは ret = (1, 2, 3)
return ret

のようにすればよい。
python のアンパック機能により、
[1, 2, 3]  と (1, 2, 3) の違いはない。

a, b, c = func()
[a, b, c] = func()
(a, b, c) = func()

どれを使っても a, b, c に数値が入る。
コンマを使って数値を区切ると、暗黙的にタプルを形成する

関数例:ヘッダ検索システム

def fhead_f(f, head):

    f.seek(0)

    find = 0

    for line in f:
        line = line.rstrip()    # 行末の改行除去
        line = line.strip()     # 前後の空白除去
        if line == head:
            find = 1
            break

    return find

◆◆ 関数の引数 ◆◆


・デフォルト値の設定
・キーワードを指定することにより順序の入れ替え

def sub(a, b = 3, c = [1, 2, 3]):
    print(a, b, c)

sub(1)              a = 1, b, c はデフォルト値
sub(1, 2)           a = 1, b = 2, c はデフォルト値
sub(1, b=2)         a = 1, b = 2, c はデフォルト値
sub(1, c = [2, 3])  a = 1, c = [2, 3], b はデフォルト値


◆◆ メイン関数 ◆◆


関数はメイン関数よりも上に配置する。
メイン関数は最後に来ることになる。

メイン関数を先頭に持ってきたい場合は、以下のように書く。

def main():
    print("main")
    func()

def func():
    print("func")

if __name__ == "__main__":
    main()

if __name__ == "__name__":
はそのファイルが直接実行されたときのみ「真」となる。
import されたときは「偽」となる。


◆◆ cgi GET 専用 ◆◆


引数は環境変数 QUERY_STRING に入る
コマンドラインから試しに実行するとき
http://.......?key=value  なら
$ export QUERY_STRING=key=value
をあらかじめ実行しておけば良い。

import sys, io
import os
import cgi
import subprocess     ver.2 は commands

# CGI から実行するとき以下が必要
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding = 'utf-8')

err1 = 0
err2 = 0

key = 'QUERY_STRING'
if key in os.environ:
    query = cgi.parse_qs(os.environ[key])
else:
    err1 = 1

key = 'radio1'
if key in query:
    value = cgi.escape(query[key][0])
else:
    err2 = 1

print("""\   <---- これを忘れると空行が 1 つ入り、http のヘッダがないのでエラー発生
Content-type: text/html

<html>
<head>
<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">
<title>タイトル</title>
<style>
body,p { font-size: x-large; margin: 20px; }
</style>
</head>
<body>""")

◆◆ cgi GET POST 両用 ◆◆


import cgi

method = os.environ.get('REQUEST_METHOD')

form = cgi.FieldStorage()
for key in form.keys():
    print "<p>{} = {}</p>".format(key, form[key].value)

◆◆ 外部コマンドの実行 ◆◆


import subprocess

os.chdir("dir_name")     # ".." なども可能
ret = subprocess.call(cmd)  # 正常実行の時 0 が返る

ret = subprocess.getoutput(cmd)  # 標準出力に出力した文字列が返る
print(ret)

os.system(cmd) は古いので推奨されない。


◆◆ cgi で GPIO やシリアルポートにアクセスする ◆◆


cgi から GPIO, serial をアクセスするプログラムを呼び出す
ときは root 権限が必要。

apache2 の cgi は www-data の権限で動作する。

cmd = "sudo prog.py arg"
ret = subprocess.getoutput(cmd)

とするとき、www-data の権限でパスワードなしで sudo できる
必要がある。

  $ sudo visudo

で以下の行を付加する。

www-data ALL=(ALL) NOPASSWD: ALL


◆◆ 文字列の検索(正規表現なし) ◆◆


・文字が含まれているか否かを判定

line = "abc123"
if 'c' in line:
    見つかったときの処理

・何文字目か?

line = "abc123"
a = line.find('123')    #  a = 3
a = line.find('x')      #  a = -1
a = line.find('a')      #  a = 0

見つからないとき -1
見つかったとき n 文字目からなら n-1 を返す。

・置換

line = line.replace('pre','after')

◆◆ 文字列のマッチング・置換(正規表現使用) ◆◆


import re

正規表現
 リテラル   r'正規表現'     r (raw) をつけると、エスケープシーケンス (\) を
                             無視する。
                             例えば \t はタブではなく \ と t の 2 文字
 変数化     str = re.compile(r'正規表現')

・re.search

部分一致(perl のマッチングと同等。正規表現, グループの使い方も同じ)
m にはマッチオブジェクトが入る

m = re.search(str, line)
if m:                    あるいは if m != None:
    moji  = m.group(1)
    moji2 = m.group(2)

マッチしたとき:m はオブジェクト
マッチしないとき:m は None

最短・最長マッチ

(.\S*) (.\S+)  などは最長マッチする。
最短マッチしたい場合は (.\S*?) (.\S+?) のように末尾に ? をつける

例:
r'.*?・(.*).pdf'  ……「・」が 2 箇所にある場合、最初の「・」にマッチする。

(参考)
http://www.geocities.jp/m_hiroi/light/python04.html
http://www.yukun.info/blog/2008/08/python-regexp-greedy-reluctant-match.html

・re.match()

先頭からマッチング(re.serach の検索文字列の先頭に ^ を
つけるのと同等と思われる)

例
    m = re.match('Time/div\(second\),(\S*)', line)
    m = re.match('Points,(\S*)', line)
    m = re.match('(\S+),(\S*),(\S*),(\S*)$', line)

・re.findall()

全て見つけ出し、リストに入れる

list = re.findall("(\w+):", all)

末尾に「:」がある任意の文字列を抽出してリスト変数 list に
入れる。ただし : は除外する


◆◆ 正規表現 ◆◆


.   任意の1文字
x?  0 個あるいは 1 個の x
x*  0 個以上の x
x+  1 個以上の x
^   行の先頭
$   行末
\s  空白文字 ( スペース、タブ、改行 )
\S  空白以外の文字
\d  数字
\w  単語を構成する文字 [a-z0-9_]
\*  * そのもの

[0-9\-]   0 〜 9 の文字あるいは '-' 記号の固まり
[^\"]+\"  " 以外の文字の 1 つ以上の連続の後、" 文字


◆◆ 標準エラー出力 ◆◆


import sys

print("usage: % makehtml.py fname", file = sys.stderr)
sys.stderr.write("usage: % makehtml.py fname\n")

◆◆ コマンドライン引数 ◆◆


import sys

argc = len(sys.argv)    引数の個数 (コマンド名含む)
sys.argv[0]             コマンド名
sys.argv[1]             第 1 引数


◆◆ スクリプトファイル名の取得 ◆◆


script_fname      = os.path.basename(__file__)   # ファイル名
script_path_fname = os.path.abspath(__file__)    # フルパスのファイル名
script_path       = os.path.abspath(os.path.dirname(__file__))
                          # ファイル名を除いたパス名の部分

自力でやるなら以下のように書く

m = re.match('.*/(.*)$', __file__)  # __file__ の代わりに sys.argv[0] も可
if m != None:
    script_fname = m.group(1)
else:
    print("error")
    sys.exit(1)


◆◆ ファイルの日付 ◆◆


stat = os.stat(fname)
time = stat.st_mtime
dt = datetime.datetime.fromtimestamp(time)
line = dt.strftime("%Y.%m.%d")

◆◆ 現在の日付と時刻の取得 ◆◆


import datetime

d = datetime.datetime.now()
year = d.year
month = d.month
day = d.day
hour = d.hour
minute = d.minute
second = d.second

out_line = "{:04}/{:02}/{:02} {:02}:{:02}:{:02}".\
           format(year,month,day,hour,minute,second)

◆◆ ディレクトリ、ファイルの操作 ◆◆


import os, shutil

os は低水準ファイル処理用のモジュール
shutil は高水準ファイル処理用のモジュール

cwd = os.getcwd()       cwd の取得
os.chdir("dir")         cd
list = os.listdir(cwd)  ファイルとフォルダのリストの取得

os.path.exists(name)        True/False  ファイル・ディレクトリどちらも可
os.path.isfile(fname)       ファイルの存在
os.path.isdir(dir_name)     ディレクトリの存在
os.path.getsize(file_name)  サイズ

os.rename(fname, fname2)  リネーム  shutil.move と同じ
                          同一ファイルシステムのみ使用可
os.remove(fname)          ファイル削除
os.mkdir(path)            ディレクトリ作成

フォルダだけ取り出す
dir_list = []
list = os.listdir(cwd)
for dir in list:
    if os.path.isdir(dir) == True:
        dir_list.append(dir)

shutil.move(fname, fname2)           move コマンドと同じ
shutil.copy(src_fname, dst_fname)    cp コマンドと同じ
shutil.copy2(src_fname, dst_fname)   更新日時などもコピー
shutil.rmtree(dir)                   ディレクトリ以下全て消去

◆◆ ファイル名、拡張子を取り出す ◆◆


fname = os.path.basename(full_fname)
body, ext = os.path.splitext(fname)     splittext でないのに注意!
                                        ext の中に . が含まれることに注意!

ext には "." の含まれているのに注意!

◆◆ キー入力 ◆◆


line = input("input line  ")
print(line)

◆◆ ソケット通信(クライアント) ◆◆


import socket

host = "denki.nara-edu.ac.jp"
port = 80
size = 4000

addr = (host, port)

out_line1 = "GET /~yabu/index.html HTTP/1.0\r\n"
out_line2 = "\r\n"

so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
so.connect(addr)
so.send(out_line1)
so.send(out_line2)
recv_data = so.recv(size)
so.close()

lines = recv_data.split("\n")

for line in lines:
    line = line.rstrip()
    print("<" + line + ">")

html の場合、送信元のファイルの改行記号が
0d 0a (Windows) の場合と 0a (Linux) の場合の
2 通りがある。

0d 0a の場合 rstrip() で行末の 0d を除去することが
必要。それを怠ると、上記のプログラムの場合、
行末文字の ">" を行頭の "<" に重ね書きする。

recv_data.split("\r\n")

はダメ。全く分離しない。


◆◆ ソケット通信(サーバー) ◆◆


import socket

port = 10003
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", port))
s.listen(5)

while True:
    sock, addr = s.accept()
    print("Conneted by" + str(addr))
    data = sock.recv(1024)
    print("recv:",data)
    sock.close()

(参照)  http://docs.python.jp/3/howto/sockets.html

◆◆ hostname と ip address ◆◆


import socket
addr = "202.236.176.170"
host_tuple = socket.gethostname(addr)

hostname  = host_tuple[0]
hostcname = host_tuple[1]
addr2     = host_tuple[2]

◆◆ 暗号化 smtp 接続 ◆◆


import smtplib

smtp_server = "smtp.gmail.com"
port_no = 587
id = "xxyyzz@gmail.com"
passwd = "password"

from_addr = "xxyyzz@gmail.com"
to_addr = "to_addr@nifty.com"

charset = "ISO-2022-JP"
subject = "test mail"
text = "send message"

msg = MIMEText(text.encode(charset),"plain",charset)

msg["Subject"] = Header(subject,charset)
msg["From"] = from_addr
msg["To"] = to_addr
msg["Date"] = formatdate(localtime=True)

s = smtplib.SMTP(smtp_server, port_no)
s.ehlo()
s.starttls()          # <-------- これを入れると拒否されるサーバーもある
s.login(id, passwd)
s.sendmail(from_addr, to_addr, msg.as_string())
s.close()

◆◆ ディレイ delay ◆◆


import time

time.sleep(3)    # 3 秒

◆◆ 環境変数の取得 ◆◆


import os

path = os.environ.get('PATH')

for env in os.environ:
    value = os.environ[env]
    print("env = " + env + "   value = " + value)


◆◆ zip ファイルの作成 ◆◆


import os, zipfile

flist = []
flist.append("filea.txt")
flist.append("fileb.exe")
flist.append("filec.docx")

あるいは

flist = ["filea.txt", "fileb.exe", "filec.docx"]

      -----------

os.chdir(dir)  ' 必要な場合は入れる

myzip = zipfile.ZipFile("aaa.zip", 'w', zipfile.ZIP_DEFLATED)
for f in flist:
    myzip.write(f)
myzip.close

os.chdir('..')  ' 必要な場合は入れる

◆◆ プロセス id ◆◆


pid = os.getpid()       # 数値型    文字にするには str(os.getpid())


◆◆ ファイルのロック ◆◆


import fcntl

f = open('rec.txt','a')
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
print (time_str + "," + value, file = f)
#time.sleep(5)
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
f.close()


fcntl.LOCK_EX  でロック
fcntl.LOCK_UN  で開放

ロックしようとしたときに、他のプロセスが既に
ロックしているときは、自分の順番がまわってくるまで
スリープする。

(参照)
https://www.phactory.jp/blog/python_lock/


◆◆ 乱数 ◆◆


import random

random.seed()             現在の時刻が使用される

i = random.randint(2, 4)  2〜4 の整数
f = random.random()       0 以上 1 未満の実数



◆◆ Windows 版 python IDLE のインストール ◆◆


インストールはここから

https://www.python.org/downloads/

ブラウザの情報を利用しているのか、今使っている OS 用の
python をダウンロードする。

exe ファイルをダブルクリックして python をインストールすると
IDE である IDLE もインストールされる。

起動すると shell window が開く。

エディタの設定は Options --- Configure IDLE
背景を黒にする。

shell window と editor window の 2 つがある。

shell window を開くには Run --- python shell
ダイレクトコマンド入力用

editor windows を開くには File --- New File/Open
実行するには Run --- Run Module


◆◆ on を検知するアルゴリズム ◆◆


python に限った話ではないが、
「ボタンを押した」「ラインを通過した」などを
検知するとき、2 つの考え方がある。

(1) 今の値と一つ過去の値を使う方法

now = input()
while True:
    old = now    # 1 つ過去の値を記憶
    now = input()
    if now == ON and old == OFF:
        manage()

(2) 状態変数を使う
    今 off 状態か on 状態かを保持する変数 mode を使う

(2a) モードによって処理を変える

mode = OFF
while True:
    in = input()
    if mode == OFF:
        if in == ON:
            mode = ON
            manage()
    else:
        if in == OFF:
            mode = OFF

(2b) if の順序を逆にした
   コードは少し簡潔になる

mode = OFF
while True:
    in = input()
    if in == on:
        if mode = OFF:
            mode = ON
            manage()
    else:
        mode = OFF

(2b) の方が若干簡潔になるのは、下図のように
in=off のとき mode を気にする必要がないため。

mode \ in|   off     |   on
---------+-----------+----------
off      | mode=off  | mode=on
         |           | manage()
---------+-----------+----------
on       | mode=off


◆◆ ライブラリ(モジュール)のインストール ◆◆


pip3 というコマンドでインストールする
pip でインストールすると python2.7 用のライブラリが入ることがある

# apt install python3-pip      pip3 をインストール

$ pip3 install matplotlib      インストール
$ pip3 freeze                  インストールしたライブラリの確認
$ pip3 list                    インストールしたライブラリの確認
$ pip3 uninstall matplotlib    アンインストール
$ pip3 install --force-reinstall module-name

pip のアップグレード

$ python3 -m pip install --upgrade pip

○ Windows 版 IDLE で使う python のモジュールインストール

コマンドプロンプトで where python と打つと、
以下の2箇所に python がある。

(1) C:\Users\user-name\AppData\Local\Programs\Python\Python310\python.exe
(2) C:\Users\user-name\AppData\Local\Microsoft\WindowsApps\python.exe

IDLE から呼ばれる python は

path = sys.executable
print(path)

で確認すると

C:\Users\user-name\AppData\Local\Programs\Python\Python310\pythonw.exe

である。(1) のフォルダの python のようだ。
コマンドラインから

> py -m pip install module-name

あるいは

> pip install module-name

でインストールすると (2) の python 用にインストールされ、
IDLE から使うことができない。
IDLE から以下のスクリプトを実行してインストールする

import subprocess, sys

path = sys.executable
print(path)

module = "Pillow"

cmd = path + " -m pip install " + module
ret = subprocess.getoutput(cmd)
print(ret)

cmd = path + " -m pip freeze"
ret = subprocess.getoutput(cmd)
print(ret)


◆◆ matplotlib のインストールと使い方 ◆◆


$ sudo -s

# apt update
# apt install python3-pip
# apt install python3-tk

$ pip3 install matplotlib
$ pip3 install numpy

サンプルプログラム(ファイルからデータを読み込んでグラフを書く)

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# 参照元 https://qiita.com/MENDY/items/fe9b0c50383d8b2fd919
#

import matplotlib.pyplot as plt
import re

x = []
y = []

f = open("data.txt", "r")

for line in f:
    if re.search("^#", line): # コメント行
        continue
    line = line.strip()  # スペースの除去
    if line == "":       # 空行をスキップ
        continue
    list = line.split()  # 文字列分解
    x.append(float(list[0]))  # list[] は文字列なので
    y.append(float(list[1]))  # 数値にコンバート必要

f.close()

print(x)
print(y)

xtics = [0, 5, 10]
ytics = range(0, 11, 2)

fsize = 15

plt.rcParams["axes.linewidth"] = 2
plt.rcParams["xtick.direction"] = "in"
plt.rcParams["ytick.direction"] = "in"

plt.title("Title of figure", fontsize = fsize)
plt.plot(x,y,marker="o",linewidth=1, color="#0000ff")
plt.xlabel("X-Axis", fontsize = fsize)
plt.ylabel("Y-Axis", fontsize = fsize)
plt.xlim([0, 10])
plt.ylim([0, 10])
plt.xticks(xtics, fontsize = fsize)
plt.yticks(ytics, fontsize = fsize)
plt.grid()
plt.legend()

plt.savefig("fig.eps")
plt.savefig("fig.svg", bbox_inches = "tight", pad_inches = 0.05)
plt.savefig("fig.png")
plt.savefig("fig.pdf", bbox_inches = "tight")

plt.show()  # show を savefig より先に実行すると、セーブされない

◆◆ ftp ◆◆


import ftplib

・テキストファイルの get

local_fname = "~/dir1/fname.txt"
remote_fname = "dir2/fname.txt"
# remote_fname の冒頭に / は不要だが、付けても良いケースと
# ダメなケースがある。
# 手動で ftp して  'cd /'  'pwd'  を実行して
# どのディレクトリにいるかを調査すれば良い

ftp = ftplib.FTP("192.168.xx.yy")
ftp.set_pasv("true")
ftp.login("user-name", "passwd")

f = open(local_fname, "w")
command = "RETR " + remote_fname
ftp.retrlines(command, f.write)
f.close()
ftp.close()

・バイナリファイルの get

f = open(local_fname, "wb")
command = "RETR " + remote_fname
ftp.retrbinary(command, f.write)
f.close()
ftp.close()

・テキストファイルの put

src_fname = "/home/user-name/dir1/fname.txt"
dist_fname = "dir2/fname.txt"

ftp = ftplib.FTP("192.168.xx.yy")
ftp.set_pasv("true")
ftp.login("user-name", "passwd")

f = open(src_fname, "rb")
command = "STOR " + dist_fname
ftp.storlines(command, f)
f.close()
ftp.close()

・バイナリファイルの put

ftp.storbinary(command, f) に変更


◆◆ ftps ◆◆


・バイナリファイルの put

from ftplib import FTP_TLS

src_fname  = "/home/pi/aaa/bbb.jpg"   // full path
dist_fname = "dir/bbb.jpg"            // from top dir

ftps = FTP_TLS("192.168.xxx.yy")
ftps.login("user-name", "passwd")
ftps.prot_p()

fp = open(src_fname, 'rb')

command = "STOR " + dist_fname

ftps.storbinary(command, fp)
ftps.close()
fp.close()

◆◆ web アクセス (requests 使用) ◆◆


url = "http://denki.nara-edu.ac.jp/~yabu/"

ret = requests.get(url)
ret.encoding = ret.apparent_encoding

if ret.status_code != 200:
    fname = "err.txt"
    f = open(fname,"a")
    print('error occur return code = ' + str(ret.status_code), file = f)
    f.close()
    sys.exit(1)

text = ret.text
list = text.split('\n')

for line in list:
    print(line)

n = len(list)
for i in range(0, n):
    print(list[i])


◆◆ web アクセス (urllib 使用) ◆◆


requests の方が高機能だが urllib は標準でついてくる
urllib は取得先のサイトのエンコーディングを知っておく
必要がある。

import urllib.request, urllib.error

url = 'http://denki.nara-edu.ac.jp/~yabu/'

try:
    req = urllib.request.urlopen(url)
except urllib.error.URLError as e:
    if hasattr(e, 'reason'):
        print('We failed to reach a server.')
        print('Reason: ', e.reason)
    elif hasattr(e, 'code'):
        print('The server couldn\'t fulfill the request.')
        print('Error code: ', e.code)
    sys.exit(1)

page = req.read()              # page は b'....\n....\n....'
page2 = page.decode('euc-jp')  # page2 は html ファイルと同一。改行は 0x0a
                               # 変換元の漢字コードを指定。省略時は utf-8
                   # shift jis は 'shift_jis' or 'shift-jis'
lines = page2.split('\n')      # lines はリスト [b'xxxx', b'yyyy', b'zzzz', ...]

f = open("page.txt","w")
print(page, file = f, end = "")
f.close()

f = open("page2.txt","w")
print(page2, file = f, end = "")
f.close()

f = open("lines.txt","w")
print(lines, file = f, end = "")
f.close()

for line in lines:
    print(line)

for i in range(len(lines)):
    print(lines[i])


◆◆ 関数のネスト ◆◆


関数の中に関数を書くことができる。

def outer_function():
    a = 1
    print("outer 1")
    inner_function()  # call 1  NG

    def inner_function():
        print("inner")
        print("a = " + str(a))

    inner_function()  # call 2  OK
    print("outer 2")

inner_function()  # call 3  NG

・内側に書いた関数は外側の関数から呼び出せるが、
 それ以外の場所からは呼び出せない。隠蔽される。
 ゆえに call 3 は NG である。

・内側に書いた関数は、外側の関数の変数にアクセスできる
 ゆえに print("a = " + str(a)) は a = 1 となる。

・python では呼び出す関数を後ろに書くとエラーとなるので、
 call 1 も NG である。


◆◆ 関数を代入 ◆◆


def hello():
    print("hello")

func = hello   # <--- (1)
func()         # <--- (2)

(1) 変数 func に関数 hello を代入
  () はつけない。() をつけると return 値の代入となる
(2) 実行するときは () をつける


◆◆ スレッドの使い方 ◆◆


import threading

thread1 = threading.Thread(target=func1, args=(var1,), daemon=True)
thread1.start()

def func1(var1):
    global g1, g2
    スレッドの処理

・引数はタプルで渡す
 1 個のとき (var1, )  2 個のとき (var1, var2)

・スレッドではグローバル変数は global 宣言する必要がある。
 メインプログラムで使用した変数と同名の変数を使ってもローカル変数となる

・daemon=True にしておかないと、スレッドが走っている状態で
 sys.exit() すると python がハングアップする。

◆◆ serial にアクセスする ◆◆


python3 から以下のように書く必要がある。

send_str = "l"
send_str = send_str.encode('utf-8')

rs = serial.Serial('/dev/ttyAMA0', 9600, timeout=10)
rs.write(send_str)
recv_str = rs.readline()
rs.close()

recv_str = recv_str.decode('utf-8').rstrip()


◆◆ json 形式とは ◆◆


python の連想配列の宣言と同じ形式の文字列

import json

a = {"name": "tarou", "age": 23}   # 連想配列
b = json.dumps(a)                  # 文字列に変換する
c = '{"name": "tarou", "age": 23}' # 文字列

print(a)
print(type(a))

print(b)
print(type(b))

print(c)
print(type(c))

if b == c:
   print("b == c")
else:
   print("b != c")

◆◆ 画像の作成とセーブ ◆◆


fill_color = (255, 255, 255)
outline_color = (0, 0, 0)
size = (640, 480)
bg_color = (255, 0, 0)

im = Image.new('RGB', size, bg_color)
draw = ImageDraw.Draw(im)

fill_color = (0, 255, 0)
outline_color = (0, 0, 255)
# 左上は (0,0)    y は下向き増加
draw.rectangle( (x1, y1, x2, y2), \
     fill=fill_color, outline=outline_color, width=0))
# 外接する長方形を指定する
draw.ellipse( (x1, y1, x2, y2), \
     fill=fill_color, outline=outline_color, width=0))
draw.line( (x1, y1, x2, y2), fill=fill_color, width=10)

# 座標の指定は (x1, y1, x2, y2)  あるいは ((x1, y1), (x2, y2)) の
# どちらでもよい

im.save("a.png")
im.show()

# アニメーション

img_list = []

im は画像のオブジェクト
img_list.append(im)

img_list[0].save("fname.gif", save_all=True, append_images=img_list[1:], optimize=False, duration=50, loop=0)

optimize=True にすると意図せぬ結果になったりする
loop=0 : 無限ループ
dration : 各フレームの表示時間 [ms]

◆◆ class の使い方 ◆◆


python の class は C++ とは変数の扱いが大幅に
異なる。

全インスタンス共通の変数(classの内側, def の外側 で宣言)
そのインスタンス固有の変数(self をつける)
一時変数(無印)

class Stack:
    nstack = 0   # 全インスタンス共通
                 # そのクラスの最初のインスタンスを作成したときに
                 # 1 回だけ実行される。
    def __init__(self):   # コンストラクタ
        self.stack = []   # インスタンス固有
        Stack.nstack = Stack.nstack + 1    # nstack = nstack + 1 でも可能
    def __str__(self):    # print(st) のとき
        return f"{self.stack}"   # f"   " はフォーマット済文字列
                                 # 出力したい変数を { } で囲む
    def push(self, a):
        self.stack.append(a)
    def clear(self):
        self.stack = []
    def pop(self):
        n = len(self.stack)      # n はローカル変数
        ret = self.stack.pop(n-1)
        return(ret)

st = Stack()

st.push(10)
st.push(20)
st.push(30)

print(st)

a = st.pop()
print(a)
a = st.pop()
print(a)

st.clear()
print(st)

◆◆ module の使い方 ◆◆


-------- module.py -------
a = 10

def sub1():
    print("sub1")

def sub2(a):
    print(a + 10)
---------------------------

import module
print(module.a)
module.sub1()
module.sub2(20)

変数:モジュール名.変数名
関数:モジュール名.関数名


◆◆ \n が効かない ◆◆


\ は 5C だが、
IDLE で \ を入れると C2 A5 が入ることがある。

これはバックスラッシュと円記号の違いに由来する