Python:在类实例初始化之前修改传递的参数

Posted

技术标签:

【中文标题】Python:在类实例初始化之前修改传递的参数【英文标题】:Python: Modifying passed arguments before class instance initialization 【发布时间】: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 方法?

更新:

由于我是新的 *** 用户,我似乎还不能正式回答我的问题。 所以,我相信这可能是我能想到的最好的解决方案:

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) 会接收原始未处理的操作数作为其参数...跨度> 分配器的全部目的是产生一个全新的类型。 【参考方案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

现在,使用装饰器代替 mixins:

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

Caveat Emptor:以上所有代码均未经测试。

【讨论】:

嗨,是的,这看起来是一个非常好的解决方案,事实证明我现在实际上正在按照这些思路做一些事情。这样做的一个问题是对象“G”将不再是一个类而是一个函数,因此 isinstance() 检查将失败而无需进一步调整。 为什么G 是一个函数?它仅由类装饰器修改。 @init_args_preprocessorflat 转换为一个接受一个类并返回一个类的函数。 我的错,你是对的。事实上,这正是我最终这样做的方式。

以上是关于Python:在类实例初始化之前修改传递的参数的主要内容,如果未能解决你的问题,请参考以下文章

局部变量,实例变量,静态变量

我应该将依赖注入容器(DIC)作为 MVC 中的参数传递吗?

Python Day23

python知识点1

python实例化类,数据混乱串内容问题(可变类型属性的初始化)

使用参数初始化 UIView