【デザインパターン勉強】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具体的な作業を明示) とか、わかりやすくすれば初見の人でも通りやすくなるんじゃないでしょうか。

利用方法

個人開発をしている人にはあまり実りのあるメソッドとはいえないと思います(処理の流れをわかっているため)。 しかしながら、何人も関わっている開発プロダクトの場合、は必須のパターンだと思います。

参考文献

Javaで学ぶデザインパターン入門

【デザインパターン勉強】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

http://blanktar.jp/blog/2016/07/python-singleton.html

【デザインパターン勉強】adapterメソッド

ざっくり概要

adapterメソッドとはざっくり言うと既に 提供されているclassと既に必要な抽象classを結びつけてプログラムを作成する方法です。 ポイントは、仲介役classを設定(提供されているclassと必要な抽象class)することです。

登場メソッド

必要な抽象メソッド(抽象classに存在)

  • make_csv(CSVファイル内容作成)
  • make_txt(TXTファイル内容作成)

提供されているメソッド(提供されているclass)

  • make_file_csv(CSVファイル書き込み)
  • make_file_txt(TXTファイル書き込み)

本来であれば・・・

抽象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クラスの仲介役をします。 具体的には以下の手順となります。

    1. TextMake(必要な抽象クラス)をオーバーライドしてメソッドを宣言
    1. オーバーライドしたメソッドにFormat(提供されているclass)のメソッドを記述 これにより、必要な抽象class→提供されているClassの仲介を果たします。

感想

理解不足もあるかもしれませんが、利用シーンがあまり思い浮かびませんでした。。

参考文献

Javaで学ぶデザインパターン入門

【デザインパターン勉強】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クラス名を修正して実行すればいい

→つまり、クラス名だけで柔軟にアウトプットを変えることが出来るということです。

参考文献

Javaで学ぶデザインパターン入門

Rails でログロテート

久しぶりにrubyネタ

知らない間に自分が作ったサービスが止まっていた件

サービス

確認してみたら定期的にproduction logを削除していなかった。。。

  config.logger = Logger.new("log/production.log", 5, 10*1024*1024)
  #第一引数ログパス、第二引数は保存するファイル数、第三引数は容量
  #古いログは削除されていく

参考

どーでも良いけど

個人的にはshellスクリプトで定期的に削除するプログラムを cronに登録した方がわかりやすい

【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用ライブラリ、使用方法を紹介します。

※今回は読み込みにしぼって説明

ライブラリ 

調査したところ、それぞれ対応しているライブラリは以下の通りのようです。

xlsxlsx 両方読み込みを行なうライブラリ、見当たりませんでした。

※ 諸事情によりxls形式を扱う人は多いのではないでしょうか。xlsxはexcel2007以降

読み込み方法

xlsとxlsx形式を扱うライブラリですが、 手順は以下に記載したとおりほとんど同じです。

  • 1.ワークシートを指定
  • 2.指定したワークシートからブック名一覧を取得
  • 3.ブックを指定
  • 4.指定したブックの値や入力された範囲を取得

あとはメソッド名を変えてあげれば対応OKです。

読み込んだファイル

今回は以下のファイル(xlsとxlsx形式)を読み込みました。 ファイル名:xls_test.xls

https://gyazo.com/3925b2837db18d3da1ce9c951b933197

プログラム例

実行環境: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
------------

参考文献

Javaで学ぶデザインパターン