5

我在 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

这就像一个魅力:

class Parent(object):

    __metaclass__ = MyType

    def test(self):
        #Here the context is set:
        print self.context #prints blabla

但是,一旦我想子类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

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

确实,(等等,这很难解释),父方法被调用,包装器被执行,但它们接收*args**kwargs寻址到Parent.test, whileself是一个Child实例,所以self如果我想用*argsand **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

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

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

  2. 有一个self总是“正确”类型的

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

4

3 回答 3

1

好吧,你不能只检查上下文是否已经设置_manage_context吗?像这样:

def _manage_context(self, *args, **kwargs):
    #Sets the context to 'blabla' before the execution
    if self.context is None:
        self.context = 'blabla'
        returned = operation(self, *args, **kwargs)
        #Cleans the context after execution
        self.context = None
        return returned
    else:
        return operation(self, *args, **kwargs)

此外,这可能应该包含在 try-catch 块中,以确保在发生异常时重置上下文。

于 2011-03-07T12:43:54.900 回答
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

这样,在调用时:

super(Child, self).a_wrapped_method

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

于 2011-03-07T15:15:30.473 回答
0

好的,首先,您的“解决方案”真的很难看,但我想您知道这一点。:-) 所以让我们试着回答你的问题。

首先是一个隐含的“问题”:为什么不使用 Python 的上下文管理器?它们几乎免费为您提供更好的语法和错误管理。查看 contextlib 模块,它可以帮助你很大。尤其是关于 reentrancy 的部分

然后你会看到人们在尝试堆栈上下文管理器时通常会遇到问题。这并不奇怪,因为要正确支持递归,您需要一堆值,而不是单个值。[您可以查看某些可重入 cm 的来源,例如 redirect_stdout,以了解它是如何处理的。] 所以您的 context_wrapper 应该:

  • (cleaner) 保留一个self.contexts 列表,在进入上下文时附加到它,并在退出时从中弹出。这样你总能得到你的上下文。

  • (更像你想要的)保持一个单一的self.context,但也是一个全局值DEPTH,进入时增加一,退出时减少一,当 DEPTH 为 0 时 self.context 重置为 None 。

As for your second question, I must say I don't quite understand you. self is of the right type. If A is subclass of B, and self is instance of A, then it is also instance of B. If self.some_minimum is "wrong" whether you consider self an instance of A or of B, that means that some_minimum is not really an instance attribute of self, but a class attribute of A or B. Right? They can be freely different on A and on B, because A and B are different objects (of their metaclass).

于 2015-07-18T19:41:57.347 回答