functools.wrapper - AttributeError:'type'对象的属性'__doc__'不可写

时间:2016-08-18 05:06:40

标签: python-2.7 decorator functools dictionary-missing

执行下面的代码时,我收到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

回溯:

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

即使提供了doc字符串,我也不知道这有什么问题。

如果没有包裹它可以正常工作,但我需要这样做。

3 个答案:

答案 0 :(得分:1)

@wraps(f)主要用作函数装饰器,而不是用作类装饰器,因此使用它作为后者可能会导致偶尔出现奇怪的怪癖。

您收到的具体错误消息与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

如果您使用Python 3,请切换到Python 2中的经典类(通过继承自UserDict.UserDict而不是dict内置),或使用闭包来管理结果缓存而不是类例如,装饰器将能够从底层函数复制文档字符串。

答案 1 :(得分:1)

functools.wraps()旨在包装函数,而不是类对象。它所做的一件事就是尝试将包装(原始)函数的__doc__字符串分配给包装函数,正如您所发现的那样,它在Python 2中是不允许的。它也是这样做的。适用于__name____module__属性。

解决此限制的一种简单方法是在定义MemoDict时手动执行此操作。这就是我的意思。 (注意为了提高可读性,我总是根据PEP 8 - Style Guide for Python Code使用CamelCase类名。)

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__
        __name__ = f.__name__
        __module__ = f.__module__

        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"""
    print('Hello world!')

print(a.__doc__)     # -> blah
print(a.__name__)    # -> a
print(a.__module__)  # -> __main__
a()                  # -> Hello world!

事实上,如果你愿意,你可以创建自己的包装/类装饰功能来实现它:

def wrap(f):
    """ Convenience function to copy function attributes to derived class. """
    def class_decorator(cls):
        class Derived(cls):
            __doc__ = f.__doc__
            __name__ = f.__name__
            __module__ = f.__module__
        return Derived

    return class_decorator

def memoize(f):
    """ Memoization decorator for functions taking one or more arguments.
        Saves repeated api calls for a given value, by caching it.
    """
    @wrap(f)
    class MemoDict(dict):
        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"""
    print('Hello world!')

print(a.__doc__)     # -> blah
print(a.__name__)    # -> a
print(a.__module__)  # -> __main__
a()                  # -> Hello world!

答案 2 :(得分:0)

您尝试应用于类的 //try this.... if (!e.Day.IsOtherMonth)//check whether the day is in selected month or not { // 2nd Saturday must be before the 15th day and after 7th of the month...,so check the condition if (e.Day.Date.DayOfWeek == DayOfWeek.Saturday && e.Day.Date.Day > 7 && e.Day.Date.Day < 15) { e.Cell.ForeColor = System.Drawing.Color.Red;//set the Day in Red colour e.Cell.ToolTip = "Second Saturday";//set the tooltip as 'Second saturday' } } 装饰器不起作用,因为在创建类之后无法修改类的docstring。您可以使用以下代码重新创建错误:

wraps

Python 3中不会发生异常(我不确定为什么会发生变化)。

解决方法可能是在类中分配类变量class Foo(object): """inital docstring""" Foo.__doc__ = """new docstring""" # raises an exception in Python 2 ,而不是在类存在后使用__doc__设置docstring:

wraps

这不会复制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) 尝试复制的任何其他属性(如wraps等)。如果它们对您很重要,您可能想要自己解决这些问题。但是,在创建类之后需要设置__name__属性(您无法在类定义中指定它):

__name__