While executing the code below, I'm getting AttributeError: attribute '__doc__' of 'type' objects is not writable
.
from functools import wraps
def memoize(f):
""" Memoization decorator for functions taking one or more arguments.
Saves repeated api calls for a given value, by caching it.
"""
@wraps(f)
class memodict(dict):
"""memodict"""
def __init__(self, f):
self.f = f
def __call__(self, *args):
return self[args]
def __missing__(self, key):
ret = self[key] = self.f(*key)
return ret
return memodict(f)
@memoize
def a():
"""blah"""
pass
Traceback:
AttributeError Traceback (most recent call last)
<ipython-input-37-2afb130b1dd6> in <module>()
17 return ret
18 return memodict(f)
---> 19 @memoize
20 def a():
21 """blah"""
<ipython-input-37-2afb130b1dd6> in memoize(f)
7 """
8 @wraps(f)
----> 9 class memodict(dict):
10 """memodict"""
11 def __init__(self, f):
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.pyc in update_wrapper(wrapper, wrapped, assigned, updated)
31 """
32 for attr in assigned:
---> 33 setattr(wrapper, attr, getattr(wrapped, attr))
34 for attr in updated:
35 getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
AttributeError: attribute '__doc__' of 'type' objects is not writable
Even though the doc string is provided, I don't know what's wrong with this.
It's works fine if not wrapped, but I need to do this.
@wraps(f)
is primarily designed to be used as a function decorator, rather than as a class decorator, so using it as the latter may lead to the occasional odd quirk.
The specific error message you're receiving relates to a limitation of builtin types on Python 2:
>>> class C(object): pass
...
>>> C.__doc__ = "Not allowed"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute '__doc__' of 'type' objects is not writable
If you use Python 3, switch to a classic class in Python 2 (by inheriting from UserDict.UserDict
rather than the dict
builtin), or use a closure to manage the result cache rather than a class instance, the decorator will be able to copy the docstring over from the underlying function.
The wraps
decorator you're trying to apply to your class doesn't work because you can't modify the docstring of a class after it has been created. You can recreate the error with this code:
class Foo(object):
"""inital docstring"""
Foo.__doc__ = """new docstring""" # raises an exception in Python 2
The exception doesn't occur in Python 3 (I'm not exactly sure why it's changed).
A workaround might be to assign the class variable __doc__
in your class, rather than using wraps
to set the docstring after the class exists:
def memoize(f):
""" Memoization decorator for functions taking one or more arguments.
Saves repeated api calls for a given value, by caching it.
"""
class memodict(dict):
__doc__ = f.__doc__ # copy docstring to class variable
def __init__(self, f):
self.f = f
def __call__(self, *args):
return self[args]
def __missing__(self, key):
ret = self[key] = self.f(*key)
return ret
return memodict(f)
This won't copy any of the other attributes that wraps
tries to copy (like __name__
, etc.). You may want to fix those up yourself if they're important to you. The __name__
attribute however needs to be set after the class is created (you can't assign it in the class definition):
class Foo(object):
__name__ = "Bar" # this has no effect
Foo.__name__ = "Bar" # this works
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With