强制两个python类属性中的一个始终被赋值?

时间:2013-05-29 04:44:54

标签: 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 之后,必须始终将两个属性(apple,orange)中的一个分配给None以外的值,并且任何时候都不应将这两个属性分配给某个属性除了无。

换句话说:

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

如何在python中执行此操作?

6 个答案:

答案 0 :(得分:4)

在构造函数中,如果它们raise或两者都设置,则ValueError None就足够简单了。问题稍后在代码中。

遵循最小惊喜的原则,我认为你应该将变量标记为私有并使用setter方法(不是属性设置器,普通旧方法)。这清楚地表明,当设置值时,您正在做额外的逻辑,并且如果需要,它会为您提供显而易见的添加额外逻辑的位置。但是,使用getter属性方法会很好。所以像这样:

class Foo(object):
    def __init__(self, apple=None, orange=None):
        super(Foo, self).__init__()
        if apple is None and orange is None:
            raise ValueError('apple and orange cannot both be None')
        if apple is not None and orange is not None:
            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

是的,它有点冗长,但显而易见你的意图是什么,实际上更重要。

答案 1 :(得分:1)

这样的东西
if !((self.apple==None)^(self.orange==None))
    // some error

可能做的诀窍...... ^是XOR运算符,如果操作数中的一个(但不是两个)都为真,则返回true。

答案 2 :(得分: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.")

答案 3 :(得分: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__()中进行一些测试。

答案 4 :(得分: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

答案 5 :(得分:0)

assert (apple is None) != (orange is None)

如果您将该代码放在__init__方法中,并且前提条件为false,则会抛出AssertionError