假设我已定义:
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”def返回的那个。
可能很重要的是,这只是一个基本的例子,但我希望对许多应用程序使用to_class,例如在将“插入”类“构造函数”之前修改返回值;并且我希望将它用作其他类方法的装饰器。
我确信有人认为“to_class”装饰者毫无意义;操作可以在装饰方法中完成。虽然,我发现它很方便,它有助于我提高可读性。
最后,我想补充一点,由于实际原因,我感兴趣的是20%,研究原因使我感兴趣的是80%,因为我发现这是我对Python中的装饰器一般不太了解的事情。
答案 0 :(得分: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')
装饰师的通用版本并不容易;你的难题唯一的其他解决方法是使用元类黑客攻击;请参阅another answer of mine,我将更详细地描述该方法。
你基本上需要进入class-under-construction命名空间,设置一个临时元类,然后依赖于你的装饰器工作之前至少有一个类的实例;临时元类方法挂钩到类创建机制,以便稍后检索构造的类。
看到你正在使用这个装饰器作为替代类工厂,但可能不将是理想的;如果有人使用你的装饰函数来专门创建类实例,则会调用元类太晚了。
答案 1 :(得分:3)
好吧,你忘了这个类是传递给用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')
答案 2 :(得分: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>