Reputation: 1552
I have a child class method I want to conditionally short circuit. What I'm trying to do is something like this except I want to put the validation logic into the base class.
class BaseClass(object):
def getvalue(self):
return True
def validate(self):
validated = self.getvalue()
return validated
class ExtendedClass1(BaseClass):
def do_some_work(self):
validated = self.validate()
if not validated:
print "Not validated."
return
print "Things are validated if the method got this far.", validated
class ExtendedClass2(BaseClass):
def do_some_work(self):
validated = self.validate()
if not validated:
print "Not validated."
return
print "Things are validated if the method got this far.", validated
class ExtendedClass3(BaseClass):
def do_some_work(self):
print "This one doesn't require validation."
work1 = ExtendedClass1()
work1.do_some_work()
work2 = ExtendedClass2()
work2.do_some_work()
work3 = ExtendedClass3()
work3.do_some_work()
Following this example I was able to convert some of the repeated code into a decorator pattern.
class BaseClass(object):
def validate(input_function):
def wrapper(*args,**kwargs):
validated = True
if not validated:
print "Not validated."
return
input_function(*args, **kwargs)
return wrapper
validate = staticmethod(validate)
class ExtendedClass1(BaseClass):
@BaseClass.validate
def do_some_work(self):
print "Things are validated if the method got this far."
class ExtendedClass2(BaseClass):
@BaseClass.validate
def do_some_work(self):
print "Things are validated if the method got this far."
class ExtendedClass3(BaseClass):
def do_some_work(self):
print "This one doesn't require validation."
work1 = ExtendedClass1()
work1.do_some_work()
work2 = ExtendedClass2()
work2.do_some_work()
work3 = ExtendedClass3()
work3.do_some_work()
However, I need to call a method of the base class in the decorator to do the validation work, and retrieve the value of (validated) in the child class. Following this example here, I modified the decorator in an attempt to let it call self.getvalue(). At this point it doesn't error out, but it doesn't work either because self.getvalue() doesn't return True. This is starting to seem like more trouble than it's worth, but now I'm curious as to whether or not it's possible.
class BaseClass(object):
def getvalue(self):
return True
def validate(self):
def wrap(input_function):
def wrapper(*args,**kwargs):
validated = self.getvalue()
if not validated:
print "Not validated."
return
input_function(*args, **kwargs)
return wrapper
return wrap
validate = staticmethod(validate)
class ExtendedClass1(BaseClass):
@BaseClass.validate
def do_some_work(self):
print "Things are validated if the method got this far."#, validated
class ExtendedClass2(BaseClass):
@BaseClass.validate
def do_some_work(self):
print "Things are validated if the method got this far."#, validated
class ExtendedClass3(BaseClass):
def do_some_work(self):
print "This one doesn't require validation."
work1 = ExtendedClass1()
work1.do_some_work()
work2 = ExtendedClass2()
work2.do_some_work()
work3 = ExtendedClass3()
work3.do_some_work()
Is it possible to set an attribute with the decorator and then retrieve it later?
...
self.validated = True
if not self.validated:
print "Not validated."
return
...
print work1.validated
...
AttributeError: 'ExtendedClass1' object has no attribute 'validated'
Essentially, I want to turn this:
class ExtendedClass1(BaseClass):
def do_some_work(self):
validated = self.validate()
if not validated:
print "Not validated."
return
print "Things are validated if the method got this far.", validated
Into this:
class ExtendedClass1(BaseClass):
@BaseClass.validate
def do_some_work(self):
print "Things are validated if the method got this far.", validated
Using the suggestion posted by Zaur Nasibov, this example satisfies my use case. I'm still interested to know if @validate can be implemented as a method instead of a stand alone function, but this gets the job done.
class BaseClass(object):
def getvalue(self):
return True
def validate(func):
def wrapped(self, *args, **kwargs):
validated = self.getvalue()
self.validated = validated
if not validated:
print "Not validated."
return
func(self, *args, **kwargs)
return wrapped
class ExtendedClass1(BaseClass):
@validate
def do_some_work(self,input):
print "Things are validated if the method got this far.", self.validated, input
class ExtendedClass2(BaseClass):
@validate
def do_some_work(self):
print "Things are validated if the method got this far.", self.validated
class ExtendedClass3(BaseClass):
def do_some_work(self):
print "This one doesn't require validation."#, self.validated
work1 = ExtendedClass1()
work1.do_some_work(input="some text")
work2 = ExtendedClass2()
work2.do_some_work()
work3 = ExtendedClass3()
work3.do_some_work()
Upvotes: 0
Views: 98
Reputation: 1121844
How about only calling the decorated method if it has been validated? You can pass the return value of the validate
method along if you like:
class BaseClass(object):
def getvalue(self):
return True
def validate(input_function):
def wrapper(self, *args, **kwargs):
self.validated = self.getvalue()
if not self.validated:
print "Not validated."
return
input_function(self, validated=self.validated, *args, **kwargs)
return wrapper
validate = staticmethod(validate)
class ExtendedClass1(BaseClass):
@BaseClass.validate
def do_some_work(self, validated=None):
print "Things are validated if the method got this far.", validated
class ExtendedClass2(BaseClass):
@BaseClass.validate
def do_some_work(self, validated=None):
print "Things are validated if the method got this far.", validated
class ExtendedClass3(BaseClass):
def do_some_work(self):
print "This one doesn't require validation."
work1 = ExtendedClass1()
work1.do_some_work()
work2 = ExtendedClass2()
work2.do_some_work()
work3 = ExtendedClass3()
work3.do_some_work()
The key here is adding self
to the wrapper
function. What happens is that your decorated functions do not get bound to the instance (and become methods), but the function returned by the decorator (wrapper
in the above example) get's bound instead. So this function will get the self
(the instance) parameter passed in when called! It's important to remember that what a @decorator
does is simply call decorator
passing in the function you are decorating, and then replace the function you are decorating with whatever the decorator returned. In your example this is wrapper
, and to the class, there is no difference between that and the original function before decorating.
In the above example, I declared self
explicitly. If we hadn't, we could also have just taken it from args
:
def validate(input_function):
def wrapper(*args, **kwargs):
print "args[0] is now the instance (conventionally called 'self')", args[0]
self = args[0]
self.validated = self.getvalue()
if not self.validated:
print "Not validated."
return
input_function(validated=self.validated, *args, **kwargs)
Also note that we pass an extra keyword argument to the wrapped method, named validated
. This is entirely optional, you can just drop the validated=self.validated
and validated=None
parts from the example.
Upvotes: 1
Reputation: 22659
@tponthieux, what you can do is setting the attribute of the called function (method) and then retrieving it:
Simple example (updated):
def validate(func):
def wrapped(self, *args, **kwargs):
self.valid = True
func(self, *args, **kwargs)
return wrapped
class TestClass(object):
@validate
def do_some_work(self):
print "some work done"
tc = TestClass()
tc.do_some_work()
print tc.valid
Upvotes: 1