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 をトラバースしないことだと思います。