daemonfreaks.com

pythonでimport文で指定したクラスを動的生成したい

posted by jun-g at Tue, 03 Apr 2007 02:16 JST

ここんとこずーっと悩んでて解決方法がわからんのでブログで晒してみる。

pythonでMochiKit.DOMのcreateDOMみたいのがあったら便利かなと思って下記のようなクラスを書いた。

class Tag:
    """A base class of HTML tag."""

    name = None
    inline = True
    container = False

    def __init__(self, *nodes, **attributes):
        self.nodes = nodes
        self.attributes = attributes

    def __str__(self):
        return self.serialize()

    def serialize(self):
        text = ""
        for node in self.nodes:
            if isinstance(node, Tag):
                text += node.serialize()
                if not node.inline:
                    text += "\n"
            else:
                text += node
        tag = "<" + self.name
        attrs = map("=".join, [(x[0], "\"%s\"" % (x[1])) for x in self.attributes.items()])
        if len(attrs) > 0:
            tag += " " + " ".join(attrs)
        if text == "":
            tag += " />"
            if self.container:
                tag += "\n"
        else:
            tag += ">"
            if self.container:
                tag += "\n"
            tag += text + "</" + self.name + ">"
        return tag

サニタイズとか全然してないけど、とりあえずなのであまり気にしない。こいつから各タグのサブクラスを作っておくと、

class DIV(Tag):
    name = "div"
    inline = False
    container = True

class A(Tag):
    name = "a"
    inline = True
    container = False

下記のように使える。

>>> a = A("DaemonFreaks.com", href="http://www.daemonfreaks.com/")
>>> div = DIV("僕のサイト: " , a, style="border: 1px solid #000000;")
>>> print div
<div style="border: 1px solid #000000;">
僕のサイト: <a href="http://www.daemonfreaks.com">DaemonFreaks.com</a>
</div>

でもって、タグを全部クラスとして定義するのもアレなので、クラスを動的に生成する関数を作った。

from new import classobj
def _get_class(name, inline, container):
    name = name.lower()
    cls = classobj(name.upper(),
                   {"name": name,
                    "inline": inline,
                    "container": container})
    return cls

で、各タグの名前、inlineとcontainerの設定を用意しておけば準備完了。

# name: (inline, contaier)
TAGS = {
    "html": (False, True),
    "head": (False, True),
    "body": (False, True),
    (省略)
    "div":  (False, True),
    "span": (True, False)
    }

後は、import文が呼び出された際に、「import *」なら定義内容すべてのクラスを、タグが指定されたなら指定タグのクラスをグローバル空間に展開するようにしたい。つまり、モジュール「html」内にこれらのコードが書かれていたとして、

>>> from html import HEAD, BODY
>>> dir()
['HEAD', 'BODY']
>>> from html import *
>>> dir()
['HTML', 'HEAD', 'BODY', (省略), 'DIV', 'SPAN']

のように動作させたい。

しかし、同一モジュール内で__import__をオーバーロードしたりihookを使用してみたものの、どうも期待どおり動作しない。同一モジュール内にオーバーロードの処理を書くと、そのモジュールがimportされた後に__import__がオーバーロードされてしまう(当たり前か…)。なので、モジュール内で自分自身を再度インポートするように記述するとなんとなく上手く動いてるような感じにはなる。

importされるモジュール側でimportをhookして、指定されているクラス(orモジュール)名を取得する方法は無いものか…。

Comments / TrackBacks

TrackBack URL for this entry: https://www.daemonfreaks.com/blog/trackback/200704030100








コメントスパム対策の為、各入力項目へのURL記載は全てリジェクトしてます。ごめんなさい。