【デザインパターン勉強】Facadeメソッド
ざっくり概要
Facade(建物の正面)という意味を指しています。 このパターンは内部の処理を簡素化したクラスを一つにすることで、複雑な処理を見せないメソッドです。
自分なりの言葉で言うと、処理を一つのクラスに集約したら見通して良くなって、すぐわかるんじゃね? ということです。
コード
ココのコードを利用して説明します。
import time class TC1: def run(self): print("test1") print("settup") print("finish") time.sleep(0.1) class TC2: def run(self): print("test2") print("settup") print("finish") time.sleep(0.1) class TC3: def run(self): print("test3") print("settup") print("finish") time.sleep(0.1) class TestRunner: def __init__(self): self.tc1 = TC1() self.tc2 = TC2() self.tc3 = TC3() self.tests = [self.tc1,self.tc2,self.tc3] def runAll(self): [i.run() for i in self.tests] if __name__ == '__main__': testrunner = TestRunner() testrunner.runAll()
ざっくり解説
TestRunnerクラス
このクラスで処理をまとめています。 具体的にはTC1~3で標準出力するメッドをrunAllに集約しています。
今回はTC1~TC3という3つのクラスのメソッドを用いて集約してますが、 もっと複雑なケースを考えてみます。
- そのままAクラスはインスタンス化して、
- Aのメソッドを呼び出し
- Bクラスメソッドを使って
・・・C、D、E、F、Gの処理へ続く。
といったケースですと、複雑で何しているかわからなくなりますね。
一つのクラス(窓口)に集約化することで見通しが良くなります。 クラス名もわかりやすく(例えば、MakeCSV具体的な作業を明示) とか、わかりやすくすれば初見の人でも通りやすくなるんじゃないでしょうか。
利用方法
個人開発をしている人にはあまり実りのあるメソッドとはいえないと思います(処理の流れをわかっているため)。 しかしながら、何人も関わっている開発プロダクトの場合、は必須のパターンだと思います。
参考文献
【デザインパターン勉強】Singletonメソッド
ざっくり概要
このパターンの目的は 一つのクラスに複数のインスタンスを作らないようにすることです。
利用シーン... パスワード管理のメソッドなど管理が厳重にする必要があるメソッドを管理する時に使うんでしょうかね。。
コード
class Singleton: initial_class_name = None # クラスインスタンス化した際、__new__→__init__が呼び出される。 def __init__(self): print("init") def __new__(self): if Singleton.initial_class_name is None: print("new") Singleton.initial_class_name = super().__new__(self) return Singleton.initial_class_name if __name__ == '__main__': a = Singleton() #こんな感じで出力→<__main__.Singleton object at 0x106614438> b = Singleton() #↑と同じ。<__main__.Singleton object at 0x106614438> print(a) print(b) print(a is b)
ざっくり解説。
classをインスタンス化した際にクラス変数(initial_class_name)をつけておくことで フラグ(インスタンスを生成したかどうか)として機能させています。
今回は参考URLが非常に秀逸だったのであまり語ることはないです。。
参考URL
【デザインパターン勉強】adapterメソッド
ざっくり概要
adapterメソッドとはざっくり言うと既に 提供されているclassと既に必要な抽象classを結びつけてプログラムを作成する方法です。 ポイントは、仲介役classを設定(提供されているclassと必要な抽象class)することです。
登場メソッド
必要な抽象メソッド(抽象classに存在)
提供されているメソッド(提供されているclass)
本来であれば・・・
抽象classと提供されているclassのメソッドを同一化すれば問題ないです が、 今回は 所々の事情で 修正出来ないケースを想定しています。
コード
#python3.5.2 import abc import os import csv class Format: def __init__(self,content): self.__content = content def make_file_csv(self,file_name): f = open('{}.csv'.format(file_name),'w') f.write(self.__content) def make_file_txt(self,file_name): f = open('{}.txt'.format(file_name),'w') f.write(self.__content) class TextMake(metaclass=abc.ABCMeta): @abc.abstractmethod def make_csv(self): pass @abc.abstractmethod def make_txt(self): pass class TextMakeFormat(TextMake,Format): def __init__(self,content): super().__init__(content) def make_csv(self,file_name): self.make_file_csv(file_name) def make_txt(self,file_name): self.make_file_txt(file_name) if __name__ == '__main__': textobj = TextMakeFormat('おはようございます。\n今日も元気で頑張りましょう。') textobj.make_csv("csv_file") #TextMakeFormatを経由してcsvファイル作成 textobj.make_txt("txt_file") #TextMakeFormatを経由してtxtファイル作成
各クラスの概要
Format(提供されているclass)
具体的処理メソッド(csv txtファイル作成)が記述されています。
TextMake(必要な抽象クラス)
既に必要とされているケースであり、メソッド(≠Formatのメソッド)が記述されています。
TextMakeFormat(仲介役)
FormatとTextMakeクラスの仲介役をします。 具体的には以下の手順となります。
- TextMake(必要な抽象クラス)をオーバーライドしてメソッドを宣言
- オーバーライドしたメソッドにFormat(提供されているclass)のメソッドを記述 これにより、必要な抽象class→提供されているClassの仲介を果たします。
感想
理解不足もあるかもしれませんが、利用シーンがあまり思い浮かびませんでした。。
参考文献
【デザインパターン勉強】builderメソッド
ざっくり概要
builderメソッドを自分の言葉でいうと、
作成するメソッドの役割は同じだが、異なる結果を得るプログラムを何個も 作る際、有用なプログラム(パターン) といった感じです。
ここで重要な点は 作成するメソッドの役割は同じ ということです。
本記事で記載したコードは税計算を表示するプログラムです。
登場メソッド
- set_total(金額インプット)
- set_tax(税率インプット)
- message_tax(消費税を計算、出力)
コード
# python3.5.2 import abc class TaxCalcDirector(): def __init__(self, builder): self.__builder = builder def construct(self,total,tax): self.__builder.set_total(self,total) self.__builder.set_tax(self,tax) self.__builder.message_tax(self) class AbstractTaxCalcBuilder(metaclass=abc.ABCMeta): @abc.abstractmethod def set_total(self,total): pass @abc.abstractmethod def set_tax(self,rate): pass @abc.abstractmethod def message_tax(self,message): pass class ConsumptionTaxBuilder(AbstractTaxCalcBuilder): def __init__(self): self.rate = 0 self.total = 0 def set_total(self,total): self.total = total def set_tax(self,rate): self.rate = rate def message_tax(self): tax = self.total * self.rate message = " ".join(["あなたの払う税金は",str(tax),"円です"]) print(message) class DeductionTaxBuilder(AbstractTaxCalcBuilder): def __init__(self): self.rate = 0 self.total = 0 def set_total(self,total): self.total = total def set_tax(self,rate): self.rate = -1 * rate def message_tax(self): tax = self.total * self.rate message = " ".join(["あなたが還付される税金は",str(tax),"円です"]) print(message) def main(): # 支払う税金 tax = TaxCalcDirector(ConsumptionTaxBuilder) tax.construct(1000,0.3) #あなたの払う税金は 300.0 円です # 還付 tax = TaxCalcDirector(DeductionTaxBuilder) tax.construct(200,0.1) #あなたが還付される税金は -20.0 円です if __name__ == '__main__': main()
main()メソッドでは、行いたい作業(クラス名)をインスタンス化しています。
作業に応じて、
tax = TaxCalcDirector(クラス) #クラス→ ConsumptionTaxBuilderやDeductionTaxBuilder
とすることで、クラスの継承を変えて、税の還付金計算に変更することが出来ます。
つまり、具体的な作業を認識せずに処理を変えられます。
今回は消費税計算、税の還付金計算と処理を変えています。
各クラスの役割
AbstractTaxCalcBuilder(Builder)
インスタンスを作成する際に必要な作業(メソッド)を規定しています。 つまり、以下のメソッドとなります。
- 金額インプット set_total
- 税率インプット set_tax
- 消費税を計算、出力 message_tax
メソッドをオーバーライドし忘れを防ぐために抽象クラスとしています。 教科書では Builder と呼ばれますが、「現場監督」といったほうがイメージしやすい気がします。
ConsumptionTaxBuilder(ConcreteBuilder)
AbstractTaxCalcBuilderで定義されたメソッドを具体化しています。
教科書では ConcreteBuilder と呼ばれますが「現場作業員」といったほうがイメージしやすい気がします。
TaxCalcDirector(Director)
AbstractTaxCalcBuilderのメソッドを用いてインスタンスを生成しています。 また、インスタンス作成の際、ConsumptionTaxBuilder継承してインスタンス化しています。
必要な情報は builderのメソッドとConsumptionTaxBuilderというConcreteBuilder のクラス名のみです。
ここで、重要なのは以下の2点です。
- AbstractTaxCalcBuilder(Builder)のメソッドのみを使っていること
- 具体的なアウトプットを得る際はConcreteBuilderのクラス名を継承して実行
教科書では Directorと呼ばれて説明されています。監督する立場なのでイメージしやすいですね。
ここでわかることは アウトプットを変更する際、ConcreteBuilderクラス名を修正して実行すればいい
→つまり、クラス名だけで柔軟にアウトプットを変えることが出来るということです。
参考文献
【Pythonメモ】collectionsモジュール
# collectionsモジュール勉強 import collections #counterメソッドはカウント系メソッド c = collections.Counter() c['spam'] += 1 c[100] += 1 c[200] += 2 c[200] += 3 # Counter({200: 5, 'spam': 1, 100: 1}) print(type(c)) c2 = collections.Counter() c2['spam'] = 1 c2[200] = 1 print(c & c2) #Counter({200: 1, 'spam': 1}) print(c + c2) #Counter({200: 6, 'spam': 2, 100: 1}) # ChainMapはdict d1 ={'spam':1} d2 ={'ham':2} d = collections.ChainMap(d1,d2) print(d)
エクセルコピペ作業自動化(xls、xlsx)
エクセルをpythonで扱う
仕事していると、エクセルで延々と だるい コピペ作業とかってありませんか。
先日、pythonを使ってコピペ作業を自動化しました。
その時、用いたpython用ライブラリ、使用方法を紹介します。
※今回は読み込みにしぼって説明
ライブラリ
調査したところ、それぞれ対応しているライブラリは以下の通りのようです。
※ xlsと xlsx 両方読み込みを行なうライブラリ、見当たりませんでした。
※ 諸事情によりxls形式を扱う人は多いのではないでしょうか。xlsxはexcel2007以降
読み込み方法
xlsとxlsx形式を扱うライブラリですが、 手順は以下に記載したとおりほとんど同じです。
- 1.ワークシートを指定
- 2.指定したワークシートからブック名一覧を取得
- 3.ブックを指定
- 4.指定したブックの値や入力された範囲を取得
あとはメソッド名を変えてあげれば対応OKです。
読み込んだファイル
今回は以下のファイル(xlsとxlsx形式)を読み込みました。 ファイル名:xls_test.xls
プログラム例
実行環境:python 3.5.2
from abc import ABCMeta, abstractmethod import re import glob import openpyxl import xlrd class ExcelTemplate(metaclass=ABCMeta): # デコレータ(abstractmethod)をつけると、サブクラスが抽象メソッドをオーバーライドしないとエラーとなる @abstractmethod def read_sheet(): pass def get_filename(self,file): print(file + "の読み取りを行います。") class XlsRead(ExcelTemplate): # xlsファイル読み込み def read_sheet(self,worksheet): # ワークシートを指定 book = xlrd.open_workbook(worksheet) # シート名を取得 戻り値はリスト print(book.sheet_names()) sheet_name = book.sheet_names()[0] # 一番目のシートを指定 sheet_1 = book.sheet_by_name(sheet_name) print("値が入力されている 行列数:",sheet_1.ncols,sheet_1.nrows) print("1行目:1列目の値は:",sheet_1.cell(0, 0).value) class XxlsRead(ExcelTemplate): # xlsxファイル読み込み def read_sheet(self,worksheet): # ワークシートを指定 book = openpyxl.load_workbook(worksheet) # シート名表示 戻り値はリスト print(book.get_sheet_names()) # 一番初めのシート名を取得 sheet_name = book.get_sheet_names()[0] # 一番目のシートを指定 sheet_1 = book[sheet_name] print("値が入力されている 行列数:",sheet_1.max_column,sheet_1.max_row) print("2行目:1列目の値は:",sheet_1.cell(row=2,column=1).value) def main(): xls_instance = XlsRead() xxls_instance = XxlsRead() worksheets = glob.glob(パスを指定) # 取得したエクセルリストに対して # xlsファイル、xlsmファイルに応じて切り替え for worksheet in worksheets: if re.match(r".+\.xls$",worksheet): xls_instance.get_filename(worksheet) xls_instance.read_sheet(worksheet) print("------------") elif re.match(r".+\.xlsx$",worksheet): xxls_instance.get_filename(worksheet) xxls_instance.read_sheet(worksheet) print("------------") else: pass if __name__ == '__main__': main()
$ python template_xls.py xls_test.xlsの読み取りを行います。 ['Sheet1'] 値が入力されている 行列数: 1 10 1行目:1列目の値は: xls_test1 ------------ xlsx_test.xlsxの読み取りを行います。 ['Sheet1'] 値が入力されている 行列数: 1 10 2行目:1列目の値は: xlsx_test2 ------------