2008-12-11 18 views
6

Debe haber una manera fácil de hacer esto, pero de alguna manera puedo entenderlo. La mejor forma en que puedo describir lo que quiero es una función lambda para una clase. Tengo una biblioteca que espera como argumento una versión desinstalada de una clase para trabajar. A continuación, ejemplifica la clase en sí para trabajar. El problema es que me gustaría poder crear versiones dinámicas de la clase, pasarlas a la biblioteca, pero no puedo entender cómo hacerlo, ya que la biblioteca espera una versión desinstalada. El código siguiente describe el problema:Función lambda para clases en python?

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) 

no puedo usar la clase genérica Multiplicar, porque debo instanciarlo primero que rompe la biblioteca "AttributeError: Multiplicar instancia no tiene llamada método". Sin cambiar la clase de la Biblioteca, ¿hay alguna forma de que pueda hacer esto?

Respuesta

8

No hay necesidad de lambda en absoluto. lambda es solo azúcar sintáctico para definir una función y usarla al mismo tiempo. Al igual que cualquier llamada lambda puede ser reemplazada por una def explícita, podemos resolver su problema creando una clase real que satisfaga sus necesidades y la devuelva.

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) 
+0

La ventaja de usar un convertidor general, por supuesto, es que aceptaría en principio, cualquier función de un solo arg, liberando implementaciones adicionales de la necesidad de definir explícitamente sus clases –

+0

me gusta la función createMultiplier(). Elegante. – Deestan

+0

Sé que esta es una publicación anterior, pero aquí hay algo que vale la pena señalar. createMultiplier (4)! = createMultiplier (4). Esto se debe a que la clase se crea dos veces en dos ubicaciones de memoria diferentes.A menos que Multiply tenga una metaclase que anule el comportamiento predeterminado para comparar clases, debe crear un diccionario simple para almacenar en caché los objetos de clase producidos. El dict estaría codificado por n, y los valores serían las clases. Pero esto puede no ser eficiente desde el punto de vista de la memoria si se crean un grupo de clases pero no se utilizan. La forma más sencilla aquí es adjuntar un atributo de clase a Multiplicar. P.ej. Multiplicar.x = 5. –

1

Esta es una especie de trampa, pero se puede dar a su clase Multiplicar un método que devuelve __call__ sí:

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

De esta forma cuando la biblioteca llama c() lo que realmente llama c.__call__() que devuelve el objeto que desee.

1
def mult(x): 
    def f(): 
     return Multiply(x) 
    return f 


op3 = mult(5) 
lib3 = Library(op3) 
print lib3.Op(2) 
11

¿La biblioteca realmente especifica que quiere una "versión no inicializada" (es decir, una referencia de clase)?

Me parece que la biblioteca realmente quiere una fábrica de objetos. En ese caso, es aceptable para escribir:

lib3 = Library(lambda: Multiply(5)) 

Para entender cómo funciona el lambda, considere lo siguiente:

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

Si entiendo su espacio problema correctamente, que tienen una interfaz general que toma 1 argumento que se llama utilizando la clase Library. Desafortunadamente, en lugar de llamar a una función, Library asume que la función está envuelta en una clase con un método run.

Ciertamente puede crear estas clases programáticamente. Las clases pueden devolverse por métodos y, gracias al concepto de cierres, debe poder ajustar cualquier función en una clase que satisfaga sus necesidades. Algo así como:

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)) 

Dicho esto, el cambio de la Biblioteca de tomar una función y luego proporcionar funciones es probablemente la manera más fuerte para hacer esto.