Question

I've encountered a rather cryptic (to me at least) error message while trying to use a decorator to update a function's wrapper. Any ideas how I could remedy this?

I've tried to make my code as general as possible so it will apply to other situations as well.

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__

Desired output:

>>>x**2

Actual output:

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__'
Était-ce utile?

La solution

A decorator takes a function as an argument and returns another "decorated" function. You're passing a string and attempting to return a function which is really a function factory. functools.wraps and functools.update_wrapper expect a function. A function object would have a __module__ attribute while instances of str don't have an __module__ attribute.

Do you want to generate a function from the string "x**2"?

Your implementation of decorator is unnecessary. Just use 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

However, you don't want a decorator in this case but a function factory.

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

Now you can use this like this:

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

Warning: The Surgeon General Has Determined that eval is Dangerous to Your Health

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top