簡體   English   中英

動態更新python屬性設置器方法

[英]Dynamically update python property setter method

我試圖在值上動態添加“可鎖定”功能。 盡管這個特殊情況看起來微不足道,甚至是人為的,但我想將可鎖定的混合類擴展為各種不同的用例。 我不想一次性設定可鎖定的值; 我希望它足夠通用,可以控制任何數量的類屬性。

完成后,我的期望是最后一個斷言將過去。

我曾嘗試使用超級代替自我。 setattr ,但是我收到一個錯誤,指出該屬性是只讀的。 這讓我想知道我是否還能做自己想做的事。

任何幫助將不勝感激,並在此先感謝!

一些代碼:

from collections import OrderedDict as OD


def lockable(func, locked=None):
    def wrapper(*args, **kwds):
        if locked:
            val = None
        else:
            val = func(*args, **kwds)
        return val
    return wrapper


class Mixin(object):

    @property
    def meta(self):
        attr = "__meta__"
        if not hasattr(self, attr):
            setattr(self, attr, OD())
        return getattr(self, attr)


class LockableMixin(Mixin):

    @property
    def locked(self):
        self.meta.setdefault("locked", False)
        return self.meta.get("locked")

    @locked.setter
    def locked(self, value):
        value = value if value in [None, True, False] else self.meta['locked']
        self.meta['locked'] = value

    def lock(self):
        self.locked = True

    def unlock(self):
        self.locked = False

    def is_locked(self):
        return self.locked

    def __init__(self):
        super(LockableMixin, self).__init__()
        self.__setattr__ = lockable(self.__setattr__, self.locked)


class Attribute(object):

    @property
    def value(self):
        attr = "__value__"
        if not hasattr(self, attr):
            setattr(self, attr, False)
        return getattr(self, attr)

    @value.setter
    def value(self, value):
        self.__value__ = value

    def __init__(self, value):
        self.value = value
        super(Attribute, self).__init__()

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = value

    def __str__(self):
        return str(self.value)

    def __repr__(self):
        cname = self.__class__.__name__
        value = str(self.value)
        return "<%s %s>" % (cname, value)


class LockableAttribute(Attribute, LockableMixin):
    pass

if __name__ == "__main__":
    a1 = Attribute(1)
    a2 = LockableAttribute(1)
    assert a2.locked is False
    assert a2.value == 1
    a2.lock()
    assert a2.locked is True
    a2.unlock()
    assert a2.locked is False
    a2.value = 2
    assert a2.value == 2
    a2.locked = True
    a2.value = 3
    assert a2.value == 2    # This will raise an exception, but it shouldn't.

這是組件類的更多用例:

class Component(object):

    @property
    def attributes(self):
        attrs = {}
        for field in self.__fields__:
            attrs[field] = self.get(field)
        return attrs

    def __init__(self, **attributes):
        super(Component, self).__init__()
        self.__fields__ = []
        for name, val in attributes.iteritems():
            if name not in self.__fields__:
                self.__fields__.append(name)
                setattr(self, name, val)

    def __setattr__(self, name, value):
        if not name.startswith("__"):
            if not isinstance(value, Attribute):
                value = Attribute(value)
        super(Component, self).__setattr__(name, value)

    def __getitem__(self, name):
        return getattr(self, name, None)

    def get(self, name, default=None):
        return getattr(self, name, default)

# Case 1:  a lockable attribute
c = Component(name="Joe Schmoe", dob=LockableDateAttribute("04/12/2014"))

c.dob.lock()
c.dob.unlock()

# Case 2:  a lockable component class containing arbitrary number of lockable attributes
c2 = LockableComponent(name="Jill Pill", dob=LockableDateAttribute("04/12/2014))
c2.lock()   #  locks all of the lockable attributes

假設在你的示例代碼的最后斷言是一個錯字,而你試圖確保a2.value不是3 ,因為它已被鎖定之前就行了,關於做怎樣value的的LockableAttribute描述符?

我創建了一個Foo類,該類使用LockableAttribute並且具有一種方法來鎖定所有LockableAttribute而另一種方法可以將它們全部解鎖。 就像您在評論中所說的那樣,關於構想具有一組屬性的組件,我可以將組件鎖定起來

class LockableValue(object):
    def __get__(self, instance, owner):
        return instance.__dict__['value']
    def __set__(self, instance, value):
        if not(instance.locked):
            instance.__dict__['value'] = value

class LockableAttribute(object):
    value = LockableValue()
    def __init__(self, value=None):
        self.locked = False
        self.value = value
    def lock(self):
        self.locked = True
    def unlock(self):
        self.locked = False

class Foo(object):
    def __init__(self):
        self.a = LockableAttribute()
        self.b = LockableAttribute()
    def lock_all(self):
        for k, v in vars(self).iteritems():
            if isinstance(v, LockableAttribute):
                v.lock()
    def unlock_all(self):
        for k, v in vars(self).iteritems():
            if isinstance(v, LockableAttribute):
                v.unlock()


if __name__ == "__main__":
    foo = Foo()
    foo.a.value = 1
    foo.b.value = "hello"
    assert foo.a.locked is False
    assert foo.a.value == 1
    assert foo.b.locked is False
    assert foo.b.value == "hello"
    foo.lock_all()
    assert foo.a.locked is True
    assert foo.b.locked is True
    foo.a.unlock()
    assert foo.a.locked is False
    assert foo.b.locked is True
    foo.a.value = 2
    assert foo.a.value == 2
    foo.a.value += 1
    assert foo.a.value == 3
    foo.a.locked = True
    foo.a.value = 4
    print "foo.a.value: %s" % foo.a.value
    assert foo.a.value == 4

這似乎可以滿足您的要求...不? 我不知道,也許我誤解了。 如果是這樣,請讓我知道(我自己對描述符和元類很好奇)

它輸出:

foo.a.value: 3
Traceback (most recent call last):
  File "./stack31.py", line 56, in <module>
    assert foo.a.value == 4
AssertionError

我相信這可行:

def lockable(func):
    def _lockable(self, *args, **kwds):
        locked = getattr(self, 'locked', None)
        val = None if locked else func(self, *args, **kwds)
        return val
    return _lockable


class LockableMixin(Mixin):

    @property
    def locked(self):
        value = None
        if hasattr(self, 'meta'):
            self.meta.setdefault("locked", False)
            value = self.meta.get("locked")
        return value

    @locked.setter
    def locked(self, value):
        locked = None
        if hasattr(self, 'locked'):
            if value in [None, True, False]:
                locked = value
            self.meta['locked'] = locked

    def lock(self):
        self.locked = True

    def unlock(self):
        self.locked = False

    def is_locked(self):
        return self.locked

    def __setattr__(self, name, value):
        func = super(LockableMixin, self).__setattr__
        locked = getattr(self, 'locked', None)
        if not locked or name == 'locked':
            func(name, value)

    def __init__(self):
        super(LockableMixin, self).__init__()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM