4

假设我已经定义:

def to_class(cls):
  """ returns a decorator
  aimed to force the result to be of class cls. """
  def decorating_func(func):
    def wrapper(*args, **kwargs):
      return cls(func(*args, **kwargs))
    return wrapper
  return decorator(decorating_func)

我希望用它来创建装饰器,将函数结果转换为给定类的对象。但是,这不起作用:

class TestClass(object):
  def __init__(self, value):
    self._value = (value, value)

  def __str__(self):
    return str(self._value)

  @staticmethod
  @to_test_class
  def test_func(value):
    return value

to_test_class = to_class(TestClass)

因为 test_func 将寻找 to_test_class 并且不会找到它。另一方面,在类定义之前将赋值给 to_test_class 也会失败,因为尚未定义 TestClass。

尝试将 @to_class(TestClass) 放在 test_func 的定义之上也会失败,因为该方法是在类之前构造的(如果我没记错的话)。

我发现的唯一解决方法是将 to_test_class 手动定义为装饰器,而不是从一般的“to_class”定义返回。

值得一提的是,这只是一个基本示例,但我希望将 to_class 用于许多应用程序,例如在将返回值“插入”到类的构造函数之前对其进行修改;我也希望将它用作其他类方法的装饰器。

我相信有些人认为“to_class”装饰器毫无意义;相反,可以在装饰方法中进行操作。不过,我觉得它很方便,并且有助于提高可读性。

最后,我想补充一点,20% 是出于实际原因,80% 是出于学习原因,因为我发现这是我对 Python 中的装饰器一般不完全理解的东西。

4

3 回答 3

4

事实上,在类构建时,类对象本身还没有被构建,因此你不能将它用作装饰器的基础。

我能想到的一种解决方法是使用staticmethod装饰器。相反,在您自己的装饰器内部,重新使用classmethod装饰器。这样,您可以确保 Python 至少为您传递相关的类:

def to_class(func):
    """ returns a decorator
    aimed to force the result to be of class cls. """
    def wrapper(cls, *args, **kwargs):
        return cls(func(*args, **kwargs))
    return classmethod(wrapper)

然后像这样使用它:

class TestClass(object):
    def __init__(self, value):
        self._value = (value, value)

    def __str__(self):
        return str(self._value)

    @to_class
    def test_func(value):
        return value

示范:

>>> def to_class(func):
...     """ returns a decorator
...     aimed to force the result to be of class cls. """
...     def wrapper(cls, *args, **kwargs):
...         return cls(func(*args, **kwargs))
...     return classmethod(wrapper)
... 
>>> class TestClass(object):
...     def __init__(self, value):
...         self._value = (value, value)
...     def __str__(self):
...         return str(self._value)
...     @to_class
...     def test_func(value):
...         return value
... 
>>> TestClass.test_func('foo')
<__main__.TestClass object at 0x102a77210>
>>> print TestClass.test_func('foo')
('foo', 'foo')

装饰器的通用版本并不容易;解决您的难题的唯一其他解决方法是使用元类黑客;请参阅我的另一个答案,其中我更详细地描述了该方法。

您基本上需要进入类正在构建的命名空间,设置一个临时元类,然后在您的装饰器工作之前依赖该类的至少一个实例;临时元类方法与类创建机制挂钩,以便稍后检索构造的类。

但是,当您将此装饰器用作替代类工厂时,这可能并不理想;如果有人使用您的修饰函数专门创建类实例,则调用元类为时已晚。

于 2012-11-16T11:33:17.290 回答
3

好吧,你忘记了 class 是传递给使用 classmethod 修饰的方法的第一个参数,所以你可以这样写:

def to_this_class(func):
    def wrapped(cls, value):
        res = func(cls, value)
        return cls(res)
    return wrapped

class TestClass(object):
    def __init__(self, value):
        self._value = (value, value)

    def __str__(self):
        return str(self._value)

    @classmethod
    @to_this_class
    def test_func(cls, value):
        return value

x = TestClass('a')

print x.test_func('b')
于 2012-11-16T11:35:54.313 回答
0

问题是装饰器在定义它所装饰的东西时被评估,所以在定义方法时test_func(),装饰器to_test_class被调用,即使它已经存在,它应该处理的东西(类TestClass)还不存在(因为这个在创建所有方法之后创建)。

也许您可以在使用类的位置使用占位符,然后(在创建类之后)在占位符处填充该值(类)。

例子:

lazyClasses = {}
def to_lazy_class(className):
  """ returns a decorator
  aimed to force the result to be of class cls. """
  def decorating_func(func):
    def wrapper(*args, **kwargs):
      return lazyClasses[className](func(*args, **kwargs))
    return wrapper
  return decorating_func 

class TestClass(object):
  def __init__(self, value):
    self._value = (value, value)

  def __str__(self):
    return str(self._value)

  @staticmethod
  @to_lazy_class('TestClass')
  def test_func(value):
    return value

lazyClasses['TestClass'] = TestClass

>>> TestClass.test_func('hallo')
<__main__.TestClass object at 0x7f76d8cba190>
于 2012-11-16T11:33:08.520 回答