带参数的Python装饰器
我有一个类具有许多非常相似的属性:带参数的Python装饰器,python,python-3.x,decorator,Python,Python 3.x,Decorator,我有一个类具有许多非常相似的属性: class myClass(object): def compute_foo(self): return 3 def compute_bar(self): return 4 @property def foo(self): try: return self._foo except AttributeError:
class myClass(object):
def compute_foo(self):
return 3
def compute_bar(self):
return 4
@property
def foo(self):
try:
return self._foo
except AttributeError:
self._foo = self.compute_foo()
return self._foo
@property
def bar(self):
try:
return self._bar
except AttributeError:
self._bar = self.compute_bar()
return self._bar
...
所以我想我会写一个装饰师来做属性定义的工作
class myDecorator(property):
def __init__(self, func, prop_name):
self.func = func
self.prop_name = prop_name
self.internal_prop_name = '_' + prop_name
def fget(self, obj):
try:
return obj.__getattribute__(self.internal_prop_name)
except AttributeError:
obj.__setattr__(self.internal_prop_name, self.func(obj))
return obj.__getattribute__(self.internal_prop_name)
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.func is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
class myClass(object):
def compute_foo(self):
return 3
foo = myDecorator(compute_foo, 'foo')
def compute_bar(self):
return 4
bar = myDecorator(compute_bar, 'bar')
这很好,但是当我想使用@myDecorator('foo')
语法时,它会变得更复杂,并且无法理解\uuuuuu调用\uuuuu
方法应该返回什么以及如何将属性附加到其类
目前我有:
class myDecorator(object):
def __init__(self, prop_name):
self.prop_name = prop_name
self.internal_prop_name = '_' + prop_name
def __call__(self, func):
self.func = func
return #???
def fget(self, obj):
try:
return obj.__getattribute__(self.internal_prop_name)
except AttributeError:
obj.__setattr__(self.internal_prop_name, self.func(obj))
return obj.__getattribute__(self.internal_prop_name)
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.func is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
class myClass(object):
@myDecorator('foo')
def compute_foo(self):
return 3
c = myClass()
print(c.foo)
它返回:
AttributeError:“myClass”对象没有属性“foo”
如果要使用@decorator
语法,则无法将属性重新映射到类上的其他名称。这意味着您的compute\ux
方法必须重命名为与属性相同的名称
编辑:可以重新映射名称,但也需要使用类装饰器
class MyProperty(property):
def __init__(self, name, func):
super(MyProperty, self).__init__(func)
self.name = name
self.internal_prop_name = '_' + name
self.func = func
def fget(self, obj):
try:
return obj.__getattribute__(self.internal_prop_name)
except AttributeError:
obj.__setattr__(self.internal_prop_name, self.func(obj))
return obj.__getattribute__(self.internal_prop_name)
def __get__(self, obj, objtype=None)
if obj is None:
return self
if self.func is None:
raise AttributeError('unreadable')
return self.fget(obj)
def myproperty(*args)
name = None
def deco(func):
return MyProperty(name, func)
if len(args) == 1 and callable(args[0]):
name = args[0].__name__
return deco(args[0])
else:
name = args[0]
return deco
class Test(object):
@myproperty
def foo(self):
return 5
def clsdeco(cls):
for k, v in cls.__dict__.items():
if isinstance(v, MyProperty) and v.name != k:
delattr(cls, k)
setattr(cls, v.name, v)
return cls
@clsdeco
class Test(...)
@myproperty('foo')
def compute_foo(self):
pass
如果没有类decorator,name参数唯一相关的时间是如果内部变量名与函数名不同,那么
@myproperty('foobar')
def foo(self):
return 5
它将查找\u foobar
而不是\u foo
,但属性名称仍然是foo
然而,有一种方法可以重新映射属性名,但也必须使用类装饰器
class MyProperty(property):
def __init__(self, name, func):
super(MyProperty, self).__init__(func)
self.name = name
self.internal_prop_name = '_' + name
self.func = func
def fget(self, obj):
try:
return obj.__getattribute__(self.internal_prop_name)
except AttributeError:
obj.__setattr__(self.internal_prop_name, self.func(obj))
return obj.__getattribute__(self.internal_prop_name)
def __get__(self, obj, objtype=None)
if obj is None:
return self
if self.func is None:
raise AttributeError('unreadable')
return self.fget(obj)
def myproperty(*args)
name = None
def deco(func):
return MyProperty(name, func)
if len(args) == 1 and callable(args[0]):
name = args[0].__name__
return deco(args[0])
else:
name = args[0]
return deco
class Test(object):
@myproperty
def foo(self):
return 5
def clsdeco(cls):
for k, v in cls.__dict__.items():
if isinstance(v, MyProperty) and v.name != k:
delattr(cls, k)
setattr(cls, v.name, v)
return cls
@clsdeco
class Test(...)
@myproperty('foo')
def compute_foo(self):
pass
这将遍历类上的所有属性,找到
MyProperty
实例,并检查集合名称是否与映射名称相同,如果不相同,则将属性重新绑定到传递到MyProperty
装饰器的名称 始终可以使用wrapps技巧将参数传递给decorator,如下所示:
from functools import wraps
class myDecorator(property):
def __init__(self, prop_name):
self.prop_name = prop_name
def __call__(self, wrappedCall):
@wraps(wrappedCall)
def wrapCall(*args, **kwargs):
klass = args[0]
result = wrappedCall(*args, **kwargs)
setattr(klass, self.prop_name, result)
return wrapCall
class myClass(object):
@myDecorator('foo')
def compute_foo(self):
return 3
c = myClass()
c.compute_foo()
print c.foo
我最终得到了一个元类,以使子类化更容易。感谢Brendan Abel在这方面的暗示
import types
class PropertyFromCompute(property):
def __init__(self, func):
self.func = None
self.func_name = func.__name__
self.internal_prop_name = self.func_name.replace('compute', '')
def fget(self, obj):
try:
return obj.__getattribute__(self.internal_prop_name)
except AttributeError:
obj.__setattr__(self.internal_prop_name, self.func())
return obj.__getattribute__(self.internal_prop_name)
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.func is None:
try:
self.func = obj.__getattribute__(self.func_name)
except AttributeError:
raise AttributeError("unreadable attribute")
return self.fget(obj)
class WithPropertyfromCompute(type):
def __new__(cls, clsname, bases, dct):
add_prop = {}
for name, obj in dct.items():
if isinstance(obj, types.FunctionType) and name.startswith('compute_'):
add_prop.update({name.replace('compute_',''): PropertyFromCompute(obj)})
dct.update(add_prop)
return super().__new__(cls, clsname, bases, dct)
class myClass(object, metaclass=WithPropertyfromCompute):
def compute_foo(self):
raise NotImplementedError('Do not instantiate the base class, ever !')
class myChildClass(myClass):
def compute_foo(self):
return 4
base = myClass()
try:
print(base.foo)
except NotImplementedError as e:
print(e)
print(myClass.foo)
child = myChildClass()
print(child.foo)
我越看它,我就越相信我想要达到的目标是疯狂的。。。
myDecorator
能知道什么是myClass
向其添加属性吗?除非调用了\uuuu get\uuuu()
等等。谢谢。如果我理解正确,那么在本例中使用@myDecorator('foo')
语法是没有意义的。类属性foo
应该在类定义中定义,而myDecorator
应该被调用myCustomProperty
?您不应该将修饰函数作为参数传递给decorator。这可以通过使用包装器来解决。这很有意义。非常感谢你的例子。实际上,我将compute\u foo
作为一个单独的函数,以便在子类化时能够覆盖它。所以我可能会坚持单独定义class属性。我喜欢类装饰器的想法。那太好了!我假设我需要对测试的所有子类应用相同的装饰器,它才能工作?是的,它需要对所有子类应用。如果使用元类而不是类装饰器,则可以避免添加所有子类。谢谢,我想到了类似的方法,但我不熟悉functools.wrapps
。我也将对此进行调查。