Pythonの自作モジュール入門


Pythonの自作モジュール入門

概要

この記事では、Pythonを使った自作モジュールの作り方を紹介する。

業務では、可能な限り、

出来合いのモジュール・ライブラリ・フレームワークを使うべきである。

しかし、場合によっては自作の必要が出てくる。

その時のために、自作モジュールの作成法、配置法、使用法を解説する。

題材

簡単なAltCss(Css Preprocessor)モジュール1を作成する。

特徴

  1. CSSの個別ルールを模倣したクラスを1つ、CSS全体を模倣したクラスを1つ
  2. 両クラスとも、内容をCSS文字列にしたものをCSSファイルに書き出せるようにする
  3. 両クラスとも、コンストラクターによってのみプロパティ定義を行う

作成

事前準備(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などがある。
  • このエントリーをはてなブックマークに追加

PAGE TOP