お題
前回、Rubyで書いた簡易ツール「ローカルJSONファイルに保存するキーバリューストア」のPython3版。
シリーズ物としては、もともと複数言語で同じ内容のプログラムを書いて比較した「簡単なツール作成を通して各プログラミング言語を比較しつつ学ぶ」からの改善版。
試行Index
- 第1回:簡単なツール作成を通して各プログラミング言語を比較しつつ学ぶ
- 第2回:【改善編】簡単なツール作成を通してRubyを学ぶ
- 第3回:【改善編】簡単なツール作成を通してPython3を学ぶ
- 第4回:【改善編】簡単なツール作成を通してGolangを学ぶ
- 第5回:【改善編】簡単なツール作成を通してJavaを学ぶ
- 第6回:【改善編】簡単なツール作成を通してScalaを学ぶ
- 第7回:簡単なツール作成を通してRustを学ぶ
- 第8回:【改善編】簡単なツール作成を通してRustを学ぶ
更新履歴
- 【2019/09/21】コメントにならってプログラム修正
実装・動作確認端末
# 言語バージョン
$ python3 --version
Python 3.6.8
# IDE - PyCharm
PyCharm 2019.2 (Professional Edition)
Build #PY-192.5728.105, built on July 24, 2019
実践
要件
アプリを起動すると、キーバリュー形式でテキスト情報をJSONファイルに保存する機能を持つコンソールアプリ。
オンメモリで保持していた点だけ除けば前回と同じ仕様なので詳細は以下参照。
https://qiita.com/sky0621/items/32c87aed41cb1c3c67ff#要件
ソース全量
解説
全ソースファイル
Pythonソース | 説明 |
---|---|
main.py | アプリ起動エントリーポイント |
store_info.py | キーバリュー情報を保存するストア(JSONファイル)に関する情報を扱う。 現状は「ファイル名」だけ保持 |
commands.py | キーバリューストアからの情報取得や保存、削除といった各コマンドを管理。 コマンドの増減に関する影響は、このソースに閉じる。 |
command.py | 各コマンドに共通のインタフェース(と言いたいがPythonにはインタフェースが無いので普通のclass) |
save.py | キーバリュー情報の保存を担う。 |
get.py | 指定キーに対するバリューの取得を担う。 |
list.py | 全キーバリュー情報の取得を担う。 |
remove.py | 指定キーに対するバリューの削除を担う。 |
clear.py | 全キーバリュー情報の削除を担う。 |
help.py | ヘルプ情報の表示を担う。 |
end.py | アプリの終了を担う。 |
[main.py]アプリ起動エントリーポイント
[main.py]
from commands import Commands
from store_info import StoreInfo
def main():
commands = Commands(StoreInfo("store.json"))
print("Start!")
running = True
while running:
running = commands.exec(input().split())
if __name__ == '__main__':
main()
[store_info.py]ストア情報の管理
[store_info.py]
# Pythonには定数はない。ただ、全て大文字の変数を定数と扱うのが暗黙のルールとなっているらしい。
DEFAULT_STORE_NAME = "store.json"
class StoreInfo:
# オブジェクト生成時に自動で呼ばれるメソッド(コンストラクタ扱い)
# デフォルト引数が指定できる
# インスタンス変数privateにする方法は無いが慣習的に「_」で始まる変数はprivate扱いとする
def __init__(self, _store_name=DEFAULT_STORE_NAME):
self._store_name = _store_name
def get_name(self):
return self._store_name
[commands.py]各コマンドの管理
[commands.py]
import os
from clear import Clear
from end import End
from get import Get
from help import Help
from list import List
from remove import Remove
from save import Save
from store_info import StoreInfo
class Commands:
def __init__(self, store_info: StoreInfo):
self.commands = {
"end": End(store_info),
"help": Help(store_info),
"clear": Clear(store_info),
"save": Save(store_info),
"get": Get(store_info),
"remove": Remove(store_info),
"list": List(store_info),
}
if not os.path.isfile(store_info.get_name()):
self.commands.get("clear").exec(self)
def exec(self, cmds):
if len(cmds) < 1:
print("no target")
return True
c = self.commands.get(cmds[0])
if c is None:
print("no target")
return True
if len(cmds) == 1:
return c.exec([])
else:
return c.exec(cmds[1:])
[command.py]各コマンドの親クラス
[command.py]
# Ruby同様、Pythonも言語仕様にinterfaceを持たないのでclassで定義
# このクラスをそのまま使うことは想定していないため、デフォルトの処理としてエラースローしておく
class Command:
def exec(self, args):
raise NotImplementedError
各コマンドクラス
各コマンドについては、ストア情報がオンメモリのハッシュからJSONに変わった点以外はやることは一緒。(なので説明省く。)
■保存
[save.py]
import json
from command import Command
class Save(Command):
def exec(self, args):
if len(args) != 2:
print("not valid")
return True
with open(self.store_info.get_name(), 'r') as fr:
json_data = json.load(fr)
json_data[args[0]] = args[1]
with open(self.store_info.get_name(), 'w') as fw:
json.dump(json_data, fw)
return True
■1件取得
[get.py]
import json
from command import Command
class Get(Command):
def exec(self, args):
if len(args) != 1:
print("not valid")
return True
with open(self.store_info.get_name(), 'r') as fr:
json_data = json.load(fr)
if args[0] in json_data:
print(json_data[args[0]])
return True
■全件取得
[list.py]
import json
from command import Command
class List(Command):
def exec(self, args):
with open(self.store_info.get_name(), 'r') as fr:
json_data = json.load(fr)
print('"key","value"')
for k, v in json_data.items():
print(f'"{k}","{v}"')
return True
■1件削除
[remove.py]
import json
from command import Command
class Remove(Command):
def exec(self, args):
if len(args) != 1:
print("not valid")
return True
with open(self.store_info.get_name(), 'r') as fr:
json_data = json.load(fr)
if args[0] in json_data:
del json_data[args[0]]
with open(self.store_info.get_name(), 'w') as fw:
json.dump(json_data, fw)
return True
■全件削除
[clear.py]
import json
from command import Command
class Clear(Command):
def exec(self, args):
with open(self.store_info.get_name(), 'w') as f:
json.dump({}, f, ensure_ascii=False)
return True
■ヘルプ
[help.py]
from command import Command
class Help(Command):
def exec(self, args):
msg = """
[usage]
キーバリュー形式で文字列情報を管理するコマンドです。
以下のサブコマンドが利用可能です。
save ... keyとvalueを渡して保存します。
get ... keyを渡してvalueを表示します。
remove ... keyを渡してvalueを削除します。
list ... 保存済みの内容を一覧表示します。
clear ... 保存済みの内容を初期化します。
help ... ヘルプ情報(当内容と同じ)を表示します。
end ... アプリを終了します。
"""
print(msg)
return True
■アプリ終了
[end.py]
from command import Command
class End(Command):
def exec(self, args):
print("End!")
return False
まとめ
次は、どの言語にしようか。