2013-05-29 42 views
1

是否有在python的方式,使得給定的Python類:強制將兩個python類屬性中的一個始終賦值?

class Foo(object): 
    apple = None 
    orange = None 
    def __init__(self, apple=None, orange=None) 
     super(Foo, self).__init__() 
     self.apple = apple 
     self.orange = orange 

Foo對象之後是INIT,這兩個屬性(蘋果,桔子)中的一個必須總是被分配到比其他的值沒有,並且在任何時候都不應將這兩個屬性分配給無。

換句話說:

     orange is None | orange is not None 
             | 
apple is None    NO   |   YES           
________________________________________|___________________________ 
             | 
apple is not None   YES   |   NO 

如何將一個在Python做到這一點?

+1

您需要提供更多信息。你想如何工作?如果有人通過蘋果和橙子的價值觀,你想要發生什麼?如果一個人被分配並且有人試圖分配另一個人,你是否希望第一個人被自動設置爲None,或者你是否想要引發錯誤,或者是什麼? – BrenBarn

回答

4

在構造函數中,如果raise a ValueError已經足夠簡單,如果它們都是None或兩者都設置。問題在後面的代碼中。

遵循最少驚喜的原則,我認爲你應該將變量標記爲private並使用setter方法(不是屬性setter,普通的舊方法)。這清楚地表明您在設置該值時正在執行額外的邏輯,並且如果需要,它會給您一個顯而易見的地方以便添加額外的邏輯。雖然使用getter屬性方法會很好。因此,像這樣:

class Foo(object): 
    def __init__(self, apple=None, orange=None): 
     super(Foo, self).__init__() 
     if None is apple and None is orange: 
      raise ValueError('apple and orange cannot both be None') 
     if None is not apple and None is not orange: 
      raise ValueError('apple and orange cannot both be set') 

     self._apple = apple 
     self._orange = orange 

    @property 
    def apple(self): 
     return self._apple 

    @property 
    def orange(self): 
     return self._orange 

    def setAppleClearOrange(self, value): 
     if (value is None): 
      raise ValueError('Cannot set both to None') 
     self._orange = None 
     self._apple = value 

    def setOrangeClearApple(self, value): 
     if (value is None): 
      raise ValueError('Cannot set both to None') 
     self._apple = None 
     self._orange = value 

是的,這是一個有點冗長,但它是明顯你的意圖是什麼,這其實更重要的是。

+0

我回滾了編輯,因爲我將這些方法命名爲自述文檔。我特別說明了爲什麼我沒有使用setter。 set方法中的異常也需要確保「只有一個可能是'None'」約束不被違犯。 – jpmc26

+0

非常好。將邏輯本地化爲只處理必要的變量+1。 – jpaugh

+0

我覺得'apple是None == orange is None'仍然是可讀的,並且更短。 – Elazar

1

喜歡的東西

if !((self.apple==None)^(self.orange==None)) 
    // some error 

可能做的伎倆... ^是XOR運算符,它返回如果屬實,但操作數不是兩個,都是如此。

0
objs = filter(None, [apple, orange, ...]) 
if len(objs) == 0: 
    raise Exception("At least 1 object of [apple, orange] must be different from None.") 
elif len(objs) > 1: 
    raise Exception("At most 1 object of [apple, orange] should be different from None.") 
0

您可以覆蓋該類的__setattr__(),並讓它爲您處理該邏輯。見http://docs.python.org/2/reference/datamodel.html#object.setattr

喜歡的東西

class FooBar(object): 
    def __setattr__(self, key, val): 
     if key == 'apple' or key == 'orange': 
      # Decide what to do about it 
     self.__dict__[key] = val 

請注意,您需要通過物體的__dict__訪問密鑰,以避免無限循環,因爲__setattr__()取代正常的分配機制。

此外,請注意,這將檢查賦值給這兩個變量,包括在您自己的代碼中。如果這不是你想要的,那麼在__init__()中進行一些測試,正如其他人所建議的那樣。

0

這裏是一個版本,如果有必要,將另一方面設置爲None。你必須確定,如果他們都被設置爲None

class Foo(object): 
    def __init__(self, apple=None, orange=None): 
     # make sure exactly 1 of apple/orange is not None 
     if sum(x is None for x in (apple, orange)) != 1: 
      raise ... 
     self._apple = apple 
     self._orange = orange 

    @property 
    def apple(self): 
     return self._apple 

    @apple.setter 
    def apple(self, value): 
     if value is not None: 
      self._orange = None 
     elif self._orange is None: 
      raise ... 
     self._apple = value 

    @property 
    def orange(self): 
     return self._orange 

    @orange.setter 
    def orange(self, value): 
     if value is not None: 
      self._apple = None 
     elif self._apple is None: 
      raise ... 
     self._orange = value 
0
assert (apple is None) != (orange is None) 

如果你把這些代碼在__init__方法中會發生什麼,你的前提是假的,一個AssertionError將被拋出。

相關問題