Determining if a function is a method of a class within a decorator

D

David Hirschfield

I'm having a little problem with some python metaprogramming. I want to
have a decorator which I can use either with functions or methods of
classes, which will allow me to swap one function or method for another.
It works as I want it to, except that I want to be able to do some
things a little differently depending on whether I'm swapping two
functions, or two methods of a class.

Trouble is, it appears that when the decorator is called the function is
not yet bound to an instance, so no matter whether it's a method or
function, it looks the same to the decorator.

This simple example illustrates the problem:

import inspect
class swapWith(object):
def __init__(self, replacement):
self.replacement = replacement

def __call__(self, thingToReplace):
def _replacer(*args, **kws):
import inspect
print
"replacing:",self.replacement,inspect.ismethod(self.replacement)
return self.replacement(*args, **kws)
return _replacer

class MyClass(object):

def swapIn(self):
print "this method will be swapped in"

@swapWith(swapIn)
def swapOut(self):
print "this method will be swapped out"

c = MyClass()
c.swapOut()


def swapInFn():
print "this function will be swapped in"

@swapWith(swapInFn)
def swapOutFn():
print "this function will be swapped out"

swapOutFn()


Both MyClass.swapIn and swapInFn look like the same thing to the
decorator, and MyClass.swapOut and swapOutFn look the same. So is there
a pattern I can follow that will allow me to determine whether the
objects I'm given are plain functions or belong to a class?

Thanks in advance,
-David
 
S

Steven D'Aprano

I'm having a little problem with some python metaprogramming. I want to
have a decorator which I can use either with functions or methods of
classes, which will allow me to swap one function or method for another.
It works as I want it to, except that I want to be able to do some
things a little differently depending on whether I'm swapping two
functions, or two methods of a class.

Trouble is, it appears that when the decorator is called the function is
not yet bound to an instance, so no matter whether it's a method or
function, it looks the same to the decorator.

Then:

* use a naming convention to recognise methods (e.g. check if the first
argument is called "self" by looking at function.func_code.co_varnames);

* use two different decorators, or a decorator that takes an extra
"method or function argument";

* play around with the class metaclass and see if you can do something
special (and probably fragile);

* do the replacements after the class is created without decorator
syntax, e.g.:

Class.method1 = swapWith(Class.method2)(Class.method1)



This simple example illustrates the problem:
[snip]


No it doesn't. It appears to work perfectly, from what I can guess you're
trying to accomplish.



Have you considered a possibly simpler way of swapping methods/functions?

.... print "It's pining for the fjords."
........ print "We don't stock Cheddar."
....It's pining for the fjords.


Admittedly, this doesn't let you do extra processing before calling the
swapped functions, but perhaps it will do for what you need.
 
C

Carl Banks

So is there
a pattern I can follow that will allow me to determine whether the
objects I'm given are plain functions or belong to a class?

Thanks in advance,



class HomemadeUnboundMethod(object):
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print "is a function: %s" % self.func.func_name
return self.func(*args,**kwargs)
def __get__(self,obj,owner):
return HomemadeBoundMethod(obj,self.func)

class HomemadeBoundMethod(object):
def __init__(self,obj,func):
self.obj = obj
self.func = func
def __call__(self,*args,**kwargs):
print "is a method: %s" % self.func.func_name
return self.func(self.obj,*args,**kwargs)

class A(object):
@HomemadeUnboundMethod
def method(self): pass

@HomemadeUnboundMethod
def function(): pass

A().method()
function()



Just override the __call__ functions to do what you want the decorated
function to do. There are other little improvements you might make
(account for the owner parameter of __get__ for instance) but you get
the idea.


Carl Banks
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,434
Messages
2,571,691
Members
48,796
Latest member
Greg L.

Latest Threads

Top