2011-07-22 47 views
5

更具體地說,我希望能夠支持lambda: <some_or_other_setter>,但我希望保持代碼清晰和簡潔。我必須驗證這個值,所以我需要一個類似的setter。我需要使用lambda,因爲我需要將回調傳遞給Tkinter事件。我還需要能夠修改綁定外的屬性值。什麼將是一個可以用在lambda中的屬性的最pythonic方法?

在我下面的例子中,假設名爲spam_button的按鈕小部件已經在全局聲明。另外假設​​類將至少有10個屬性,而不需要全部以相同的方式訪問(我喜歡一致性)。

第一個可能的辦法,我能做到這一點是隻使用getter和setter方法:

class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.set_spam('Monster') 
     print self.get_spam() 
     spam_button.bind('<Enter>', lambda: self.set_spam('Ouch')) 

    def set_spam(self, spam): 
     if len(spam) <= 32: 
      self._spam = spam 

    def get_spam(self): 
     return self._spam 

但是,如果我用這個方法,並有很多的屬性,代碼可能會太長管理。

我能做到這一點的第二種方法是使用性能,並在回調使用的setter:

class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.spam = 'Monster' 
     print self.spam 
     spam_button.bind('<Enter>', lambda: self.set_spam('Ouch')) 

    def set_spam(self, spam): 
     if len(spam) <= 32: 
      self._spam = spam 

    def get_spam(self): 
     return self._spam 

    spam = property(get_spam, set_spam) 

直接訪問屬性時,此方法是比第一更簡單了一點,但不一致使用時拉姆達。

第三種方式做,這將是使用get和set方法的附加類:

class Spam(object): 

    def __init__(self): 
     self._value = '' 

    def set(self, value): 
     if len(spam) <= 32: 
      self._value = value 

    def get(self): 
     return self._value 


class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.spam = Spam() 
     self.spam.set('Monster') 
     print self.spam.get() 
     spam_button.bind('<Enter>', lambda: self.spam.set('Ouch')) 

這種方法比第一個更有組織,但我需要做一個類每類的驗證。

我可能會做最後的辦法是使用方法,而不是屬性(Properties是第二個例子):

class Eggs(object): 

    def __init__(self): 
     self._spam = '' 
     self.spam('Monster') 
     print self.spam() 
     spam_button.bind('<Enter>', lambda: self.spam('Ouch')) 

    def spam(self, spam=None): 
     if spam != None: 
      if len(spam) <= 32: 
       self._spam = spam 
     else: 
      return self._spam 

這種方法將可能是最短的,但一看就知道是難我正在接受或設置。

哪些(如果有)這些方法是首選?

回答

2

使用閉包返回作爲一個回調函數,並且定義每個條件的功能(使用lambdadef,你的偏好),而不是每個二傳手的功能。

class Eggs(object): 

    def __init__(self): 
     self.spam = 'Monster' 
     def spam_condition(string = None): 
      return (string is not None) and (len(string) <= 32) 
     self.spam_setter = self.set('spam', 'Ouch', spam_condition) 
     spam_button.bind('<Enter>', self.spam_setter) 
     self.spam_setter(val='Horse') 


    def set(self, name, value, condition): 
     def setter(val): 
      if type(val) is not .... : # fill this in with the TYPE of the event 
       value = val 
      if condition(value): 
       setattr(self, name, value) 
     return setter 

編輯:現在你可以調用setter,它會從任何地方檢查條件。

編輯2:這樣,setter將檢查它是否得到一個事件,如果沒有,使用它傳遞的值。您還可以使用:

if not isinstance(val, ....) : # fill this in with the CLASS of the event 
+0

感謝您的想法,但我真的不想使用字符串來訪問屬性(我可能只是使用字典),並且這不提供一種方法來設置和驗證綁定外的值除非我每次想設置值self.set('spam','Ouch',spam_condition)()',這真的很難看)。另外,你需要爲'setter'函數添加一個'event'參數,否則Tkinter不能傳遞事件(並且會得到一個'ValueError')。 –

+0

現在可以在任何地方使用setter。 – agf

+0

如果我使用'lambda e:self.spam_setter()'作爲回調函數,並從'setter'中移除'event'參數,那麼每次設置該值時都不需要指定'val ='。是否有可能在回調中擺脫'lambda e:'的'e'? –

1

我仍然建議使用字典並稱它爲一天。它的子類,並覆蓋__setitem__做你的數據驗證,那麼不用擔心干將:

#!/usr/bin/env python 

class Egg(dict): 
    def __init__(self, *args, **kwargs): 
     super(Egg, self).__init__(*args, **kwargs) 
     self['spam'] = 'foo' 
     spam_button.bind('<Enter>', lambda: self.__setitem__('spam', 'Ouch')) 

    def __setitem__(self, key, value): 
     if key == 'spam': 
      if len(value) > 32: 
       raise ValueError('"%s" is longer than 32 characters') 
      return super(Egg, self).__setitem__(key, value) 
     raise KeyError(key) 
0

的驗證問題可以使用property處理:

class Egg(object): 
    @property 
    def spam(self): 
     return self._spam 

    @spam.setter 
    def spam(self, value):  
     if len(value) <= 32: 
      self._spam = value 

很明顯,你仍然可以使用self._spam = 'spam '*10+'baked beans and spam'不受懲罰。

使用內置setattr

lambda: setattr(self, 'spam', 'Ouch') 

如果你反對..."spam"...,寧願只是...spam...,您可以使用property方法:

lambda: self.__class__.spam.fset(self, 'Ouch') 

,或者因爲property是一個描述符:

lambda: type(self).spam.__set__(self, 'Ouch') 

但是第一個版本是首選,我希望有明顯的原因。

+0

他希望在設置垃圾郵件時自動檢查該情況,因此只需使用'setattr'不起作用。 – agf

0

我喜歡基於類的方法。你可能會有一些你想要做的有限的驗證(例如字符串的最大長度,數字的有效範圍等),所以你將只有有限的幾個類。

例如:

class LimitedLengthString(object): 
    def __init__(self, max_length): 
     self.max_length = max_length 
    def set(self, value): 
     if len(value) <= self.max_length: 
      self.value = value 
    def get(self): 
     return value 

class Eggs(object): 
    def __init__(self): 
     self.spam = LimitedLengthString(32) 
     self.spam.set('Monster') 
     print self.spam.get() 
     spam_button.bind('<Enter>', lambda: self.spam.set('Ouch')) 

如果你真的有需要做不同屬性的唯一驗證,你不能從編寫大量的代碼脫身。但是一如既往地在編程中,當你開始重複自己的時候概括一下。使用相當未知的Python功能 「描述」 另類:由TokenMacGuy建議後


更新

class LimitedLengthString(object): 
    def __init__(self, name, max_length): 
     self.name = name 
     self.max_length = max_length 

    def __set__(self, instance, value): 
     if len(value) <= self.max_length: 
      instance.__dict__[self.name] = value 

    def __get__(self, instance, owner): 
     return instance.__dict__[self.name] 

class Eggs(object): 
    spam = LimitedLengthString('spam', 32) 
    def __init__(self): 
     self.spam = 'Monster' 
     print self.spam # prints 'Monster' 
     self.spam = 'x' * 40 
     print self.spam # still 'Monster' 
     spam_button.bind('<Enter>', lambda: self.spam = 'Ouch') 

我發現了一個pretty good introduction to descriptors

+0

爲什麼不實現描述符協議? (http://docs.python.org/reference/datamodel.html#descriptors) – SingleNegationElimination

+0

@TokenMacGuy:好點。更新了我的答案。 –

相關問題