2012-08-14 13 views
7

He encontrado un mensaje de error bastante críptico (al menos) al intentar usar un decorador para actualizar el contenedor de una función. ¿Alguna idea de cómo podría remediar esto?Error al usar el decorador para actualizar el contenedor

He tratado de hacer que mi código sea lo más general posible, por lo que se aplicará a otras situaciones también.

def decorator(d): 
    """Make function d a decorator: d wraps a function fn.""" 

    def _d(fn): 
     return functools.update_wrapper(d(fn), fn) 
    functools.update_wrapper(_d, d) 
    return _d 


@decorator 
def f(fn): 
    """Converts the string fn to a function and returns it. 
    Because of the @decorator decorator, _f.__name__ should 
    be identical to f.__name__""" 

    f.__name__ = fn 
    def _f(fn): 
     return eval(fn) 
    return _f 

g = f('x**2') 
print g.__name__ 

salida deseada:

>>>x**2 

salida real:

Traceback (most recent call last): 
    File "C:\python\swampy-2.0\testcode.py", line 18, in <module> 
    g = f('x**2') 
    File "C:\python\swampy-2.0\testcode.py", line 6, in _d 
    return functools.update_wrapper(d(fn), fn) 
    File "C:\Python27\lib\functools.py", line 33, in update_wrapper 
    setattr(wrapper, attr, getattr(wrapped, attr)) 
AttributeError: 'str' object has no attribute '__module__' 

Respuesta

5

Un decorador tiene una función como argumento y devuelve otra función "decorado". Está pasando una cadena e intentando devolver una función que realmente es una fábrica de funciones. functools.wraps y functools.update_wrapper esperan una función. Un objeto de función tendría un atributo __module__, mientras que las instancias de str no tienen un atributo __module__.

¿Desea generar una función a partir de la cadena "x ** 2"?

Su implementación de decorator es innecesaria. Sólo tiene que utilizar functools.wraps:

def f(fn): 
    """Converts the string fn to a function and returns it.""" 
    @functools.wraps(fn) 
    def _f(fn): 
     return eval(fn) 
    return _f 

Sin embargo, no quieren un decorador en este caso, pero una fábrica de función.

def factory(exp): 
    def f(**kwargs): 
     return eval(exp, globals(), kwargs) 
    f.__name__ = exp 
    return f 

Ahora usted puede utilizar esto como esto:

>>> x_squared = factory("x**2") 
>>> x_squared(x=7) 
49 

Advertencia: El Cirujano General ha determinado que eval es peligroso para su salud

+0

Gracias por la respuesta! Sí, soy consciente de que eval debe usarse con extrema precaución :) En este caso, creo que me voy a quedar con eso. En este caso particular, es crítico que también almacene el exp en _f .__ name___. No parece que ninguna de estas soluciones brinde ese requisito. –

+1

Oh, puedes establecer 'f .__ name__'. Déjame actualizar mi respuesta. – stderr

+0

En este caso, lo que _f() realmente está haciendo realmente no es mi principal preocupación (debería haber dejado eso en claro en mi pregunta). Me preocupa principalmente actualizar el contenedor de funciones usando un decorador. –

Cuestiones relacionadas