我在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
,而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
进行挑战,则super(Child, self)
属性具有不相关的值(例如,用于自动验证) ,例如:
self
基本上,为了解决这个问题,我看到了两个解决方案:
阻止在使用{{1}}
{{1}}始终为“正确”类型
这两种解决方案对我来说都是不可能的......有人对如何解决这个问题有所了解吗?一个建议?
答案 0 :(得分: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块中,以确保在异常的情况下重置上下文。
答案 1 :(得分: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,但它的确有效...
答案 2 :(得分:0)
好的,首先,你的"解决方案"真的很难看,但我想你知道的。 :-)所以,让我们试着回答你的问题。
首先是一个隐含的"问题":为什么不使用Python的上下文管理器?它们实际上是免费提供更好的语法和错误管理。请参阅contextlib模块,它可以帮助您。特别是section about reentrancy。
然后,您会发现人们在尝试堆叠上下文管理器时通常会遇到问题。这并不奇怪,因为要正确支持递归,您需要一堆值,而不是单个值。 [你可以看到一些重入cm的源代码,例如redirect_stdout,看看它是如何处理的。]所以你的context_wrapper应该:
(清洁)保留self.context
的列表,在输入上下文时附加到它,并在退出时从中弹出。这样你总能得到你的背景。
(更像是你想要的)保持一个self.context
,但也是一个全局值DEPTH
,在进入时增加1,在退出时增加1,self.context为当DEPTH为0时,重置为无。
至于你的第二个问题,我必须说我不太了解你。 self
是的正确类型。如果A是B的子类,而self是A的实例,那么它也是B的实例。如果self.some_minimum是"错误"你是否认为self是A或B的实例,这意味着some_minimum实际上不是self的实例属性,而是A或B的class属性。对吗? 他们在A和B上可以自由地不同,因为A和B是不同的对象(他们的元类)。