【发布时间】:2012-05-18 08:13:26
【问题描述】:
我正在尝试用 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。
【问题讨论】:
-
覆盖分配器并让它检查基数。
-
嗯,这将如何运作?在 Preprocess.__call__ 方法中打印出 cls 对象的 bases 产生(如预期的那样) (
main.Flat'>, main .Orderless'>, main.Operation'>) -
你需要返回一个动态类型的对象。这就是您在分配器中执行此操作的原因。
-
分配器是指“__new__()”方法吗?我之前也这样做过,但问题是,如果我在 new 方法中更改操作数,但返回相同类型的对象,构造函数 (init) 会接收原始未处理的操作数作为其参数...跨度>
-
分配器的全部目的是产生一个全新的类型。
标签: python oop multiple-inheritance metaclass