2014-04-12 108 views
1

我想動態地添加一個'可鎖定'功能的值。雖然這個特定的案例看起來微不足道或者比較麻煩,但我想擴展我的可鎖定混合類以適應各種不同的用例。我不想一次性鎖定價值;我希望這是足夠通用的,以控制任何數量的類屬性。動態更新python屬性setter方法

我完成後的期望是最後的斷言會通過。

我曾嘗試使用超級而不是自我。 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 
+0

有趣的運動:)讓我試着去了解......那麼你是否想要在一個具有任意數量屬性的類中鎖定一個屬性? – BorrajaX

+0

總之:是的。 我想象一個組件,其中有一組屬性,我可以鎖定組件,包括其所有屬性(可能是列表,元組或集合)。我希望所有屬性都可以在一定程度上相互兼容。但有些可能是可鎖定的。有些可能會被禁用...等等。 –

+0

等等......如果你在鎖定a2的值爲2時,爲什麼會聲明'a2.value == 2'失敗?你的意思是'assert a2.value == 3'會失敗嗎? – BorrajaX

回答

0

假設在你的示例代碼的最後斷言是一個錯字,而你試圖讓確定a2.value不是3,因爲它之前已經被鎖定在線上了,如何使的LockableAttribute爲描述符?

我創建了一個Foo類,它使用LockableAttribute s,並有一種方法來鎖定所有的LockableAttribute s和另一個來解鎖它們。喜歡的東西你在您的評論說,大約設想一個組件與一組屬性,我可以鎖定組件了

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 
+0

我喜歡這個答案,但它不夠通用。爲所有屬性修改setter。我可能本應該像這樣佈置它的麻煩,因爲因爲我認爲這將幫助我得出我正在尋找的答案。我將在我的問題中添加一個組件類。 –

0

我相信這個工程:

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__()