5
def foo(a, b, c = 0):
    return a+b

I have dozens of functions like 'foo', which all have different argument numbers and names. Is there a common way that I can get the return values of these functions and do just a single extra operation like pformat to them?

Yes I can just generate a new function like the following:

func = ...  # func can be got using getattr by name
def wrapper(*arg, **kw):
    data = func(*arg, **kw)
    return pprint.pformat(data)
return wrapper

But then the new function 'wrapper' is different to the old one 'func', for example, in argument number, 'wrapper' has only 2 args--'arg' and 'kw', but 'func' may have many args, like 'a', 'b', 'c'.

I just want play with the return value, everything else should stay still, is it possible?

Thanks!

Update Finally this problem was solved using decorator module and the following patch:

--- /home/jaime/cache/decorator-3.2.0/src/decorator.py  2010-05-22 23:53:46.000000000 +0800
+++ decorator.py    2010-10-28 14:55:11.511140589 +0800
@@ -66,9 +66,12 @@
             self.name = '_lambda_' 
             self.doc = func.__doc__
             self.module = func.__module__
-            if inspect.isfunction(func):
+            if inspect.isfunction(func) or inspect.ismethod(func):
                 argspec = inspect.getargspec(func)
                 self.args, self.varargs, self.keywords, self.defaults = argspec
+                if inspect.ismethod(func):
+                    self.args = self.args[1:] # Remove the useless 'self' arg
+                    argspec = inspect.ArgSpec(self.args, self.varargs, self.keywords, self.defaults)
                 for i, arg in enumerate(self.args):
                     setattr(self, 'arg%d' % i, arg)
                 self.signature = inspect.formatargspec(

This patch allows you to decorate bounded methods, it just throws the first 'self' argument away, usage of decorator.decorator stays the same, no bad effects found right now.

example code:

def __getattr__(self, attr):
    def pformat_wrapper(f, *args, **kw):
        data = f(*args, **kw)
        return pprint.pformat(data, indent = 4)

    method = getattr(self.ncapi, attr)
    return decorator(pformat_wrapper, method) # Signature preserving decorating





jaime@westeros:~/bay/dragon.testing/tests$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import decorator
>>> class A:
...   def f(self):
...       pass
... 
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0xb774a20c>>
>>> def hello(f, *args, **kw):
...     print 'hello'
...     return f(*args, **kw)
... 
>>> f1 = decorator.decorator(hello, a.f)
>>> f1()
hello
>>>
jaimechen
  • 511
  • 1
  • 5
  • 17
  • BTW Python doesn't support method overloading. – Tauquir Oct 22 '10 at 10:28
  • Changing your functions to replace usable values with `pprint.pformat` strings is actually a Really Bad Idea (tm). Don't. Do. It. – S.Lott Oct 22 '10 at 10:44
  • Seems like changing the return value(s) of your functions -- as in turning them all into single strings -- would really foul up the functioning of the rest of your application -- or am I missing something? – martineau Oct 22 '10 at 19:18
  • @S.Lott & martineau, I forgot to mention that the return value is still returned untouched in the new function. The extra operation actually can be any kind of code that taking a value and returning a new value according to your need. This is much more like monkey patching a function with input signature preserving. – jaimechen Oct 23 '10 at 16:45
  • @chenz "monkey patching a function" doesn't really make much sense. The concept only applies to classes and modules where there are a large number of features. You'll be a lot happier just creating a new function that "wraps" the old function. It's so much simpler to just write design `b(a(x))`. Even if there are dozens, ordinary design of ordinary functions is so much simpler than this "monkey patching" concept. – S.Lott Oct 23 '10 at 17:47
  • You're definitely right, S.Lott, thanks for the advice. – jaimechen Oct 25 '10 at 02:17

3 Answers3

4

About your problem :

"But then the new function 'wrapper' is different to the old one 'func', for example, >> in argument number, 'wrapper' has only 2 args--'arg' and 'kw', but 'func' may have many >> args, like 'a', 'b', 'c'."

you can use the decorator module which enable you to create a signature-preserving decorators.

mouad
  • 67,571
  • 18
  • 114
  • 106
  • +1 to mention the decorator module. It's really powerful. Also [Venusian](http://pypi.python.org/pypi/venusian) might be useful when dealing with decorators. – Attila O. Oct 22 '10 at 12:40
  • decorator module almost solves the problems, but it seems does not work with bound or unbound methods. "Moreover, notice that you can decorate a method, but only before if becomes a bound or unbound method, i.e. inside the class....", so it's useless if you want to decorating some methods of a class without modifying its source code as far as I can see. – jaimechen Oct 25 '10 at 07:47
2

Decorators.

from functools import wraps
def pformat_this( someFunc ):
    @wraps( someFunc )
    def wrapper(*arg, **kw):
        data = someFunc(*arg, **kw)
        return pprint.pformat(data)
    return wrapper


@pformat_this
def foo(a, b, c = 0):
    return a+b
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    @S.Loot : functools.wrap is not a signature-preserving decorators , it just preserve _ _name_ _ and _ _doc_ _ – mouad Oct 22 '10 at 11:59
2

Decorators are essentially the same as what you do not want.

Getting curious, I looked into this for python 2.7, and found that there is a wealth of meta information available for user defined functions under Callable types -> User-defined functions. Unfortunately, there is nothing about the returned value.

There is also an internal type you can access through the function, a code object, on the same page, under Internal types -> Code objects. Even though these internal types are essentially provided with no promises to API stability, there doesn't seem to be anything writable with regard to the returned value there either.

I have a feeling that if there were anything you could do directly, it would be here. Hopefully someone else has better luck for you.