2012-01-22 16 views
5

Python no es compatible con funciones anónimas complicadas. ¿Cuál es una buena alternativa? Por ejemplo:python: alternativa a las funciones anónimas

class Calculation: 
    def __init__(self, func): 
     self.func = func 

    def __call__(self, data): 
     try: 
     # check if the value has already been calculated 
     # if it has, it would be cached under key = self.func 
      return data[self.func] 
     except KeyError: 
      pass # first-time call; calculate and cache the values 
     data[self.func] = self.func(data) 
     return data[self.func] 

# with a simple function, which can be represented using lambda, this works great 
f1 = Calculation(lambda data : data['a'] * data['b']) 

# with a complicated function, I can do this: 
def f2_aux: 
    # some complicated calculation, which isn't suitable for a lambda one-liner 
f2 = Calculation(f2_aux) 

¿Es este un diseño razonable para empezar?

Si es así, ¿hay alguna manera de evitar la fealdad de f * _aux por cada f * que defino en el módulo?

UPDATE:

Ejemplo de uso:

d = {'a' : 3, 'b' : 6} 

# computes 3 * 6 
# stores 18 in d under a key <function <lambda> at ...> 
# returns 18 
f1(d) 

# retrieves 18 from d[<function <lambda> at ...>] 
# returns 18, without having to recalculate it 
f1(d) 

UPDATE:

Sólo por mi entendimiento, añadí una versión que utiliza la función interior.

def memoize(func): 
    def new_func(data): 
     try: 
     # check if the value has already been calculated 
     # if it has, it would be cached under key = self.func 
      return data[func] 
     except KeyError: 
      pass # first-time call; calculate and cache the values 
     data[func] = func(data) 
     return data[func] 
    return new_func 

@memoize 
def f1(data): 
    return data['a'] * data['b'] 
+5

No, este no es un diseño razonable. Parece que está haciendo memoialización (que no está relacionada con funciones anónimas) y lo está haciendo mal. 'f1 = lambda a: expression' es siempre exactamente igual que' def f1 (a): return expression'. [Este es un buen decorador de memoraciones para estudiar] (http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize). Por cierto, '@decorator def function: pass' es siempre exactamente lo mismo que' def function: pass; function = decorator (función) ' –

+2

Lo malo es que tu memoial está roto. El 'caché 'de datos de resultado también es el argumento de su función ... –

+0

@FrancisAvila: Tenía la intención de almacenar en caché el valor de' f (data) 'en el elemento del diccionario' data [f] '. Sé que los "datos" que se me pasan nunca utilizarán esa clave, y el conflicto con otro tampoco debería ocurrir, ya que el valor de otra función se almacenaría en caché con otra clave. Parece que funciona (técnicamente); es el problema solo en el código innecesariamente confuso? – max

Respuesta

5

No necesita funciones anónimas. Además, la memorización se ha hecho mejor que esto, probablemente no haya ninguna razón para que usted desarrolle la suya propia.

Pero para responder a la pregunta: Puede usar su clase como decorador.

@Calculation 
def f2(): 
    ... 

Esto simplemente define la función, lo envuelve en Calculation y se almacena el resultado de que, como f2. La sintaxis decorador se define que es equivalente a:

_decorator = Calculation # a fresh identifier 
# not needed here, but in other cases (think properties) it's useful 
def f2(): 
    ... 
f2 = _decorator(f2) 
+0

¿Entiendo correctamente que, aunque normalmente un decorador es una función que toma una función como argumento y devuelve otra función, en este caso, 'clase Cálculo' se ajusta a esta función debido a su firma de constructor y su método' __call__'? – max

+0

@max: no hay restricciones para los decoradores, excepto por supuesto que deben ser invocables, y para ser * útiles * deben tener un valor de retorno significativo. Las clases se pueden llamar a través de '__new__' /' __init__', y como las instancias de su clase son invocables (aunque '__call__'), el valor de retorno es útil. – delnan

+0

@max "a callable", en el lenguaje de Python. –

1

Un cierre puede ser una alternativa sucinta a escribir una clase como la que en su ejemplo. La técnica implica poner def dentro de otra def. La función interna puede tener acceso a la variable en la función adjunta. En Python 3, la palabra clave no local le da acceso de escritura a esa variable. En Python 2, debe usar un valor mutable para la variable no local para poder actualizarlo desde la función interna.

Sobre la cuestión relativa a funciones anónimas, el lenguaje intencionalmente empuja de nuevo a utilizar def para nada más complicado que una lambda puede manejar.

+0

¿Quiere decir algo como lo que agregué en la última actualización de mi pregunta? – max

+0

Sí, eso es un cierre. –

4

La alternativa a una función anónima es una función no anónima. Una función anónima es solo anónima en el contexto donde fue definida. Pero no es verdaderamente anónimo, porque entonces no podrías usarlo.

En Python realiza funciones anónimas con la instrucción lambda. Por ejemplo, puede hacer esto:

output = mysort(input, lambda x: x.lastname) 

La lambda va a crear una función, pero esa función no tiene nombre en el espacio local, y su propio nombre por sí mismo es sólo '<lambda>'. Pero si nos fijamos en mysort, tendría que definirse así:

def mysort(input, getterfunc): 
    blahblahblah 

Como vemos aquí, en este contexto, la función no es anónima en absoluto. Tiene un nombre, getterfunc.Desde el punto de vista de esta función, no importa si la función transferida es anónima o no. Esto funciona igual de bien, y es exactamente equivalente en todos los aspectos significativos:

def get_lastname(x): 
    return x.lastname 

output = mysort(input, get_lastname) 

Claro, se utiliza más código, pero no es más lento o algo por el estilo. En Python, por lo tanto, las funciones anónimas no son más que azúcar sintáctico para funciones ordinarias.

Una verdadera función anónima sería

lambda x: x.lastname 

Pero como no asignamos la función resultante a nada, que no recibe un nombre para la función, y entonces no puede utilizarlo. Todas las funciones verdaderamente anónimas son inutilizables.

Por esa razón, si necesita una función que no puede ser una lambda, conviértala en una función normal. Nunca puede ser anónimo de ninguna manera significativa, ¿por qué molestarse en que sea anónimo? Las lambdas son útiles cuando quieres una función pequeña de una línea y no quieres perder espacio definiendo una función completa. Que son anónimos son irrelevantes.

+0

Gracias ... Creo que solo estaba tratando de guardar la cantidad de código escrito (y de ahí el número de posibles errores tipográficos), lo cual, para empezar, no debería haber sido una gran preocupación. – max

Cuestiones relacionadas