Python の覚え書き

初版作成  2016.2.5
最終更新  2018.11.17


◆◆ 役に立つサイト ◆◆

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

◆◆ ファイル先頭 ◆◆

#!/usr/bin/python
# -*- coding: 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 で使おうとすると
上記のエラーがが発生する。


◆◆ コメント ◆◆

#  より右側  行の途中も可

◆◆ インデント ◆◆

python はインデントで if, for などの範囲を表す。
tab はスペース 8 個と等価と見なされる。

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

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


◆◆ 変数 ◆◆

宣言不要

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

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

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

◆◆ 文字列 ◆◆

連結  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.rstrip()    行末の空白 (改行記号を含む) を除去する
line = line.strip()     行頭と行末の空白を除去する

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

◆◆ 文字列の分解 ◆◆

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

list = line.split()

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

list = line.split(' ')

ではダメ。空白が n 個連続している場合、
n-1 個の長さなしの文字列が発生する。
split の省略形を使わずに書くなら、以下のようになる。

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 / 2.0   2.5
5 // 2.0   2  小数点以下切り捨て


◆◆ 表示 ◆◆

i = 10
s = "10"

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

print "abc",            最後に , をつけると改行されない。
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
"""

標準エラー出力

sys.stderr.write("error")

あるいは

print >> sys.stderr, "error"


◆◆ format 付き表示 ◆◆

a = 1234.56789
s = "abcd"

line = "<%05d> <%8.2f> <%s>" % (a, a, s)
print line          <01234> < 1234.57> <abcd>


◆◆ 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


◆◆ 配列(リスト) ◆◆

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

list = range(0, 3)       list = [0, 1, 2]

sub_list = list[2:3]     list[2] を取り出す
sub_list = list[3:]      list[3] とそれ以降
sub_list = list[:2]      list[1] とそれまで

For との組み合わせ

for i in range(0, len(var)):
    print var[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]

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']

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

ash_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]

◆◆ 継続行 ◆◆

\    バックスラッシュ

◆◆ ファイル読み込み ◆◆

オープン  クローズ

f = open('a.txt', 'r')
f.close()

エラー処理付き

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

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

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

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

1. for, next 系

for line in f:       for の使い方
    print line,

line = f.next()      next の使い方

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

try:
    while 1:
        line = f.next()
        print line,
except:
    pass

2. readline 系

while 1:
    line = f.readline()     readline の使い方
    if not line:            # if line == "":  でも同じ
        break
    print line,

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

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


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

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

print >> f, "abcde"         # 改行あり
print >> f, "abcde",        # 改行なし

f.write("abcde\n")

f.close()

◆◆ 終了 ◆◆

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

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

◆◆ 関数 ◆◆

呼び出し方は C と同じ
関数中で引数の値を変更しても、呼び出し元の変数は不変
複数の値を返したいときは ret = [1, 2, 3]    return ret  のように
リストを使えばよい。

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

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 main():
    print "main"
    func()

def func():
    print "func"

if __name__ == "__main__":
    main()

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


◆◆ cgi GET 専用 ◆◆

import sys
import os
import cgi
import commands

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>%s = %s</p>'  %  (key, form[key].value)



◆◆ cgi から外部プログラムを実行する ◆◆

import commands

ret = commands.getoutput(cmd)
#ret = os.system(cmd)      # Bad header: <p>m というエラーが発生する

print ret

ret にコマンドが標準出力に出した文字列が格納される。
os.system(cmd) では apache2 の Bad header というエラーが発生する。


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

os.chdir('dir_name')
ret = os.system(cmd)
if ret <> 0:
   print "Hit any key"
   inkey = raw_input()
   sys.exit(1)
os.chdir('..')

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

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

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

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

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

  $ sudo visudo

で以下の行を付加する。

www-data ALL=(ALL) NOPASSWD: ALL


◆◆ 文字列のマッチング・置換 ◆◆

<正規表現あり>

import re

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

先頭からマッチング(perl のマッチングと同等なのは後述する「部分一致」)
   部分一致の検索文字列の先頭に ^ を付けるのと同等と思われる。

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

正規表現は perl と全く同じ。グループの使い方も全く同じ

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

部分一致(perl のマッチングと同等)

m = re.search(str, line)

最短・最長マッチ

* + ? などは最後にマッチした場所を記録する。
最初にマッチしたものにマッチさせたい場合は *? +? ?? の
ように ? をつける

ex. 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

<正規表現なし>

検索

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

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

置換

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

◆◆ 正規表現 ◆◆

.   任意の1文字
x?  0 個あるいは 1 個の x
x*  0 個以上の x
x+  1 個以上の x
^   行の先頭
$   行末
\s  空白文字 ( スペース、タブ、改行 )
\S  空白以外の文字
\*  * そのもの

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


◆◆ 標準エラー出力 ◆◆

import sys

sys.stderr.write("usage: % makehtml.py fname")

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

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 = "%04d/%02d/%02d %02d:%02d:%02d" % \
           (year,month,day,hour,minute,second)
out_line = out_line + " " + ondo_line


◆◆ ファイル・フォルダの作成・削除 ◆◆

import os, shutil

cwd = os.getcwd()       cwd の取得
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.remove(fname)        ファイル削除
os.mkdir(path)          ディレクトリ作成

shutil.copyfile(src_fname, dst_fname)  ファイルコピー
shutil.rmtree(dir)                     ディレクトリ以下全て消去

◆◆ キー入力 ◆◆

print "hit any key"
instr = raw_input()


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

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_line = so.recv(size)
so.close()

print recv_line

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

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()

◆◆ ディレイ ◆◆

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

◆◆ ftp ftps 2018.1.31 ◆◆

----------------- ftp -----------------
import ftplib

server     = "aaa.bbb.ne.jp"
user_id    = "user-id"
passwd     = "password"
from_fname = "/home/pi/aaa/bbb.jpg"   // full path
to_fname   = "dir/bbb.jpg"            // from top dir

ftp = ftplib.FTP(server)
ftp.set_pasv("true")
ftp.login(user_id, passwd)

fp = open(from_fname, 'rb')

line = "STOR " + to_fname

ftp.storbinary(line, fp)
ftp.close()
fp.close()
-----------------------------------------------

--------------- ftps --------------------------
from ftplib import FTP_TLS

server     = "aaa.bbb.ne.jp"
user_id    = "user-id"
passwd     = "password"
from_fname = "/home/pi/aaa/bbb.jpg"   // full path
to_fname   = "dir/bbb.jpg"            // from top dir

ftps = FTP_TLS(server)
ftps.login(user_id, passwd)
ftps.prot_p()

fp = open(from_fname, 'rb')

line = "STOR " + to_fname

ftps.storbinary(line, fp)
ftps.close()
fp.close()
---------------------------------------------------


◆◆ 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())


◆◆ web アクセス ◆◆

<参照 url>
https://docs.python.org/ja/2.7/howto/urllib2.html

アマゾンの本の順位を取り出す

import sys
import urllib2
import re

url = "amazon 内の本の url"
try:
    req = urllib2.urlopen(url)
except urllib2.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()
lines = page.split('\n')

for i in range(0, len(lines)):
    line = lines[i]
    m = re.match('.* - (.+)位.*',line)
    if m != None:
        rank = m.group(1)
        rank = rank.replace(',','')

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

f = open('rec.txt','a')
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
print >> f, time_str + "," + value
#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/


◆◆ 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) on を認めてよいか否かのフラグを使う方法

flag = 1      # flag = 1 のときカウント可能
while True:
    now = input()
    if now == OFF:
        flag = 1
    else             # now == ON
        if flag = 1:
            manage()
            flag = 0