Python:如果三件事中有一件以上是真的,则返回false

时间:2011-07-14 01:37:41

标签: python django

我正在写一个django模型,允许我的网站有优惠券。

优惠券可以有三种类型:终身帐户凭证,特定月份凭证,一定数量的美元凭证。

为了简单起见,我只允许优惠券拥有三种可能值中的一种(即优惠券不能是10美元和5个月)。但我想检查优惠券何时被保存以确保此规则为真。

目前我有:

true_count = 0
if self.months:
    true_count += 1
if self.dollars:
    true_count += 1
if self.lifetime:
    true_count += 1    

if true_count > 1:
    raise ValueError("Coupon can be valid for only one of: months, lifetime, or dollars")  

我知道有更好的方法可以做到这一点,但我没有看到它(称之为编码器块)。

非常感谢帮助。

如果是maters,则三种类型是int,int和bool

months = models.IntegerField(default=0)
cents = models.IntegerField(default=0)
#dollars = models.FloatField(default=0.00)
#dollars replaced with integer cents per advice of group
lifetime = models.BooleanField(default=False)

11 个答案:

答案 0 :(得分:10)

我在类似情况下做过的一件事是:

coupon_types = (self.months, self.dollars, self.lifetime,)

true_count =  sum(1 for ct in coupon_types if ct)
if true_count > 1:
    raise ValueError("Coupon can be valid for only one of: months, lifetime, or dollars")  

现在更容易添加新的优惠券类型以供将来检查!

答案 1 :(得分:4)

您还可以使用list comp来过滤错误值:

if len([x for x in [self.months, self.dollars, self.lifetime] if x]) > 1:
    raise ValueError()

建立MRAB's answer

if sum(map(bool, [self.months, self.dollars, self.lifetime])) > 1:
    raise ValueErrro()

答案 2 :(得分:2)

if (self.months && (self.dollars || self.lifetime))  || (self.dollars && (self.months || self.lifetime)) || (self.lifetime && (self.dollars || self.months))
    raise ValueError("Coupon can be valid for only one of: months, lifetime, or dollars") 

修改

我使用卡诺图(http://en.wikipedia.org/wiki/Karnaugh_map)进行了快速电路模拟。它最终是布尔逻辑的最小可能函数:

if((self.months && self.dollars) || (self.dollars && self.lifetime) || (self.lifetime && self.months))
    raise ValueError("Coupon can be valid for only one of: months, lifetime, or dollars") 

从逻辑上讲,我的陈述都是公平的,但第二个是技术上更快/更有效。

编辑#2 :如果有人对此感兴趣的是K-Map

A | B | C | f(A, B, C)
----------------------
0 | 0 | 0 |     0
----------------------
0 | 0 | 1 |     0
----------------------
0 | 1 | 0 |     0
----------------------
0 | 1 | 1 |     1
----------------------
1 | 0 | 0 |     0
----------------------
1 | 0 | 1 |     1
----------------------
1 | 1 | 0 |     1
----------------------
1 | 1 | 1 |     1

减少到:

   C\AB
     -----------------
     | 0 | 0 | 1 | 0 |     
     -----------------      OR      AB + BC + AC
     | 0 | 1 | 1 | 1 |
     -----------------

答案 3 :(得分:2)

您的代码看起来很好。原因如下:

1。)你写了它,而你就是那个描述逻辑的人。你可以玩各种语法技巧来减少代码行(true_count + = 1如果self.months else 0,巨大if语句等),但我认为你拥有它的方式是完美的,因为它是你的第一个想到什么时候试图描述逻辑。

为编程挑战留下可爱的代码,这是现实世界。

2。)如果您决定需要添加其他类型的优惠券价值类型,您确切知道需要做什么:添加另一个if语句。在一个复杂的if语句中,你最终会面临更难的任务。

答案 4 :(得分:2)

将数量保存在一个字段中,并将该类型设为使用choices的单独字段。

答案 5 :(得分:2)

我认为通过几行传播这一点很好 - 如果将来有更多属性要测试,这会使维护更容易。使用lensum感觉有点混淆

# Ensure that only one of these values is set
true_count = 0
true_count += bool(self.months)
true_count += bool(self.dollars)
true_count += bool(self.lifetime)

答案 6 :(得分:1)

我不知道这对你有好处,但这样做会有效:

if (self.months && self.dollars) || (self.months && self.lifetime) || (self.dollars && self.lifetime):
   raise ValueError("Coupon can be valid for only one of: months, lifetime, or dollars") 

答案 7 :(得分:1)

如果你有Python2.7或更新

from collections import Counter
items_to_test = (self.months, self.dollars, self.lifetime)
true_count = Counter(map(bool, items_to_test))[True]

答案 8 :(得分:0)

使用combinationsanyall比以前更好的解决方案。 假设您要在名为attributes的序列中测试所有属性:

from itertools import combinations
any(map(all, combinations(attributes, 2)))

用英文写的,

  

属性的任何长度为2的组合都是真的吗?

此解决方案适用于任意数量的属性,可以修改以测试任意数量的属性是否为真。

虽然这是非常低效的,但我会说它非常可爱和可读。

答案 9 :(得分:0)

怎么样

if len(filter([self.months, self.dollars, self.lifetime])) > 1:
...

我发现它与具有if子句的列表理解一样可读,并且更简洁。

答案 10 :(得分:-2)

boolint的子类,因为Python最初缺少bool,使用int表示布尔值,所以:

if self.months + self.dollars + self.lifetime > 1:
    ...

这可行,因为False == 0True == 1都是正确的。