【问题标题】:Python - using decorator.py to preserve a method docstringPython - 使用 decorator.py 保存方法文档字符串
【发布时间】:2013-06-14 03:36:45
【问题描述】:

我有兴趣将实例方法作为类方法和实例方法来调用。这可以通过使用 class_or_instance 装饰器来完成,如下所示:

class class_or_instance(object):
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, obj, cls):
        if obj is not None:
            return lambda *args, **kwds: self.fn(obj, *args, **kwds)
        else:
            return lambda *args, **kwds: self.fn(cls, *args, **kwds)

class A(object):
    @class_or_instance
    def func1(self,*args):
         # method body

现在我可以将 func1 称为 A.func1(*args)A().func1(*args)。 然而,当这样做时,func1 的文档字符串消失了。解决这个问题的一种方法是使用来自decorator.py 的装饰器,但我无法让它与作为类而不是函数的装饰器一起使用。有什么建议可以解决这个问题吗?

编辑:functools.wraps() 在这种情况下将无法正常工作。请参阅 stackoverflow 上的 related question

【问题讨论】:

  • 为什么不使用@classmethod 或@staticmethod?
  • @classmethod 只允许它作为类方法运行。我希望能够将其称为类或实例方法
  • 如果在实例上调用类方法,第一个参数不是对实例的引用(“self”),而是对它的类(“cls”)的引用。与静态方法的唯一区别是缺少第一个“cls”参数,但除此之外它们的行为相同。

标签: python oop decorator docstring


【解决方案1】:

基本描述符/装饰器

你只需要记住应该装饰哪个函数。您的函数是在__get__ 中创建的,因此将包装器用作装饰器无济于事,相反,您需要在__get__ 方法中应用它。顺便说一句,您可以为此使用functools.update_wrapperdecorators.decorator。它们的工作方式非常相似,只是您必须保留decorators.decorator 的结果,而functools.update_wrapper 返回None。两者都有签名f(wrapper, wrapped)

from functools import update_wrapper
class class_or_instance(object):
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, obj, cls):
        if obj is not None:
            f = lambda *args, **kwds: self.fn(obj, *args, **kwds)
        else:
            f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
        # update the function to have the correct metadata
        update_wrapper(f, self.fn)
        return f

class A(object):
    @class_or_instance
    def func1(self,*args):
        """some docstring"""
        pass

现在如果你这样做:

print A.func1.__doc__

你会看到“一些文档字符串”。耶!


缓存属性装饰器

这里的关键是您只能影响返回的内容。由于class_or_instance 实际上并没有用作函数,所以你用它做什么并不重要。请记住,此方法会导致函数每次都被反弹。我建议你添加一点魔法,并在第一次调用后绑定/缓存函数,这实际上只涉及添加一个setattr 调用。

from functools import update_wrapper
import types

class class_or_instance(object):
    # having optional func in case is passed something that doesn't have a correct __name__
    # (like a lambda function)
    def __init__(self, name_or_func):
        self.fn = fn
        self.name = fn.__name__

    def __get__(self, obj, cls):
        print "GET!!!"
        if obj is not None:
            f = lambda *args, **kwds: self.fn(obj, *args, **kwds)
            update_wrapper(f, self.fn)
            setattr(obj, self.name, types.MethodType(f, obj, obj.__class__))
        else:
            f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
            update_wrapper(f, self.fn)
        return f

然后我们可以测试一下...neato:

A.func1 #GET!!!
obj = A()
obj.func1 #GET!!!
obj.func1 is obj.func1 # True
A.func1 # GET!!!
obj2 = A()
obj2.func1 is not obj.fun1 # True + GET!!!

【讨论】:

  • 感谢您建议的两种技术都能正确返回文档字符串。然而,使用decorator.decorator 时传递给装饰方法的参数数量存在问题,并且当调用该方法时,它会抱怨TypeError。另一方面,update_wrapper 技术可以顺利运行。我想知道第一种技术有什么问题?
  • @rk7 所以decorators.decoratorfunctools.update_wrapper 设置为相互兼容。我不认为你可以使用decorators.decorator,因为它没有设置为处理方法(将 self 作为第一个参数)......这需要很多黑客才能做到......不确定它是否值得它。
猜你喜欢
  • 2012-04-08
  • 1970-01-01
  • 2015-11-15
  • 2010-09-09
  • 1970-01-01
  • 2011-12-27
  • 2013-12-27
  • 2014-02-12
  • 1970-01-01
相关资源
最近更新 更多