4

是否可以编写一个同时创建许多属性的装饰器?

喜欢而不是写作

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)

是否可以?你推荐它吗?

4

4 回答 4

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')

whereforward_property返回一个适当地实现描述符协议的属性对象。这对文档和其他静态分析工具以及(通常)读者更友好。

于 2012-08-06T12:51:24.877 回答
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.

于 2012-08-06T12:52:47.117 回答
2

对于您可能真正想要的内容,语言中的一项规定是为 __setattr__类编写方法。

每当在实例上访问通常不存在的属性时,就会调用此方法:

>>> class Test(object):
...    a = 0
...    def __getattr__(self, attr):
...       return attr
... 
>>> t = Test()
>>> t.a
0
>>> t.b
'b'
>>> t.c
'c'

您直接询问的内容也是可能的,但需要一些技巧 - 尽管常识不建议这样做,但在野外生产中被广泛使用。也就是说,对于 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())
于 2012-08-06T13:01:53.937 回答
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
于 2012-08-06T13:05:28.447 回答