4

Python で単純化された用語書き換えシステム (TRS)/記号代数システムを実装する方法を試しています。このため、クラス インスタンスのインスタンス化プロセス中に特定のケースでオペランドをインターセプトして変更できるようにしたいと考えています。私が思いついた解決策は、(タイプ「type」の) クラス オブジェクトの典型的な呼び出し動作を変更するメタクラスを作成することでした。

class Preprocess(type):
    """
    Operation argument preprocessing Metaclass.
    Classes using this Metaclass must implement the 
        _preprocess_(*operands, **kwargs)
    classmethod.
    """

    def __call__(cls, *operands, **kwargs):
        pops, pargs = cls._preprocess_(*operands, **kwargs)
        return super(Preprocess, cls).__call__(*pops, **pargs)

例のケースは、ネストされた操作 F(F(a,b),c)-->F(a,b,c) を展開することです

class Flat(object):
    """
    Use for associative Operations to expand nested 
    expressions of same Head: F(F(x,y),z) => F(x,y,z)
    """
    __metaclass__ = Preprocess
    @classmethod
    def _preprocess_(cls, *operands, **kwargs):
        head = []
        for o in operands:
            if isinstance(o, cls):
                head += list(o.operands)
            else:
                head.append(o)
        return tuple(head), kwargs

したがって、この動作は継承によって実現できます。

class Operation(object):
    def __init__(self, *operands):
        self.operands = operands

class F(Flat, Operation):
    pass

これにより、望ましい動作が得られます。

print F(F(1,2,3),4,5).operands
(1,2,3,4,5)

ただし、そのような前処理クラスをいくつか組み合わせて、自然クラス mro に従ってオペランドを順次処理させたいと考えています。

class Orderless(object):
    """
    Use for commutative Operations to bring into ordered, equivalent 
    form: F(*operands) => F(*sorted(operands))
    """
    __metaclass__ = Preprocess

    @classmethod
    def _preprocess_(cls, *operands, **kwargs):

        return sorted(operands), kwargs

そして、これは思い通りにはいかないようです。フラットで順序のない操作タイプの定義

class G(Flat, Orderless, Expression):
    pass

最初の Preprocessing スーパークラスのみが「アクティブ」になります。

print G(G(3,2,1),-1,-3).operands
(3,2,1,-1,-3)

クラスのインスタンス化の前に、すべての Preprocessing クラスのpreprocessメソッドが確実に呼び出されるようにするにはどうすればよいですか?

アップデート:

新しいstackoverflowユーザーとしてのステータスのため、まだ正式に質問に答えることができないようです。したがって、これがおそらく私が思いつくことができる最善の解決策だと思います。

class Preprocess(type):
    """
    Abstract operation argument preprocessing class.
    Subclasses must implement the 
        _preprocess_(*operands, **kwargs)
    classmethod.
    """

    def __call__(cls, *operands, **kwargs):
        for cc in cls.__mro__:
            if hasattr(cc, "_preprocess_"):
                operands, kwargs = cc._preprocess_(*operands, **kwargs)

        return super(Preprocess, cls).__call__(*operands, **kwargs)

問題は、super(Preprocess, cls).__call__(*operands, **kwargs)期待どおりに cls の mro をトラバースしないことだと思います。

4

1 に答える 1

1

あなたはこれについて間違った方向に進んでいると思います。メタクラスを削除し、代わりにクラスとメソッドのデコレーターを使用してください。

たとえば、フラットを次のように定義します。

@init_args_preprocessor
def flat(operands, kwargs): # No need for asterisks
    head = []
    for o in operands:
        if isinstance(o, cls):
            head += list(o.operands)
        else:
            head.append(o)
    return tuple(head), kwargs

init_args_preprocessor デコレータを使用して、その関数をクラス デコレータに変換します。

def init_args_preprocessor(preprocessor):
    def class_decorator(cls):
        orig_init = cls.__init__
        def new_init(self, *args, **kwargs):
            args, kwargs = preprocessor(args, kwargs)
            orig_init(self, *args, **kwargs)
        cls.__init__ = new_init
        return cls
   return class_decorator

そして今、ミックスインの代わりにデコレータを使用します:

class Operation(object):
    def __init__(self, *operands):
        self.operands = operands

@flat
class F(Operation):
    pass

そして、クラス修飾子をきれいに組み合わせることに問題はないはずです。

@init_args_preprocessor
def orderless(args, kwargs):
    return sorted(args), kwargs

@orderless
@flat
class G(Expression):
   pass

警告 Emptor: 上記のすべてのコードは厳密にテストされていません。

于 2012-05-15T23:00:30.800 に答える