Can a Decorator of an Instance Method Access the Class

Can a decorator of an instance method access the class?

If you are using Python 2.6 or later you could use a class decorator, perhaps something like this (warning: untested code).

def class_decorator(cls):
for name, method in cls.__dict__.iteritems():
if hasattr(method, "use_class"):
# do something with the method and class
print name, cls
return cls

def method_decorator(view):
# mark the method as something that requires view's class
view.use_class = True
return view

@class_decorator
class ModelA(object):
@method_decorator
def a_method(self):
# do some stuff
pass

The method decorator marks the method as one that is of interest by adding a "use_class" attribute - functions and methods are also objects, so you can attach additional metadata to them.

After the class has been created the class decorator then goes through all the methods and does whatever is needed on the methods that have been marked.

If you want all the methods to be affected then you could leave out the method decorator and just use the class decorator.

Python Decorator with arguments and accessing class instance

Yes. In fact, in the sense you seem to mean, there isn't really a way to write a decorator that doesn't have access to self. The decorated function wraps the original function, so it has to accept at least the arguments that that function accepts (or some arguments from which those can be derived), otherwise it couldn't pass the right arguments to the underlying function.

There is nothing special you need to do to do this, just write an ordinary decorator:

def deco(func):
def wrapper(self, *args, **kwargs):
print "I am the decorator, I know that self is", self, "and I can do whatever I want with it!"
print "I also got other args:", args, kwargs
func(self)
return wrapper

class Foo(object):
@deco
def meth(self):
print "I am the method, my self is", self

Then you can just use it:

>>> f = Foo()
>>> f.meth()
I am the decorator, I know that self is <__main__.Foo object at 0x0000000002BCBE80> and I can do whatever I want with it!
I also got other args: () {}
I am the method, my self is <__main__.Foo object at 0x0000000002BCBE80>
>>> f.meth('blah', stuff='crud')
I am the decorator, I know that self is <__main__.Foo object at 0x0000000002BCBE80> and I can do whatever I want with it!
I also got other args: (u'blah',) {'stuff': u'crud'}
I am the method, my self is <__main__.Foo object at 0x0000000002BCBE80>

Can I access the target class instance in a Typescript method decorator?

It is not possible to access an instance in a Typescript method decorator. But it is possible to change the prototype and constructor using decorators. So the possible solution is to use two decorators: the first one that "marks" methods and the second one that changes the constructor adding the registration logic.

Below I'll try to illustrate the idea

const SubMethods = Symbol('SubMethods'); // just to be sure there won't be collisions

function WebsocketRequest(requestName: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[SubMethods] = target[SubMethods] || new Map();
// Here we just add some information that class decorator will use
target[SubMethods].set(propertyKey, requestName);
};
}

function WebSocketListener<T extends { new(...args: any[]): {} }>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
const subMethods = Base.prototype[SubMethods];
if (subMethods) {
subMethods.forEach((requestName: string, method: string) => {
WebsocketHandler.getInstance()
.registerRequestHandler(
requestName,
() => (this as any)[method]()
);
});
}
}
};
}

Usage:

@WebsocketListener
class ListOfStuff {
private list = [];

@WebsocketRequest("getListLength")
private listLengthRequest() : WebSocketResponse {
return new WebSocketResponse(this.list.length);
}
}

Updated repl

Use an instance method as a decorator within another class

You can use a staticmethod to wrap decorator. The inner func_wrap function of decorator contains an additional parameter in its signature: cls. cls can be used to access the ser attribute of the instance of App, and then the desired methods write and read can be called from cls.ser. Also, note that in your declarations, MySerial.write takes no paramters, but is passed the result of the wrapped function. The code below uses *args to prevent the TypeError which would otherwise be raised:

class MySerial():
def __init__(self):
pass # I have to have an __init__
def write(self, *args):
pass # write to buffer
def read(self):
pass # read to buffer
@staticmethod
def decorator(func):
def func_wrap(cls, *args, **kwargs):
cls.ser.write(func(cls, *args, **kwargs))
return cls.ser.read()
return func_wrap

class App():
def __init__(self):
self.ser = MySerial()
@MySerial.decorator
def myfunc(self):
# 'yummy_bytes' is written to the serial buffer via
# MySerial's decorator method
return 'yummy_bytes'

App().myfunc()

Is it possible for a Python decorator decorating a instance method/classmethod to know the class the function will be bound to?

As jasonharper mentions, the class doesn't exist yet by the time the decorator is called, and so the function it receives is just a regular function (except that its name mentions the class it will be bound to).


For my problem, I ended up doing it attrs-style, using an additional decorator to decorate the class as well.

def include(f: Callable) -> Callable:
"""Add function `f` to SchemaBuilder."""
SchemaBuilder.append(f)
return f

class SchemaBuilder:
records: Dict[Type, Dict[Callable, Any]] = {}
temporary: List[Callable] = []

@classmethod
def append(cls, f: Callable):
"""Temporarily store the method in a list."""
cls.temporary.append(f)

@classmethod
def register(cls, target_cls: Type):
"""Associate all methods stored in the list with `target_cls`.

We rely on the fact that `target_cls` will be instantiated
(and thus this method called) as soon as all of its (immediate)
methods have been created.
"""
cls.records[target_cls] = {k: None for k in cls.temporary}
cls.temporary = []

# In use:

@SchemaBuilder.register # called later
class SomeClass:
@property
@include # called first
def some_property(self): # will be included
pass

@property
def some_other_property(self): # will not be included
pass

Python decorators in classes

Would something like this do what you need?

class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic

@_decorator
def bar( self ) :
print "normal call"

test = Test()

test.bar()

This avoids the call to self to access the decorator and leaves it hidden in the class namespace as a regular method.

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>>

edited to answer question in comments:

How to use the hidden decorator in another class

class Test(object):
def _decorator(foo):
def magic( self ) :
print "start magic"
foo( self )
print "end magic"
return magic

@_decorator
def bar( self ) :
print "normal call"

_decorator = staticmethod( _decorator )

class TestB( Test ):
@Test._decorator
def bar( self ):
print "override bar in"
super( TestB, self ).bar()
print "override bar out"

print "Normal:"
test = Test()
test.bar()
print

print "Inherited:"
b = TestB()
b.bar()
print

Output:

Normal:
start magic
normal call
end magic

Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic

Python method decorator to access an instance variable

Decorators are ordinary functions, and self is no exception to the rule. So let's say I write a decorator function that takes an argument called self:

def needs_state(fn):
def decorator(self, *args, **kwargs):
if self.state is None:
raise ValueError('Oh no')
return fn(self, *args, **kwargs)
return decorator

It does not know what self is, because it is not in a class, but that's okay, you can use the decorator in a class, in the way you expect.

class MyClass(object):

def __init__(self, state=None):
self.state = state

@needs_state
def some_function(self):
print self.state

So, if you now instantiate the class with anything, your some_function will first check if self.state is none, because you had just decorated it. Similarly, if the class does not have a state, then the exception is raised as expected.

MyClass(1).some_function()  # 1
MyClass(None).some_function() # raises

self.process is left out so you need to do some work.

How can I decorate an instance method with a decorator class?

tl;dr

You can fix this problem by making the Timed class a descriptor and returning a partially applied function from __get__ which applies the Test object as one of the arguments, like this

class Timed(object):
def __init__(self, f):
self.func = f

def __call__(self, *args, **kwargs):
print(self)
start = dt.datetime.now()
ret = self.func(*args, **kwargs)
time = dt.datetime.now() - start
ret["time"] = time
return ret

def __get__(self, instance, owner):
from functools import partial
return partial(self.__call__, instance)

The actual problem

Quoting Python documentation for decorator,

The decorator syntax is merely syntactic sugar, the following two function definitions are semantically equivalent:

def f(...):
...
f = staticmethod(f)

@staticmethod
def f(...):
...

So, when you say,

@Timed
def decorated(self, *args, **kwargs):

it is actually

decorated = Timed(decorated)

only the function object is passed to the Timed, the object to which it is actually bound is not passed on along with it. So, when you invoke it like this

ret = self.func(*args, **kwargs)

self.func will refer to the unbound function object and it is invoked with Hello as the first argument. That is why self prints as Hello.


How can I fix this?

Since you have no reference to the Test instance in the Timed, the only way to do this would be to convert Timed as a descriptor class. Quoting the documentation, Invoking descriptors section,

In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol: __get__(), __set__(), and __delete__(). If any of those methods are defined for an object, it is said to be a descriptor.

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with a.__dict__['x'], then type(a).__dict__['x'], and continuing through the base classes of type(a) excluding metaclasses.

However, if the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead.

We can make Timed a descriptor, by simply defining a method like this

def __get__(self, instance, owner):
...

Here, self refers to the Timed object itself, instance refers to the actual object on which the attribute lookup is happening and owner refers to the class corresponding to the instance.

Now, when __call__ is invoked on Timed, the __get__ method will be invoked. Now, somehow, we need to pass the first argument as the instance of Test class (even before Hello). So, we create another partially applied function, whose first parameter will be the Test instance, like this

def __get__(self, instance, owner):
from functools import partial
return partial(self.__call__, instance)

Now, self.__call__ is a bound method (bound to Timed instance) and the second parameter to partial is the first argument to the self.__call__ call.

So, all these effectively translate like this

t.call_deco()
self.decorated("Hello", world="World")

Now self.decorated is actually Timed(decorated) (this will be referred as TimedObject from now on) object. Whenever we access it, the __get__ method defined in it will be invoked and it returns a partial function. You can confirm that like this

def call_deco(self):
print(self.decorated)
self.decorated("Hello", world="World")

would print

<functools.partial object at 0x7fecbc59ad60>
...

So,

self.decorated("Hello", world="World")

gets translated to

Timed.__get__(TimedObject, <Test obj>, Test.__class__)("Hello", world="World")

Since we return a partial function,

partial(TimedObject.__call__, <Test obj>)("Hello", world="World"))

which is actually

TimedObject.__call__(<Test obj>, 'Hello', world="World")

So, <Test obj> also becomes a part of *args, and when self.func is invoked, the first argument will be the <Test obj>.

Get Python function's owning class from decorator

If fn is an instancemethod, then you can use fn.im_class.


>>> class Foo(object):
... def bar(self):
... pass
...
>>> Foo.bar.im_class
__main__.Foo

Note that this will not work from a decorator, because a function is only transformed into an instance method after the class is defined (ie, if @specialTest was used to decorate bar, it would not work; if it's even possible, doing it at that point would have to be done by inspecting the call stack or something equally unhappy).

Decorator to convert an instance method into a class function

Sure; just think about how an unwrapped method should look:

def callback(fn):
def inner(self, *args):
return _do_callback(fn.__get__(self, type(self)), self.log, *args)
return inner

class Foo(object):
def __init__(self):
self.log = Log('Foo')

@callback
def cb1_wrapped(self, x):
pass


Related Topics



Leave a reply



Submit