tags:

views:

759

answers:

5

There must be an easy way to do this, but somehow I can wrap my head around it. The best way I can describe what I want is a lambda function for a class. I have a library that expects as an argument an uninstantiated version of a class to work with. It then instantiates the class itself to work on. The problem is that I'd like to be able to dynamically create versions of the class, to pass to the library, but I can't figure out how to do it since the library expects an uninstantiated version. The code below describes the problem:

class Double:
    def run(self,x):
        return x*2

class Triple:
    def run(self,x):
        return x*3

class Multiply:
    def __init__(self,mult):
        self.mult = mult
    def run(self,x):
        return x*self.mult

class Library:
    def __init__(self,c):
        self.c = c()
    def Op(self,val):
        return self.c.run(val)

op1 = Double
op2 = Triple
#op3 = Multiply(5)

lib1 = Library(op1)
lib2 = Library(op2)
#lib3 = Library(op3)

print lib1.Op(2)
print lib2.Op(2)
#print lib3.Op(2)

I can't use the generic Multiply class, because I must instantiate it first which breaks the library "AttributeError: Multiply instance has no call method". Without changing the Library class, is there a way I can do this?

+1  A: 

This is sort of cheating, but you could give your Multiply class a __call__ method that returns itself:

class Multiply:
    def __init__(self,mult):
        self.mult = mult
    def __call__(self):
        return self
    def run(self,x):
        return x*self.mult

That way when the library calls c() it actually calls c.__call__() which returns the object you want.

Greg Hewgill
+1  A: 
def mult(x):
    def f():
        return Multiply(x)
    return f


op3 = mult(5)
lib3 = Library(op3)
print lib3.Op(2)
Moe
+9  A: 

Does the library really specify that it wants an "uninitialized version" (i.e. a class reference)?

It looks to me as if the library actually wants an object factory. In that case, it's acceptable to type:

lib3 = Library(lambda: Multiply(5))

To understand how the lambda works, consider the following:

Multiply5 = lambda: Multiply(5)
assert Multiply5().run(3) == Multiply(5).run(3)
Deestan
+1  A: 

If I understand your problem space correctly, you have a general interface that takes 1 argument which is called using the Library class. Unfortunately, rather than calling a function, Library assumes that the function is wrapped in a class with a run method.

You can certainly create these classes programatically. Classes may be returned by methods, and thanks to the concept of closures you should be able to wrap any function in a Class that meets your needs. Something like:

def make_op(f):
  class MyOp(object):
    def run(self, x):
      return f(x)
  return MyOp

op1 = make_op(lambda x: return x*2)
op2 = make_op(lambda x: return x*3)

def multiply_op(y):
    return make_op(lambda x: return x*y)

op3 = multiply_op(3)

lib1 = Library(op1)
lib2 = Library(op2)
lib3 = Library(op3)

print( lib1.Op(2) )
print( lib2.Op(2) )
print( lib3.Op(2) )

That being said, changing Library to take a function and then providing functions is probably the stronger way to do this.

Greg Case
+4  A: 

There's no need for lambda at all. lambda is just syntatic sugar to define a function and use it at the same time. Just like any lambda call can be replaced with an explicit def, we can solve your problem by creating a real class that meets your needs and returning it.

class Double:
        def run(self,x):
            return x*2

class Triple:
    def run(self,x):
        return x*3

def createMultiplier(n):
    class Multiply:
        def run(self,x):
            return x*n
    return Multiply

class Library:
    def __init__(self,c):
        self.c = c()
    def Op(self,val):
        return self.c.run(val)

op1 = Double
op2 = Triple
op3 = createMultiplier(5)

lib1 = Library(op1)
lib2 = Library(op2)
lib3 = Library(op3)

print lib1.Op(2)
print lib2.Op(2)
print lib3.Op(2)
Parker
The advantage of using a general converter, of course, is that it would accept in principle any one-arg function, freeing further implementations from needing to explicitly define their Classes
Greg Case
I like the createMultiplier() function. Elegant.
Deestan
I know this is an old post, but here is something worth noting. createMultiplier(4) != createMultiplier(4). This is because the class is created twice in two different memory locations. Unless Multiply has a metaclass that overrides the default behaviour for comparing classes, you should create a simple dictionary to cache the produced class objects. The dict would be keyed by n, and the values would be the classes. But this may not be memory efficient if a bunch of classes are created but unused. Simpliest way here is to attach a class attribute to Multiply. E.g. Multiply.x = 5.
MTsoul