I have the below code which works fine if I remove self from methods
class tests:
def __init__(self):
pass
def func(self,a):
def wrapp(x):
y=x+2
return a(y)
return wrapp
#func
def func1(self,b):
return b
print (tests.func1(10))
I believe decorator function are functions that return another function. Will that not work inside class? Ignore the indentation error as I am not achievable when I paste the code here..
Please help me how I can achieve this scenario inside class..
You can just declare your decorator outside of the class. Also, when you are decorating a class method, it seems you need to pass the self variable from the wrapper to the decorated function (changed the names for more clarity):
def add_two(fn):
def wrapper(self, x):
y = x + 2
return fn(self, y)
return wrapper
class Test:
#add_two
def func1(self, b):
return b
f = Test()
f.func1(5) # returns 7
This issue here isn't the decorator at all. This issue is you are using func1 and your decorator as static methods without removing the self argument. If you remove the self arguments this will work fine.
Without staticmethod decorator
class Test:
def add_two(func=None):
def wrapper_add_two(*args, **kwargs):
return func(*args, **kwargs) + 2
return wrapper_add_two
#add_two
def func1(b):
return b
print(Test.func1(10)) #12
With staticmethod decorator
Unfortunately using them in this manner stores them as unbound static methods and you need to bind them for this to work properly.
class Test:
#staticmethod
def add_two(func):
def wrapper_add_two(*args, **kwargs):
return func.__func__(*args, **kwargs) + 2
return wrapper_add_two
#add_two.__func__
#staticmethod
def func1(b):
return b
print(Test.func1(10)) #12
Running with the staticmethod decorator and without the function binding gives you
TypeError: 'staticmethod' object is not callable
Related
Is it possible to write a decorator that acts upon a class's method and uses the class's attributes? For example, I would like to add a decorator to functions that will return an error if one of the class's attributes (which is set when the user calls the function) is False.
For example, my attempt (broken code since is_active can't access MyClass's methods):
def is_active(active):
if active == False:
raise Exception("ERROR: Class is inactive")
class MyClass():
def __init__(self, active):
self.active = active
#is_active
def foo(self, variable):
print("foo")
return variable
#is_active
def bar(self, variable):
print("bar")
return variable
where the expected behaviour is:
cls = MyClass(active=True)
cls.foo(42)
---> function prints "foo" and returns 42
cls = MyClass(active=False)
cls.foo(42)
---> function raises an exception as the active flag is False
The above is a dummy example and the actual use case is more complex, but hopefully this shows the problem I'm facing.
If the above is possible, my extra question is: is it possible to "hide"/delete the methods from the instantiated class based on this flag. For example, if the user instantiates the class with a active=False then when they're using iPython and press <tab>, they can only see the methods which are permitted to be used?
Thank you.
Decorators can be confusing. Note a function is passed as a parameter and the decorator expects that a function (or callable object) is returned. So you just need to return a different function. You have everything else you need since self is passed as the first argument to a class method. You just need to add a new function in your decorator that does what you want.
def is_active_method(func):
def new_func(*args, **kwargs):
self_arg = args[0] # First argument is the self
if not self_arg.active:
raise Exception("ERROR: Class is inactive")
return func(*args, **kwargs)
return new_func
class MyClass():
def __init__(self, active):
self.active = active
#is_active_method
def foo(self, variable):
print("foo")
return variable
#is_active_method
def bar(self, variable):
print("bar")
return variable
m = MyClass(True) # Prints foo from the method
m.foo(2)
m = MyClass(False) # Outputs the exception
m.foo(2)
Wrapping a class's method in a "boilerplate" Python decorator will treat that method as a regular function and make it lose its __self__ attribute that refers to the class instance object. Can this be avoided?
Take the following class:
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
def meth(self):
pass
If meth() is undecorated, MyClass().meth.__self__ refers to an instance method and enables something like setattr(my_class_object.meth.__self__, 'a', 5).
But when wrapping anything in a decorator, only the function object is passed; the object to which it is actually bound is not passed on along with it. (See this answer.)
import functools
def decorate(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
# Do something to method.__self__ such as setattr
print(hasattr(method, '__self__'))
result = method(*args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
#decorate
def meth(self):
pass
MyClass().meth()
# False <--------
Can this be overriden?
Your main misunderstanding here is order of operation.
When the decorate() decorator is called, meth() is not a method yet - it is still a function - it is only when the class block is over that meth is transformed into a method by the metaclass descriptors! - that's why it doesn't have __self__ (yet).
In other words, to decorate methods you have to ignore the fact that they are methods and treat them as normal functions - because that's what they are when the decorator is called.
In fact, the original meth function will never turn into a method - instead the function wrapper you returned from the decorator will be part of the class and will be the one that will get the __self__ attribute later.
If you decorate method of the class, first argument is always self object (you can access it with args[0]):
import functools
def decorate(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
print(hasattr(args[0], 'a'))
result = method(*args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
#decorate
def meth(self):
pass
MyClass().meth()
Prints:
True
Edit:
You can specify also self in your wrapper function (based on comments):
import functools
def decorate(method):
#functools.wraps(method)
def wrapper(self, *args, **kwargs):
print(hasattr(self, 'a'))
result = method(self, *args, **kwargs)
return result
return wrapper
class MyClass(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
#decorate
def meth(self):
pass
MyClass().meth()
Prints also:
True
Let me clarify the process of decorating:
When you decorate meth with decorate in class MyClass, you are doing:
class MyClass(object):
... omit
meth = decorate(meth) # the meth in "()" is the original function.
As you can see, decorate takes method which is a function as parameter and return wrapper which is another funtion. And now the original meth in MyClass is replaced by new one wrapper. So when you call myclass_instance.meth(), you are calling the new wrapper function.
There isn't any black magic, so self can be definitely passed into wrapper, and it is safe to accept self using wrapper(self, *args, **kwargs).
I know there is similar question, but my scenario is somehow different: refer to codes:
class MyClass(object):
def __init__(self, log_location)
self.logs = logging(log_location) # create log object by the log_location, this object should be used by the decorator fucntion
def record_log(log_object):
""" this is the decorator function
"""
def deco(func):
def wrap(*args, **kwargs):
rs = func()
# use log object to record log
if rs:
log_object.record('success')
else:
log_object.record('fail')
return wrap
return deco
#record_log(self.logs)
def test(self):
rs = do_some_thing
if rs:
return True
return False
def main():
my_class = MyClass()
my_class.test()
But, there is an error like this:
#record_log(self.logs)
NameError: name 'self' is not defined
Hos should I use the instance attribute self.logs in a decorator function in such scenario like this??
Thanks very much!
You can not pass a reference to self or any attribute of self at this point. The #record_log line is executed (the method is decorated) before the code in main is executed, i.e. before any instance of MyClass is created at all -- in fact, even before the definition of MyClass has been completed! But remember that
#record_log(self.logs)
def test(self, n):
is actually just syntactic sugar for
test = record_log(self.logs)(test)
So one way to work around your problem would be to redefine test in your __init__, i.e.
def __init__(self, log_location)
self.logs = logging(log_location)
self.test = record_log(self.logs)(self.test)
Also note that your decorator is not passing any parameters to func and not returning the results. Also, it should probably be defined on module level (before the class).
def record_log(log_object):
def deco(func):
def wrap(*args, **kwargs):
rs = func(*args, **kwargs) # pass parameters
if rs:
log_object.record('success')
else:
log_object.record('fail')
return rs # return result
return wrap
return deco
There are several objections about your code:
deco() is redundant. You can directly return wrap from record_log().
If you only plan to decorate MyClass's methods, then there is no point in passing log_object to the decorator, as self.logs will always be used. Otherwise, consider moving the decorator to module level, as already suggested by others.
The decorated method's return value is currently lost.
The call to the decorated function does not pass self to it.
The proper code would therefore be:
class MyClass(object):
def __init__(self, log_location):
self.logs = logging(log_location)
def record_log(func):
""" this is the decorator function
"""
def wrap(self):
rs = func(self)
# use log object to record log
if rs:
print 1
self.logs.record('success')
else:
print 2
self.logs.record('fail')
return rs
return wrap
#record_log
def test(self):
rs = do_some_thing
if rs:
return True
return False
Okay, so I've got a class where one of the attributes is a callback function. Problem is, whenever I call it from within the class (e.g. as self.function_attr(), it gets passed self as the first argument. Here's an idea of what I'm working with:
def callback(a, b):
# do something with a, b
class A:
def __init__(self, callback):
self.callback = callback
self.callback(1, 2) # Raises a TypeError: takes exactly 2 arguments (3 given)
I'm not willing to write each callback function to take self as a first argument. I wrote a decorator that works around the issue:
def callback_decorator(func):
def newfunc(self, *args, **kw):
return func(*args, **kw)
return newfunc
but I'm wondering if there's anything better.
Basically, my question is, how can I call instance attributes of my class which are functions without them being passed self as the first argument?
You just need to make it a staticmethod when you bind it to the class.
def callback(a, b):
# do something with a, b
class A:
def __init__(self, callback):
# now it won't get passed self
self.callback = staticmethod(callback)
self.callback(1, 2)
or
class A:
def __init__(self, callback):
self.callback(1, 2)
# now it won't get passed self
callback = staticmethod(callback)
As far as I know, a wrapper (like your decorator) is the simplest way to go. Since you already have an object in which to store the function, I wouldn't bother with a decorator. (Note I've inherited from object, which is something you should probably be doing unless you specifically want old-style class behaviour.)
class A(object):
def __init__(self, callback):
self._callback = callback
self.callback(1,2)
def callback(self, *args, **kwargs):
return self._callback(*args, **kwargs)
This behaves as you'd expect:
>>> def f(x, y):
... print "X: %s, Y: %s" % (x,y)
...
>>> mya = A(f)
X: 1, Y: 2
I'm trying to write a python class which uses a decorator function that needs information of the instance state. This is working as intended, but if I explicitly make the decorator a staticmetod, I get the following error:
Traceback (most recent call last):
File "tford.py", line 1, in <module>
class TFord(object):
File "tford.py", line 14, in TFord
#ensure_black
TypeError: 'staticmethod' object is not callable
Why?
Here is the code:
class TFord(object):
def __init__(self, color):
self.color = color
#staticmethod
def ensure_black(func):
def _aux(self, *args, **kwargs):
if self.color == 'black':
return func(*args, **kwargs)
else:
return None
return _aux
#ensure_black
def get():
return 'Here is your shiny new T-Ford'
if __name__ == '__main__':
ford_red = TFord('red')
ford_black = TFord('black')
print ford_red.get()
print ford_black.get()
And if I just remove the line #staticmethod, everything works, but I do not understand why. Shouldn't it need self as a first argument?
This is not how staticmethod is supposed to be used. staticmethod objects are descriptors that return the wrapped object, so they only work when accessed as classname.staticmethodname. Example
class A(object):
#staticmethod
def f():
pass
print A.f
print A.__dict__["f"]
prints
<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>
Inside the scope of A, you would always get the latter object, which is not callable.
I'd strongly recommend to move the decorator to the module scope -- it does not seem to belong inside the class. If you want to keep it inside the class, don't make it a staticmethod, but rather simply del it at the end of the class body -- it's not meant to be used from outside the class in this case.
Python classes are created at runtime, after evaluating the contents of the class declaration. The class is evaluated by assigned all declared variables and functions to a special dictionary and using that dictionary to call type.__new__ (see customizing class creation).
So,
class A(B):
c = 1
is equivalent to:
A = type.__new__("A", (B,), {"c": 1})
When you annotate a method with #staticmethod, there is some special magic that happens AFTER the class is created with type.__new__. Inside class declaration scope, the #staticmethod function is just an instance of a staticmethod object, which you can't call. The decorator probably should just be declared above the class definition in the same module OR in a separate "decorate" module (depends on how many decorators you have). In general decorators should be declared outside of a class. One notable exception is the property class (see properties). In your case having the decorator inside a class declaration might make sense if you had something like a color class:
class Color(object):
def ___init__(self, color):
self.color = color
def ensure_same_color(f):
...
black = Color("black")
class TFord(object):
def __init__(self, color):
self.color = color
#black.ensure_same_color
def get():
return 'Here is your shiny new T-Ford'
Solution does exist!
Problem is that Static method that is trying to be used as decorator is in fact staticmethod object and is not callable.
Solution: staticmethod object has method __get__ which takes any argument and returns real method: python documentation Python 3.5 and up:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
Min solution I came with is:
class A():
def __init__(self):
self.n = 2
#staticmethod
def _returnBaseAndResult(func):
from functools import wraps
#wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
response = func(*args, **kwargs)
return self.n, response
return wrapper
#_returnBaseAndResult.__get__('this can be anything')
def square(self):
return self.n**2
if __name__ == '__main__':
a = A()
print(a.square())
Will print (2, 4)
ensure_black is returning a _aux method that isn't decorated by #staticmethod
You can return a non-static method to a static_method
http://docs.python.org/library/functions.html#staticmethod