Декораторы, которые являются свойствами декорированных предметов? - PullRequest
1 голос
/ 23 июня 2009

Я хочу создать декоратор, который позволит мне вернуться к декорированному объекту и извлечь из него другой декоратор, так же, как вы можете использовать setter / deleter для свойств:

@property
def x(self):
    return self._x

@x.setter
def x(self, y):
    self._x = y

В частности, я хотел бы, чтобы он действовал в основном так же, как свойство, но эмулировал последовательность вместо одного значения. Вот мой первый выстрел, но, похоже, он не работает:

def listprop(indices):
    def dec(func):
        class c(object):
            def __init__(self, l):
                self.l = l
            def __getitem__(self, i):
                if not i in self.l:
                    raise Exception("Invalid item: " + i)
                return func(i)
            @staticmethod
            def setter(func):
                def set(self, i, val):
                    if not i in self.l:
                        raise Exception("Invalid item: " + i)
                    func(i, val)
                c.__setitem__ = set
        return c(indices)
    return dec

# ...
class P:
    @listprop(range(3))
    def prop(self, i):
        return get_prop(i)

    @prop.setter
    def prop(self, i, val):
        set_prop(i, val)

Я почти уверен, что c.__setitem__ = set неправильно, но я не могу понять, как получить ссылку на экземпляр в тот момент. Идеи?

Решение Алекса Мартелли работает на 2.6, но что-то в нем не работает на 2.4 и 2.5 (я бы предпочел, чтобы оно работало и на этих старых версиях, хотя это не является строго обязательным):

2,4

>>> p = P()
>>> p.prop
>>> p.prop[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unsubscriptable object

2,5

>>> p = P()
>>> p.prop
>>> p.prop[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is unsubscriptable

2,6

>>> p = P()
>>> p.prop
<__main__.c object at 0x017F5730>
>>> p.prop[0]
0

1 Ответ

3 голосов
/ 23 июня 2009

Я исправил много мелких деталей, и следующая версия, кажется, работает так, как вам нужно:

def listprop(indices):
    def dec(func):
        class c(object):
            def __init__(self, l, obj=None):
                self.l = l
                self.obj = obj
            def __get__(self, obj, cls=None):
                return c(self.l, obj)
            def __getitem__(self, i):
                if not i in self.l:
                    raise Exception("Invalid item: " + i)
                return func(self.obj, i)
            def setter(self, sfunc):
                def doset(self, i, val):
                    if not i in self.l:
                        raise Exception("Invalid item: " + i)
                    sfunc(self.obj, i, val)
                c.__setitem__ = doset
                return self
        result = c(indices)
        return result
    return dec

# ...
class P:
    @staticmethod
    def get_prop(i): return i*100

    @staticmethod
    def set_prop(i, val): print 'set %s to %s' % (i, val)

    @listprop(range(3))
    def prop(self, i):
        return self.get_prop(i)

    @prop.setter
    def prop(self, i, val):
        self.set_prop(i, val)

Как видите, присвоение c.__setitem__ не было проблемой - были другие, такие как c, в которых не было __get__ (поэтому это не был дескриптор, см. это руководство ) и setter возвращает None (таким образом, p.prop закончил как None).

...