是否可以编写一次创建多个属性的装饰器?
喜欢而不是写作
class Test:
@property
def a(self):
return self.ref.a
@property
def b(self):
return self.ref.b
我想写
class Test:
@properties("a", "b")
def prop(self, name):
return getattr(self.ref, name)
有可能吗?你推荐它吗?
答案 0 :(得分:3)
回想一下装饰者
@decorator(dec_args)
def foo(args):
pass
只是写作的语法糖
def foo(args):
pass
foo = decorator(dec_args)(foo)
因此,方法装饰器不可能将多个方法(或属性等)添加到类中。
替代方法可能是注入属性的类装饰器:
def multi_property(prop, *names):
def inner(cls):
for name in names:
setattr(cls, name, property(lambda self, name=name: prop(self, name)))
return inner
@multi_property(prop, 'a', 'b')
class Test:
pass
然而,通常更清楚的是让每个属性都出现在课堂上:
a = forward_property('ref', 'a')
b = forward_property('ref', 'b')
其中forward_property
在适当时返回一个属性对象,实现描述符协议。这对于文档和其他静态分析工具以及(通常)读者来说更友好。
答案 1 :(得分:2)
为另一个对象编写代理的最简单方法是实现__getattr__()
:
class Proxy(object):
def __init__(self, ref1, ref2):
self._ref1 = ref1
self._ref2 = ref2
def __getatrr__(self, name):
if name in ["a", "b", "c"]:
return getattr(self._ref1, name)
if name in ["d", "e", "f"]:
return getattr(self._ref2, name)
请注意,__getattr__()
仅针对当前实例中找不到的属性进行调用,因此您还可以向Proxy
添加更多方法和属性。
答案 2 :(得分:2)
lagnuage中你可能真正意图的一个条款就是为一个类写__setattr__
方法。
只要在实例上访问通常不存在的属性,就会对此方法进行处理:
>>> class Test(object):
... a = 0
... def __getattr__(self, attr):
... return attr
...
>>> t = Test()
>>> t.a
0
>>> t.b
'b'
>>> t.c
'c'
你所要求的也是可能的,bute需要一些黑客 - 尽管不是常识,但在野外生产中被广泛使用。
也就是说,对于Python中存在的属性,它是一个特殊类型对象的类属性 - 至少具有__get__
方法的对象。 (要了解更多关于Python文档的“描述符协议”)。
现在,尝试一次创建多个属性,就像您通过示例粘贴的代码一样,将要求将属性名称注入到被调用函数的类名空间中。它_是可能的,甚至在Python中用于生产,甚至难以实现。但不是很漂亮。
因此,避免这种情况的一种可能方法是进行一个返回一系列“属性”对象的调用 - 它是干净的,可读的和可维护的:
class MultiProperty(object):
def __init__(self, getter, setter, name):
self.getter = getter
self.setter = setter
self.name = name
def __get__(self, instance, owner):
return self.getter(instance, self.name)
def __set__(self, instance, value):
return self.setter(instance, self.name, value)
def multi_property(mgetter, msetter, *args):
props = []
for name in args:
props.append(MultiProperty(mgetter, msetter, name))
return props
class Test(object):
def multi_getter(self, attr_name):
# isf desired, isnert some logic here
return getattr(self, "_" + attr_name)
def multi_setter(self, attr_name, value):
# insert some logic here
return setattr(self, "_" + attr_name, value)
a,b,c = multi_property(multi_getter, multi_setter, *"a b c".split())
答案 3 :(得分:0)
这在课堂上是不可能的。但是,您可以在以后修改该类。见这里:
def makeprop(meth, name):
# make a property calling the given method.
# It is not really a method, but it gets called with the "self" first...
return property(lambda self: meth(self, name))
def propfor(cls, *names):
def wrap(meth):
for name in names:
# Create a property for a given object, in this case self,
prop = makeprop(meth, name)
setattr(cls, name, prop)
return meth # unchanged
return wrap
class O(object):
# just a dummy for your ref
a = 9
b = 12
c = 199
class C(object):
ref = O()
# Put the wanted properties into the class afterwards:
@propfor(C, "a", "b", "c")
def prop(self, name):
return getattr(self.ref, name)
# alternative approach with a class decorator:
def propdeco(*names):
meth = names[-1]
names = names[:-1]
def classdeco(cls):
propfor(cls, *names)(meth) # not nice, but reuses code above
return cls
return classdeco
@propdeco("a", "b", "c", lambda self, name: getattr(self.ref, name))
class D(object):
ref = O()
print C().a
print C().b
print C().c
print D().a
print D().b
print D().c
如果您更喜欢第二种方法,则应将propdeco
写为
def propdeco(*names):
meth = names[-1]
names = names[:-1]
def classdeco(cls):
for name in names:
# Create a property for a given object, in this case self,
prop = makeprop(meth, name)
setattr(cls, name, prop)
return cls
return classdeco