decorate specific methods of a class - python

i read about decorators and i am trying to decorate all the methods of a class WITHOUT static methods.
right now i just use the decorator i wrote for the specific functions that are not static, so i wonder if there is a way to both decorate a lot of methods but avoid static ones
what i get with my decorator:
TypeError: unbound method test() must be called with ClassTest instance as first argument (got nothing instead)
my decorator:
def decorator(func):
def wrapper(self, *args, **kwargs):
print "test"
return func(self, *args, **kwargs)
return wrapper

First of all, decorating a class is pretty simple:
def class_decorator(cls):
# modify cls
return cls
In order to add/remove/modify functionality to a method, you could call setattr with a decorated version of a method (or a variable):
setattr(some_class, some_attribute, decorator(some_callable))
As to differentiating between different types of methods, there are a couple of attributes you'll be able to use
to determine whether a method is an instance/class/static method.
A full working example:
def _is_instance_method(var):
if not hasattr(var, '__call__'): # It's not a callable
return False
if not hasattr(var, 'im_self'): # It's a callable, but it's not a bound method
return False
if getattr(var, 'im_self') is not None: # At this point, if it's a class method,
# it will be bound to the class, while
# the instance method is still unbound
# return False if it's bound (i.e. a class method)
return False
return True # All that remains is a callable, that's boundable, but not yet -- an instance method!
def func_decorator(func):
def func_wrapper(self, *args, **kwargs):
print "Inside %s!" % (func.__name__,)
return func(self, *args, **kwargs)
return func_wrapper
def class_decorator(cls):
for attr in cls.__dict__:
var = getattr(cls, attr)
if _is_instance_method(var): # Determine whether the attribute is an instance method
setattr(cls, attr, func_decorator(var)) # Replace the function with a decorated one
return cls # Return the class with its new decorated instance methods
#class_decorator
class B(object):
#staticmethod
def static_method():
return "static method"
#classmethod
def cls_method(cls):
return "cls method"
def instance_method(self):
return "instance method"
print B.static_method()
print B.cls_method()
b = B()
print b.instance_method()

Related

Applying class decorator to staticmethod: takes 1 positional argument but 2 were given

I am trying to apply a class decorator to each of its methods. But staticmethod and classmethod are not working for me. I got this:
import functools
import re
def decor(cls):
def decorator(f):
if isinstance(f, type):
for attr in f.__dict__:
if callable(getattr(f, attr)) and not re.match(r"__\w*__", attr):
setattr(f, attr, decorator(getattr(f, attr)))
return f
#functools.wraps(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
return decorator(cls)
#decor
class MyClass:
def printer1(self, string):
print(string)
#classmethod
def printer2(cls, string):
print(string)
#staticmethod
def printer3(string):
print(string)
MyClass().printer1("foo") # this works
MyClass().printer2("foo") # this does not work
MyClass.printer2("foo") # this works
MyClass().printer3("foo") # this does not work
MyClass.printer3("foo") # this works
# the error is the following:
TypeError: printer() takes 1 positional argument but 2 were given
Basically every time I instantiate the class, I get the error when calling a staticmethod and a classmethod.
You don't seem to understand the working principle of staticmethod and classmethod. This is a link about the working principle of classmethod. The working principle of the staticmethod is similar to that of the classmethod. The difference is that when accessing the staticmethod, the class will not be bound to the function, but simply return the function.
There are two problems:
The decorator also wraps the class and returns a function instead of a class, which prevents staticmethod and classmethod from working properly.
For staticmethod and classmethod, getattr will get the result returned after the descriptor triggers the __get__ method, not necessarily the function itself. And here you only use wrapper to wrap, and do not restore them to the corresponding special methods.
Here is a working example:
def decorator(cls_or_func):
if isinstance(cls_or_func, type):
cls = cls_or_func
for name, attr in cls.__dict__.items():
if isinstance(attr, (staticmethod, classmethod)):
setattr(cls, name, type(attr)(decorator(attr.__func__)))
elif callable(attr) and not re.match(r"__\w*__", name):
setattr(cls, name, decorator(attr))
return cls
func = cls_or_func
#functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper

Python3 impossible to pass #property as decorator argument

I've implemented decorator that can receive extra arguments and want to use it with class methods. I want to pass #property as decorator argument, but instead of #property result I got this:
<property object at 0x7f50f5195230>
This is my decorator:
class Decorator(object):
def __init__(self, some_arg):
self.func = None
self.some_arg = some_arg
def __get__(self, instance, owner):
import functools
return functools.partial(self.__call__, instance)
def __call__(self, func):
self.func = func
def wrapper(*args, **kwargs):
return self._process_sync(*args, **kwargs)
return wrapper
def _process_sync(self, *args, **kwargs):
try:
print(self.some_arg)
return self.func(*args, **kwargs)
except Exception as e:
print(e)
return None
My test class:
class Test(object):
#property
def some_data(self):
return {'key': 'value'}
#Decorator(some_data)
def some_method(self):
print('method output')
return None
Usage:
test = Test()
test.some_method()
Two questions:
How to correctly pass property to receive #property result instead of <property object at 0x7f50f5195230>
Does it possible to pass class properties/methods to the decorator if they are below in code?
A property object is a descriptor. To get a value out of it, you need to call its __get__ method with an appropriate instance. Figuring out when to do that in your current code is not easy, since your Decorator object has a bunch of different roles. It's both a decorator factory (getting initialized with an argument in the #Decorator(x) line), and the decorator itself (getting called with the function to be decorated). You've given it a __get__ method, but I don't expect that to ever get used, since the instance of Decorator never gets assigned to a class variable (only the wrapper function that gets returned from __call__).
Anyway, here's a modified version where the Decorator handles almost all parts of the descriptor protocol itself:
class Decorator:
def __init__(self, arg):
self.arg = arg # this might be a descriptor, like a property or unbound method
def __call__(self, func):
self.func = func
return self # we still want to be the descriptor in the class
def __get__(self, instance, owner):
try:
arg = self.arg.__get__(instance, owner) # try to bind the arg to the instance
except AttributeError: # if it doesn't work, self.arg is not a descriptor, that's OK
arg = self.arg
def wrapper(*args, **kwargs): # this is our version of a bound method object
print(arg) # do something with the bound arg here
return self.func.__get__(instance, owner)(*args, **kwargs)
return wrapper

Decorators for Instance Methods

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).

Python class method decorator

I write a decorator for class method
def decor(method):
def wrapped(self, *args, **kwargs):
return method(self, *args, **kwargs)
# [*]
return wrapped
I would like use this like:
class A(metaclass=mymetaclass):
#decor
def meth(self):
pass
How I can in decorator add method/variable to class which has decorated method? I need it do near [*].
Inside wrapped I could write self.__class__, but what to do here?
I cannot imagine a way to meet such a requirement, because decor function only receives a function object that knows nothing about a containing class.
The only workaround that I can imagine is to use a parameterized decorator and pass it the class being decorated
def decor(cls):
def wrapper(method):
def wrapped(self, *args, **kwargs):
return self.method(*args, **kwargs)
print method # only a function object here
return wrapped
print cls # here we get the class and can manipulate it
return wrapper
class A
#decor(A)
def method(self):
pass
Alternatively, you could decorate the class itself:
def cdecor(cls):
print 'Decorating', cls # here we get the class and can manipulate it
return cls
#cdecor
class B:
def meth(self):
pass
gives:
Decorating __main__.B
It looks like you just wanted to decorate one of a classes functions, not specifically an #classmethod. Here's a simple way that I did it when I wanted to call a classes save function when the function returned a successful result:
def save_on_success(func):
""" A decorator that calls a class object's save method when successful """
def inner(self, *args, **kwargs):
result = func(self, *args, **kwargs)
if result:
self.save()
return result
return inner
Here is an example of how it was used:
class Test:
def save(self):
print('saving')
#save_on_success
def test(self, var, result=True):
print('testing, var={}'.format(var))
return result
Testing to make sure it works as expected:
>>> x = Test()
>>> print(x.test('test True (should save)', result=True))
testing, var=test True (should save)
saving
True
>>> print(x.test('test False (should not save)', result=False))
testing, var=test False (should not save)
False
It looks like it is not directly possible, according to this response :
Get Python function's owning class from decorator
What you could do instead is providing a decorator for your class, something like that :
class InsertMethod(object):
def __init__(self, methodToInsert):
self.methodToInsert = methodToInsert
def __call__(self, classObject):
def wrapper(*args, **kwargs):
setattr(classObject, self.methodToInsert.__name__, self.methodToInsert)
return classObject(*args, **kwargs)
return wrapper
def IWillBeInserted(self):
print "Success"
#InsertMethod(IWillBeInserted)
class Something(object):
def __init__(self):
pass
def action(self):
self.IWillBeInserted()
a = Something()
a.action()
Actually, you may decorate the class itself:
def class_decorator(class_):
class_.attribute = 'value'
class_.method = decorate(class_.method)
return class_
#class_decorator
class MyClass:
def method(self):
pass
I'm a little late to the party, but late is better than never eh? :)
We can do this by decorating our class method with a decorator which is itself a class object, say B, and then hook into the moment when Python calls B.__get__ so to fetch the method. In that __get__ call, which will be passed both the owner class and the newly generated instance of that class, you can elect to either insert your method/variable into the original owner class, or into the newly defined instance.
class B(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
def __get__(self, instance, owner):
instance.inserted = True
# owner.inserted = True
def wrapper(*args, **kwargs):
return self(instance, *args, **kwargs)
return wrapper
class A:
#B
def method(self):
pass
if __name__ == "__main__":
a = A()
a.method()
b = A()
print(hasattr(a, 'inserted'))
print(hasattr(b, 'inserted'))
In this example, we're wrapping def method(self) with #B. As written, the inserted attribute inserted will only persist in the a object because it's being applied to the instance. If we were to create a second object b as shown, the inserted attribute is not included. IE, hasattr(a, 'inserted') prints True and hasattr(b, 'inserted') prints False. If however we apply inserted to the owner class (as shown in the commented out line) instead, the inserted attribute will persist into all future A() objects. IE hasattr(a, 'inserted') prints True and hasattr(b, 'inserted') prints True, because b was created after a.method() was called.

Decorating a method - how to call the method in oo fashion instead of procedural fashion

Following up on an earlier thread my question is how can I take this expression: fn(self,*args, **kwargs and call it in oo fashion like this self.fn(...) here is my total program with the failing line commented out:
def formatHeader(fn):
def wrapped(*args, **kwargs):
print "here is args prior to extraction - {0}".format(args)
if len(args) > 1:
self,args = args # remove self from args
else:
self, args= args[0], ()
print("Here are the arguments after extraction: {0} {1}".format(self, args))
#return '<div class="page_header">' + self.fn(*args, **kwargs)+'</div>'
return '<div class="page_header">' + fn(self,*args, **kwargs)+'</div>'
return wrapped
class MyPage(object):
def __init__(self):
self.PageName = ''
def createPage(self):
pageHeader = self.createHeader()
return pageHeader
def addem(self, a, b):
return a + b
#formatHeader #<----- decorator
def createHeader(self):
return "Page Header " + self.PageName
obj = MyPage()
print obj.createHeader()
First, self doesn't exist in that scope. self is a label that by convention refers to the current instance within instance methods of a class. Second, decorators are not meant to be aware of the instance of the function they are wrapping(at least by default). The method is bound to the instance, and the decorator operates on the bound method it was passed as a black box callable. If you want to have access to the instance members from within the decorator (which you shouldn't, since that actually breaks oo), you'll have to pass the instance to the decorator, which means enclosing the decorator in a further closure, or using introspection to dynamically discover the owning instance from within the decorator code.
The problem is your wrapper wants to access another method of the instance to which it's being applied, but it's not passed one until runtime -- as opposed to class definition time which is when the decorator runs). Basically you need an instance specific method decorator. You can accomplish this by making the decorator a descriptor as described in the class method decorator using instance recipe from the PythonDecoratorLibrary.
Here's it applied to your sample code (with some cosmetic changes):
from functools import wraps
def formatHeader(fn):
class Descriptor(object):
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, klass):
if instance is None: # Class method was requested
return self.make_unbound(klass) # will raise TypeError
return self.make_bound(instance)
def make_unbound(self, klass):
#wraps(self.fn)
def wrapped(*args, **kwargs):
'''This documentation will vanish :)'''
raise TypeError(
'unbound method {}() must be called with {} instance '
'as first argument (got nothing instead)'.format(
self.fn.__name__, klass.__name__)
)
return wrapped
def make_bound(self, instance):
#wraps(self.fn)
def wrapped(*args, **kwargs):
'''This documentation will disapear :)'''
return ('<div class="page_header">' +
self.fn(instance, *args, **kwargs) + '</div>')
# This instance does not need the descriptor anymore,
# let it find the wrapped method directly next time:
setattr(instance, self.fn.__name__, wrapped)
return wrapped
return Descriptor(fn)
class MyPage(object):
def __init__(self):
self.PageName = ''
def createPage(self):
pageHeader = self.createHeader()
return pageHeader
def addem(self, a, b):
return a + b
#formatHeader #<----- decorator
def createHeader(self):
return "Page Header " + self.PageName
obj = MyPage()
print obj.createHeader()
Note that the self argument in the nested Descriptor class methods refers to an instance of the Descriptor class not the class whose method was wrapped (that's instance in the code).

Categories