2016-08-18 3 views
2

Выполняя приведенный ниже код, я получаю AttributeError: attribute '__doc__' of 'type' objects is not writable.functools.wrapper - AttributeError: атрибут '__doc__' объектов 'type' не доступен для записи

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 

Даже если строка документации предусмотрено, я не знаю, что случилось с этим.

Это прекрасно работает, если не завернуто, но мне нужно это сделать.

+0

Это не является причиной этой конкретной проблемы, но вы пытаетесь применить декоратор к функции, которая принимает аргументы _zero_, а не одну или несколько, поэтому doc-строка 'memoize()' представляется неправильной. – martineau

ответ

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) или использовать закрытие для управления кешем результатов, а не экземпляром класса, декоратор сможет скопировать docstring из базовой функции.

0

Декоратор wraps, который вы пытаетесь применить к вашему классу, не работает, потому что вы не можете изменить docstring класса после его создания. Вы можете воссоздать ошибку с этим кодом:

class Foo(object): 
    """inital docstring""" 

Foo.__doc__ = """new docstring""" # raises an exception in Python 2 

Исключение не происходит в Python 3 (я точно не знаю, почему это изменилось).

Обходной может быть, чтобы назначить переменную класса __doc__ в своем классе, а не с помощью 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__ однако должен быть установлен после того, как будет создан класс (вы не можете назначить его в определении класса):

class Foo(object): 
    __name__ = "Bar" # this has no effect 

Foo.__name__ = "Bar" # this works 
0

functools.wraps() был разработан, чтобы обернуть функцию, а не объекты класса. Одна из вещей, которую он делает, - это попытаться присвоить строку завершенной (оригинальной) функции __doc__ функции-обертки, которая, как вы обнаружили, не разрешена в Python 2. Она также делает то же самое для __name__ и __module__ атрибутов.

Простой способ обойти это ограничение осуществляется вручную, когда задан класс MemoDict. Вот что я имею в виду. (Примечание для повышения удобочитаемости я всегда использую имена классов CamelCase согласно PEP 8 - Style Guide for Python Code.)

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! 

 Смежные вопросы

  • Нет связанных вопросов^_^