Pythonの自作モジュール入門
概要
この記事では、Pythonを使った自作モジュールの作り方を紹介する。
業務では、可能な限り、
出来合いのモジュール・ライブラリ・フレームワークを使うべきである。
しかし、場合によっては自作の必要が出てくる。
その時のために、自作モジュールの作成法、配置法、使用法を解説する。
題材
簡単なAltCss(Css Preprocessor)モジュール1を作成する。
特徴
- CSSの個別ルールを模倣したクラスを1つ、CSS全体を模倣したクラスを1つ
- 両クラスとも、内容をCSS文字列にしたものをCSSファイルに書き出せるようにする
- 両クラスとも、コンストラクターによってのみプロパティ定義を行う
作成
事前準備(sample.html)
Cssを受け入れる以下のような内容のHTMLを作成する
<html>
<header>
<link rel="stylesheet" href="./sample.css">
</header>
<h1>サンプルページ</h1>
<article>
<h2>言語リスト</h2>
<ol class="language-list">
<li>Python</li>
<li>Ruby</li>
<li>Perl</li>
</ol>
<h2>BDFLリスト</h2>
<ol class="bdfl-list">
<li>Guido (現在は名誉BDFL)</li>
<li>Matz</li>
<li>Larry Wall</li>
</ol>
</article>
</html>
モジュール作成
目標成果物 (css.py)
# ***AltCssのモジュール***
# 型アノテーションのためのライブラリ
from typing import Dict, List
# ファイル読み書きのためのライブラリ
from pathlib import Path
# Cssの個別ルールを模倣したクラス
class CssRule:
target : str
content : Dict[str, str]
# プロパティに値を代入する形式的コンストラクター
def __init__(
# Pythonはインデントが深くなり横長になりやすい
# 3つ以上の引数がある場合などは、意識してコードを縦にする
self,
target : str,
content : Dict[str, str]
):
self.target = target
self.content = content
# オブジェクトの文字列表現
# 対応するCSSルールそのものである
def __str__(self):
# Cssルールの内容を構築
# 内容部分は4つの半角スペースでインデントして整形
header : str = f"{self.target} {{\n"
content : List[str] = [f" {attribute[0]} : {attribute[1]} ;\n"
for attribute in self.content.items()]
closer : str = f"}}\n"
return (header + "".join(content) + closer)
# CSSファイルへの内容書き出し
def to_file(
self,
path : str,
encoding : str = "utf-8"
):
# 既存ファイルには追記、ないファイルは新規作成
Path(path).touch()
with open(
file = path,
mode = "a",
encoding = encoding
) as css_file:
css_file.write(self.__str__())
# Css全体を模倣したクラス
class Css:
path : str
rules : List[CssRule]
# プロパティに値を代入する形式的コンストラクター
def __init__(
self,
path : str,
rules : List[CssRule]
):
self.path = path
self.rules = rules
def __str__(self):
# 内部でCssRuleクラスの__str__()メソッドを利用
return ("".join([str(each_rule) for each_rule in self.rules]))
# CSSファイルへの内容書き出し
def to_file(
self,
encoding : str = "utf-8"
):
# 内部でCssRuleクラスのto_file()メソッドを利用
[each_rule.to_file(self.path, encoding) for each_rule in self.rules]
# モジュールなので、直接呼び出されても何もしない
if __name__ == "__main__":
pass
オブジェクトのプロパティ定義
CssRuleクラス
class CssRule:
# ルール適応対象のHTML要素を指定する文字列
target : str
# ルールの中身
content : Dict[str, str]
Cssクラス
class Css:
# 書き出されるCSSファイルのパス
path : str
# CSSの具体的なルールたち
rules : List[CssRule]
オブジェクトのコンストラクター定義
CssRuleクラス
class CssRule:
# プロパティに値を代入する形式的コンストラクター
def __init__(
# Pythonはインデントが深くなり横長になりやすい
# 3つ以上の引数がある場合などは、意識してコードを縦にする
self,
target : str,
content : Dict[str, str]
):
self.target = target
self.content = content
Cssクラス
class Css:
# プロパティに値を代入する形式的コンストラクター
def __init__(
self,
path : str,
rules : List[CssRule]
):
self.path = path
self.rules = rules
オブジェクトの文字列表現定義
CSSの文字列形式に沿うように、以下のフォーマットにする。
対象ノード {
プロパティ : 値 ;
プロパティ : 値 ;
(後略)
}
CssRuleクラス
CssRuleクラスのオブジェクトにおいて、
プロパティと値のセットは辞書型のcontent
に、
対象ノードは文字列型のtarget
に、それぞれ格納されている。
後は、上記のフォーマットに従い、
文字列に書き出すだけである。
フォーマット文字列f"文字列"
を用いる。
class CssRule:
# オブジェクトの文字列表現
# 対応するCSSルールそのものである
def __str__(self):
# Cssルールの内容を構築
# 内容部分は4つの半角スペースでインデントして整形
# ターゲット要素、半角スペース、{、をフォーマット文字列に埋め込む
header : str = f"{self.target} {{\n"
# 辞書型のcontentの各組(item)ごとに、
# 最初の要素(プロパティ名)と次の要素(値)を
# CSSの文字列に埋め込む
content : List[str] = [f" {attribute[0]} : {attribute[1]} ;\n"
for attribute in self.content.items()]
# 最後の}をフォーマット文字列に埋め込む
closer : str = f"}}\n"
# contentのみリストなので、結合する。最後すべての要素を足し算して返す
return (header + "".join(content) + closer)
Cssクラス
class CSS:
def __str__(self):
# 内部でCssRuleクラスの__str__()メソッドを利用
return ("".join([str(each_rule) for each_rule in self.rules]))
オブジェクトのCSSファイルへの書き出し定義
オブジェクトの文字列表現を、そのままファイルに書き出す。
CssRuleクラス
class CssRule:
# CSSファイルへの内容書き出し
def to_file(
self,
path : str,
encoding : str = "utf-8"
):
# 既存ファイルには追記、ないファイルは新規作成
Path(path).touch()
with open(
file = path,
mode = "a",
encoding = encoding
) as css_file:
css_file.write(self.__str__())
Cssクラス
class Css:
# CSSファイルへの内容書き出し
def to_file(
self,
encoding : str = "utf-8"
):
# 内部でCssRuleクラスのto_file()メソッドを利用
[each_rule.to_file(self.path, encoding) for each_rule in self.rules]
ドキュメントの書き方
ここでは要約コメントのやり方を採用している。
より正式には、pydocとして、
クラスやメソッドの定義の直下に、以下のフォーマットでコメントする。
"""
内容
"""
配置
以下のような配置構成にする。
モジュールディレクトリの直下に、
モジュールのクラスが入ったcss.py
ファイルと、
空の__init__py
ファイルを配置する。
__init__py
は、pythonのモジュールを
作るにあたり、必須のファイルである。
/code_sample -> この部分の名前は自由に設定してよい。
/css_module -> Pythonのモジュール名。
/__init__.py -> 必ずこのファイルを置く。内容は空でよい。
/css.py -> ライブラリ名
/write_css.py -> 実行するファイル
/sample.html -> Cssが適用される対象のHTML
実行
実行スクリプト (write_css.py)
write_css.py
に以下のようなコードを入力する
# AltCssモジュールの読み込み
from css_modules.css import Css, CssRule
# スクリプトとして実行したときにはCSSを書きだす
if __name__ == "__main__":
# 全体としてのCss生成
Css("./sample.css", [
# 名前付き引数でコンストラクター呼び出し
# Pythonとしての可読性を重視
CssRule(
target = ".language-list",
content = {
"font-size" : "12px",
"color" : "blue"
}
),
# 位置引数でコンストラクター呼び出し
# Cssに近づけた外見
CssRule(".bdfl-list", {
"font-size" : "15px",
"color" : "red"
})
]).to_file()
# 個別のルール追加
CssRule(
target = "[class*=list]",
content = {
"border" : "solid"
}
).to_file("./sample.css")
入力コマンド
code_sample
直下に移動し、以下のコマンドを入力する。
python ./write_css.py
結果
動画
生成されたCss(sample.css)
.language-list {
font-size : 12px ;
color : blue ;
}
.bdfl-list {
font-size : 15px ;
color : red ;
}
[class*=list] {
border : solid ;
}
HTMLへの反映
反映前
反映後
脚注
1 実際のプロダクトで使われているものとしては、ScssやSass、Stylus、Lessが有名。マイナーなものでは、Haskellを用いたClayなどがある。↩