Python 3 の覚え書き

初版作成  2016.2.5
最終更新  2020.03.05


◆◆ 役に立つサイト ◆◆


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

◆◆ ファイル先頭 ◆◆


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


◆◆ コメント ◆◆


#  より右側  行の途中も可

◆◆ インデント ◆◆


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(  ) が必要。

◆◆ 代入 ◆◆


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


◆◆ 文字列 ◆◆


連結  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 個の長さなしの文字列が発生する。
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", 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""")

標準エラー出力

sys.stderr.write("error")

あるいは

print("error", file = sys.stderr)

◆◆ format 付き表示 ◆◆


a = 1234.56789
s = "abcd"

python 2
line = "<%05d> <%5d> <%8.2f> <%s>" % (a, a, a, s)                   python 2
print line

<01234> < 1234> < 1234.57> <1234.57> <abcd>

python 3
line = "<{:05}> <{:5}> <{:8.2f}> <{:.2f}> <{}>".format(int(a),int(a),a,a,s)
print(line)       <01234> < 1234> < 1234.57> <1234.57> <abcd>

<01234> < 1234> < 1234.57> <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)   要素数

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

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", encoding = "utf-8")   encoding は省略可  "shift-jis"
f.close()


with を使うと close を省略可能

with open("a.txt", "r", encoding = "utf-8") as f:
    コード   (インデント必要)

エラー処理付き

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)

3. readlines   ファイル全体を読み込む

lines = f.readlines()     # ファイル全てを読み込む
for i in range(len(lines)):
    lines[i] = lines[i].rstrip("\n")
    print("|" + lines[i] + "|")

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

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

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

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


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

if a == 12:

は成立しない。

if a == "12":    あるいは   if int(a) == 12:

なら成立する。


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


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]    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>{} = {}</p>".format(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 = 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.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.serach の検索文字列の先頭に ^ を
つけるのと同等と思われる)

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

<正規表現なし>

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

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

・何文字目か?

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

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

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

◆◆ キー入力 ◆◆


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)

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