I am trying to call a decorator from another class in python. Below is the code
file_1.py
class ABC:
def decorate_me(func):
def wrapper():
print "Hello I am in decorate_me func"
print "Calling decorator function"
func()
print "After decorator"
return wrapper
file_2.py
from file_1 import ABC
#ABC.decorate_me
def test():
print "In test function ."
test()
output
TypeError: unbound method decorate_me() must be called with ABC instance as first argument (got function instance instead)
As hinted by the error, your decorator is a method; try making it a static function:
class ABC:
#staticmethod
def decorate_me(func):
...
But the question is why do you put it in ABC?
Try the below code in file_2.py:
from file_1 import ABC
dec = ABC.decorate_me
#dec
def test():
print("In test function .")
test()
Output:
Hello I am in decorate_me func
Calling decorator function
In test function .
After decorator
Since you decorator isn't using self it looks like the wrapper may be a staticmethod. If you declare decorate_me as such you can use it with #ABC.deocarate_me.
If you want to use this decorator in other classes, consider having the class with the decorator as a base class that your other classes inherit from. Another option is to not put your decorator in a class at all.
Related
I have to decorate a inherited method, but it decorates all inherited methods. Basically I have to create a decorator that will decorate just one method from the class.
The test looks like this
#my_decorator
class TestClass(Subclass):
pass
t = TestClass()
t.say_hi
Let's say my SubClass looks like this
class SubClass():
def __init__(self):
pass
def say_hi():
print("Hi")
def say_wow():
print("wow")
Now I have to make my_decorator, that has to decorate inherited function say_hi() to print("*****") before it prints "Hi"
I tried doing it like this, but than the decorator applies to all methods from SubClass
def my_decorator(cls)
def say_hi():
print("*********")
cls.say_hi()
return say_hi()
Naturally It applies to every function of the subclass, but how do I make it to apply to only a say_hi() function? -It also returns an TypeError "NoneType" object is not callable
First let us fix SubClass, because instance methods require an explicit instance parameter at definition time:
class SubClass():
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_wow(self):
print("wow")
Now you want the decorator to replace the say_hi method with a method that prints '****' before calling the original method. Le us write a decorator that just does that(*):
def my_decorator(cls):
orig = cls.say_hi # save the original method
def say_hi(self): # define a new one
print('****')
return orig(self) # ... calling the original method
cls.say_hi = say_hi # replace the method in the class
return cls
You can then use:
#my_decorator
class TestClass(SubClass):
pass
t = TestClass()
t.say_hi()
and get as expected:
****
Hi
(*) this is a very simple decorator that can only replace a say_hi(self) method: neither a different name, nor additional parameters, but decorators can be much smarter...
If you want to decorate a method, then decorate the method, not the class that contains it. If you want a new class, then the decorator applied to the class needs to return a class.
def print_banner(f):
def _(*args, **kwargs):
print("****")
f(*args, **kwargs)
return _
class SubClass():
def __init__(self):
pass
#print_banner
def say_hi(self, ):
print("Hi")
def say_wow(self):
print("wow")
For better encapsulation, I want to decorate instance methods with methods inside the same class.
class SomeClass(object):
#staticmethod
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
x = SomeClass()
x.do()
However, this piece of code raises TypeError: 'staticmethod' object is not callable
Now I make a workaround by defining a class and overload its new method to simulate a function, but it's eventually a class, not a function.
So can I access my functions inside the class scope?
Just get rid of that #staticmethod line. You want some_decorator to behave like a plain function, not like some kind of method.
The decorator is called when the class definition is being executed, before the class object itself exists. The normal method definitions inside a class are actually just plain old functions, they become methods dynamically each time they are called as attributes of the class instance (which turns them into bound methods). But while the class object itself is being built you can treat them as plain functions.
class SomeClass(object):
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
x = SomeClass()
x.do()
output
hello
world
BTW, you have an error in your decorator: it's returning wrapped() instead of wrapped.
As chepner mentions in the comments we can delete some_decorator so that it doesn't take up space in the class object after we've finished using it in the class definition. (If we accidentally try to call it we'll get an error). We could do del SomeClass.some_decorator after the class definition, but it's also perfectly valid to put a del statement inside the class definition:
class SomeClass(object):
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
del some_decorator
Let's say I have this:
class Foo:
...
def func():
return 1+2
class Bar(Foo):
...
def another_func():
# additional stuff I want to do when my parent's func() is called
I don't want to override func , but I do want to add some additional statements when it's called. Also, I don't want to change the original Foo.func.
Is it even possible? If not, any idea for a workaround?
There is no way of doing that, the canonical solution would be to overide func und wrap the original function like so:
class Bar(Foo):
...
def func():
# additional stuff I want to do when my parent's func() is called
res = super(Bar, self).func() # super().func() on Py3
# additional stuff I want to do after my parent's func() is called
return res
You need to override the func function and make the call to parent's func from within it. Python has super() for that purpose:
super(type[, object-or-type])
Return a proxy object that delegates method calls to a parent or sibling class of type.
This is useful for accessing inherited methods that have been overridden in a class.
The search order is same as that used by getattr() except that the type itself is skipped.
Example:
class Foo(object):
def func(self):
print "In parent"
class Bar(Foo):
def func(self):
super(Bar, self).func()
print 'In child' # Your additonal stuff
When you will run the func of Bar as:
b = Bar()
b.func()
will print:
In parent # <-- from Foo.func()
In child # <-- from Bar.func()
I want a decorator that would add the decorated function to list, like this :
class My_Class(object):
def __init__(self):
self.list=[]
#decorator
def my_function(self)
print 'Hi'
I expect my_function to be added to self.list, but I just can't write this decorator. If I try to write it inside My_Class, then I would have to use #self.decorator, and self does not exist since we're outside any function. And if I try to write it out of My_Class, then I can't retrieve self from my_function.
I know quite similar questions exist, but they are overly complicated, and I'm just learning python and decorators.
You can't access self from the decorator, because the decorator is run at the time the function is defined, and at that time there are no instances of My_Class yet.
It's better to put the function list as a class attribute instead of an instance attribute. Then you can pass this list as a parameter to the decorator:
def addToList(funcList):
'''Decorator that adds the function to a given list'''
def actual_decorator(f):
funcList.append(f)
return f
return actual_decorator
class MyClass(object):
funcList = []
#addToList(funcList)
def some_function(self, name):
print 'Hello,', name
Now you can access MyClass.funcList to get the list of decorated functions.
There's nothing really special about writing decorators for bound functions (instance methods). For example, this simple example works fine:
def decorator(fn):
print "I'm decorating!"
return fn
class MyClass(object):
def __init__(self):
self.list = []
#decorator
def my_function(self):
print "Hi"
If you want to use self in your decorator, you'll treat your decorator the same as you'd treat any decorator that uses the function's args:
def decorator(fn):
def _decorator(self):
print "I'm decorating, and here's my list: %s!" % self.list
return fn(self)
return _decorator
Your list attribute should be a class attribute (and it should be renamed, because list is a builtin type). Then you can do something like this:
my_methods = []
def my_method(method):
my_methods.append(method)
return method
class MyClass(object):
my_methods = my_methods
#my_method
def my_function(self):
print 'Hi'
Referring to the first answer about python's bound and unbound methods here, I have a question:
class Test:
def method_one(self):
print "Called method_one"
#staticmethod
def method_two():
print "Called method_two"
#staticmethod
def method_three():
Test.method_two()
class T2(Test):
#staticmethod
def method_two():
print "T2"
a_test = Test()
a_test.method_one()
a_test.method_two()
a_test.method_three()
b_test = T2()
b_test.method_three()
produces output:
Called method_one
Called method_two
Called method_two
Called method_two
Is there a way to override a static method in python?
I expected b_test.method_three() to print "T2", but it doesn't (prints "Called method_two" instead).
In the form that you are using there, you are explicitly specifying what class's static method_two to call. If method_three was a classmethod, and you called cls.method_two, you would get the results that you wanted:
class Test:
def method_one(self):
print "Called method_one"
#staticmethod
def method_two():
print "Called method_two"
#classmethod
def method_three(cls):
cls.method_two()
class T2(Test):
#staticmethod
def method_two():
print "T2"
a_test = Test()
a_test.method_one() # -> Called method_one
a_test.method_two() # -> Called method_two
a_test.method_three() # -> Called method_two
b_test = T2()
b_test.method_three() # -> T2
Test.method_two() # -> Called method_two
T2.method_three() # -> T2
The behavior you see is the expected behavior. Static methods are... static. When you call method_three() defined in Test it will certainly call method_two() defined by Test.
As for how to "get around" this proper behavior...
The very best way is to make methods virtual when you want virtual behavior. If you're stuck with some library code with a static method that you wish were virtual then you might look deeper to see if there's a reason or if it's just an oversight.
Otherwise, you can define a new method_three() in T2 that calls T2.method_two().
Additionally, if you want to call the "virtual static" function without an instance, you could proceed like so:
Declare the function in the base class non-static like so:
class Base:
def my_fun(self):
print('my_fun base')
class Derived(Base):
def my_fun(self):
print('my_fun derived')
Call it by passing the class type, which is not an instance, like so:
Derived.my_fun(Derived)
Note, this is useful if you have a variable "class_type", which is only known during run time.