tags:

views:

69

answers:

3

Consider the following (broken) code:

import functools                                                            
class Foo(object):                                                          
  def __init__(self):                                                 
    def f(a,self,b):                                            
      print a+b                                           
    self.g = functools.partial(f,1)                             
x=Foo()                                                                     
x.g(2)

What I want to do is take the function f and partially apply it, resulting in a function g(self,b). I would like to use this function as a method, however this does not currently work and instead I get the error

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    x.g(2)
TypeError: f() takes exactly 3 arguments (2 given)

Doing x.g(x,2) however works, so it seem the issue is that g is considered a "normal" function instead of a method of the class. Is there a way to get x.g to behave like a method (i.e implicitly pass the self parameter) instead of a function?

+3  A: 

This will work. But I'm not sure if this is what you are looking for

class Foo(object):                                                          
  def __init__(self):                                                 
    def f(a,self,b):                                            
      print a+b                                           
    self.g = functools.partial(f,1, self) # <= passing `self` also.

x = Foo()
x.g(2)
Manoj Govindan
Stupid me, pretty obvious in hindsight!
pafcu
A: 

The issue here is that you've not connected either a nor b to the object. Hence, it's going to continue to ask for the extra arguments. If you'd like to have it perform as you've indicated you'll need to establish a variable for it to act upon:

def f(self, a):
    print self.b + a
self.g = f

Otherwise, you've just defined a function that has nothing to do with your object. On the other hand if you'd like it to just "work" using the 1 variable, then you're going to need to have remove the self call.

def f(a, b)
    print a+b
self.g = functools.partial(f,1)
wheaties
+3  A: 

There are two issues at hand here. First, for a function to be turned into a method it must be stored on the class, not the instance. A demonstration:

class Foo(object):
    def a(*args):
        print 'a', args

def b(*args):
    print 'b', args

Foo.b = b

x = Foo()

def c(*args):
    print 'c', args

x.c = c

So a is a function defined in the class definition, b is a function assigned to the class afterwards, and c is a function assigned to the instance. Take a look at what happens when we call them:

>>> x.a('a will have "self"')
a (<__main__.Foo object at 0x100425ed0>, 'a will have "self"')
>>> x.b('as will b')
b (<__main__.Foo object at 0x100425ed0>, 'as will b')
>>> x.c('c will only recieve this string')
c ('c will only recieve this string',)

As you can see there is little difference between a function defined along with the class, and one assigned to it later. I believe there is actually no difference as long as there is no metaclass involved, but that is for another time.

The second problem comes from how a function is actually turned into a method in the first place; the function type implements the descriptor protocol. (See the docs for details.) In a nutshell, the function type has a special __get__ method which is called when you perform an attribute lookup on the class itself. Instead of you getting the function object, the __get__ method of that function object is called, and that returns a bound method object (which is what supplies the self argument).

Why is this a problem? Because the functools.partial object is not a descriptor!

>>> import functools
>>> def f(*args):
...     print 'f', args
... 
>>> g = functools.partial(f, 1, 2, 3)
>>> g
<functools.partial object at 0x10042f2b8>
>>> g.__get__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__get__'

There are a number of options you have at this point. You can explicitly supply the self argument to the partial:

import functools
class Foo(object):                                                          
    def __init__(self):
        def f(self, a, b):                                            
            print a + b                                           
        self.g = functools.partial(f, self, 1)

x = Foo()
x.g(2)

...or you would imbed the self and value of a in a closure:

class Foo(object):                                                          
    def __init__(self):                                                 
        a = 1
        def f(b):                                            
            print a + b                                           
        self.g = f

x = Foo()
x.g(2)

These solutions are of course assuming that there is an as yet unspecified reason for assigning a method to the class in the constructor like this, as you can very easily just define a method directly on the class to do what you are doing here.

Edit: Here is an idea for a solution assuming the functions may be created for the class, instead of the instance:

class Foo(object):
    pass

def make_binding(name):
    def f(self, *args):
        print 'Do %s with %s given %r.' % (name, self, args)
    return f

for name in 'foo', 'bar', 'baz':
    setattr(Foo, name, make_binding(name))

f = Foo()
f.foo(1, 2, 3)
f.bar('some input')
f.baz()

Gives you:

Do foo with <__main__.Foo object at 0x10053e3d0> given (1, 2, 3).
Do bar with <__main__.Foo object at 0x10053e3d0> given ('some input',).
Do baz with <__main__.Foo object at 0x10053e3d0> given ().
Mike Boers
I'm actually creating a bunch of partial functions in a loop (with different values of a) so the second method would not work (I think?) As you suspect this is of course a simplification of what I'm doing, I'm not actually creating partial functions for adding a constant to a value.
pafcu
@pafcu: What is it that you are actually trying to do? =P
Mike Boers
@Mike Boers: Generate bindings for an application at runtime. Basically I get a file containing "function signatures" and I generate methods that send commands to the application. I have a base function ("f") which gets a command ("a") and sends it to an external application. The base function need access to a file descriptor to the app stored in the class instance (so needs "self"). Due to partial application the function gets turned into a method that sends the correct command when called. I hope this satisfies your curiosity :-)
pafcu
@pacfu: Do we have to generate a set of functions for each instance, or is it permissable to do it for a class? I'll go whip up an example...
Mike Boers