Python:元类+包装方法+继承=问题

seb*_*piq 5 python inheritance metaclass

我在Python中遇到问题,我找不到任何干净的解决方案......

在调用某些方法时,我想在方法执行之前和之后执行一些代码.按顺序(在许多其他事项中)自动设置和清理context变量.

为了实现这一点,我宣布了以下元类:

class MyType(type):
    def __new__(cls, name, bases, attrs):
        #wraps the 'test' method to automate context management and other stuff
        attrs['test'] = cls.other_wrapper(attrs['test'])
        attrs['test'] = cls.context_wrapper(attrs['test'])
        return super(MyType, cls).__new__(cls, name, bases, attrs)

    @classmethod
    def context_wrapper(cls, operation):
        def _manage_context(self, *args, **kwargs):
            #Sets the context to 'blabla' before the execution
            self.context = 'blabla'
            returned = operation(self, *args, **kwargs)
            #Cleans the context after execution
            self.context = None
            return returned
        return _manage_context

    @classmethod
    def other_wrapper(cls, operation):
        def _wrapped(self, *args, **kwargs):
            #DO something with self and *args and **kwargs
            return operation(self, *args, **kwargs)
        return _wrapped
Run Code Online (Sandbox Code Playgroud)

这就像一个魅力:

class Parent(object):

    __metaclass__ = MyType

    def test(self):
        #Here the context is set:
        print self.context #prints blabla
Run Code Online (Sandbox Code Playgroud)

但是一旦我想要子类化Parent,当我用super以下方法调用父方法时出现问题:

class Child(Parent):
    def test(self):
        #Here the context is set too
        print self.context #prints blabla
        super(Child, self).test()
        #But now the context is unset, because Parent.test is also wrapped by _manage_context
        #so this prints 'None', which is not what was expected
        print self.context
Run Code Online (Sandbox Code Playgroud)

我曾想过在将其设置为新值之前保存上下文,但这只能解决部分问题...

实际上,(坚持,这很难解释),调用父方法,执行包装器,但是它们接收*args**kwargs寻址Parent.test,同时self是一个Child实例,因此self如果我想用*args和挑战它们,属性具有无关的值**kwargs(例如,用于自动验证目的),例如:

@classmethod
def validation_wrapper(cls, operation):
    def _wrapped(self, *args, **kwargs):
        #Validate the value of a kwarg
        #But if this is executed because we called super(Child, self).test(...
        #`self.some_minimum` will be `Child.some_minimum`, which is irrelevant
        #considering that we called `Parent.test`
        if not kwarg['some_arg'] > self.some_minimum:
            raise ValueError('Validation failed')
        return operation(self, *args, **kwargs)
    return _wrapped
Run Code Online (Sandbox Code Playgroud)

基本上,为了解决这个问题,我看到两个解决方案:

  1. 阻止在调用方法时执行包装器 super(Child, self)

  2. 拥有一个self总是"正确"的类型

这两种解决方案对我来说都是不可能的......有人对如何解决这个问题有所了解吗?一条建议 ?

seb*_*piq 0

实际上,我找到了一种方法来防止在调用方法时执行包装器super(Child, self)

class MyType(type):
    def __new__(cls, name, bases, attrs):
        #wraps the 'test' method to automate context management and other stuff
        new_class = super(MyType, cls).__new__(cls, name, bases, attrs)
        new_class.test = cls.other_wrapper(new_class.test, new_class)

    @classmethod
    def other_wrapper(cls, operation, new_class):
        def _wrapped(self, *args, **kwargs):
            #DO something with self and *args and **kwargs ...
            #ONLY if self is of type *new_class* !!!
            if type(self) == new_class:
                pass #do things
            return operation(self, *args, **kwargs)
        return _wrapped
Run Code Online (Sandbox Code Playgroud)

这样,当调用时:

super(Child, self).a_wrapped_method
Run Code Online (Sandbox Code Playgroud)

包装代码被绕过!这很黑客,但它有效......