Reputation: 2217
I am trying to decorate an instance method which prints the full name with another method that simply adds a designation and I've managed to figure out this much.
class DecoratorTest:
def __init__(self):
self._first_name = ['CHARLES', 'WILIAM', 'ENID']
self._last_name = ['DICKENS', 'SHAKESPEARE', 'BLYTON']
self._full_name = None
class designation(object):
def __init__(self, decorated):
self._decorated = decorated
def __call__(self, *args, **kwargs):
self._full_name = "MR" + self._decorated()
return self._full_name
@designation()
def full_name(self):
if not self._full_name:
self._full_name = random.choices(self._first_name) + random.choices(self._last_name)
return self._full_name
a = DecoratorTest()
print(a.full_name())
With some reading, I figured out that the decorator @designation cannot be an instance method since the first argument would be self and not the function that needs to be decorated so any call to it would immediately fail which is why I've specified the decorator designation
as a class hoping that the redefined descriptor __call__()
would help here.
What I'm unable to figure out is how to proceed further. I keep running into various errors and no amount of trial and error is getting me the solution.
EDIT
After some work, I managed to get this to work albeit after ditching the class decorator, the problem now however is that it is an instance method which brings up the issue pointed out in the blog link I mentioned in my comment i.e. in this link https://medium.com/@vadimpushtaev/decorator-inside-python-class-1e74d23107f6
How can I solve this ?
import random
class DecoratorTest:
def __init__(self):
self._first_name = ['CHARLES', 'WILIAM', 'ENID']
self._last_name = ['DICKENS', 'SHAKESPEARE', 'BLYTON']
self._full_name = None
def designation(func):
def wrapper(self):
full_name = "MR" + func(self)
return full_name
return wrapper
@designation
def full_name(self):
if not self._full_name:
self._full_name = str(random.choices(self._first_name)) + str(random.choices(self._last_name))
return self._full_name
a = DecoratorTest()
print(a.full_name())
Upvotes: 0
Views: 170
Reputation: 84
TLDR (go to bottom)
First lets remind ourselves what decorators do.
They are callables/functions which take a function as an input and return a new function (decorated).
So that means that we can decorate a function like this.
def return_hello():
return "hello "
def multiply_by_5(func):
def new_func(*args,**kwargs):
return func(*args, **kwargs) * 5
multiply_by_5(return_hello)()
Or using the @ syntax
@multiply_by_5
def return_hello():
return "hello "
return_hello()
So, in your example, you have decorated your method with
@designation()
Which is emulating a callable via the __call__ method (see docs). This means that the __call__ method is passed the function, (ie the __call__ method is actually the decorator here).
Therefore you can fix the code like this:
...
class designation():
def __call__(self, func):
def new_func(*n_args, **n_kwargs):
return "MR" + func(*n_args,**n_kwargs)
return new_func
@designation()
def full_name(self):
...
EDIT 2: you can also add the property decorator like this and it will work.
@property
@designation()
def full_name(self):
...
EDIT 3: you can acheive all of this with a simple function-based decorator as well. So unless you really need your decorator to be a class then I would recomend this instead.
def designation(func):
def new_func(*args, **kwargs):
name = func(*args, **kwargs)
return "MR" + name
return new_func
Upvotes: 1
Reputation: 983
I played around a little and came across this solution:
class DecoratorTest:
def __init__(self):
self._first_name = ['CHARLES', 'WILIAM', 'ENID']
self._last_name = ['DICKENS', 'SHAKESPEARE', 'BLYTON']
self._full_name = None
class designation(object):
def __init__(self, decorated, *args, **kwargs):
print(str(*args))
print(str(**kwargs))
self._decorated = decorated
def __call__(self, *args, **kwargs):
full_name = "MR" + self._decorated(self)
return full_name
@designation
def full_name(self):
if not DecoratorTest()._full_name:
full_name = ' '.join([random.choices(DecoratorTest()._first_name)[0], random.choices(DecoratorTest()._last_name)[0]])
return full_name
Main problem is that you decorate an outer method with an inner decorator class which leads to the problem, that self
from the outer method may point to the inner one. I used the classname itself to point to the correct values.
This is just a quick one, it may not be 100% accurate.
Regards, Thomas
Upvotes: 1