Python:如果三件事中有一件以上是真的,则返回false
我正在写一个django模型,它允许我的网站有优惠券 优惠券可以有三种类型:终身账户凭证、特定期间的月凭证、特定数量的美元凭证 为了简单起见,我只允许优惠券具有三个可能值中的一个(即,优惠券不能为10美元和5个月)。但我想检查优惠券何时被保存,以确保这条规则是正确的 目前我有:Python:如果三件事中有一件以上是真的,则返回false,python,django,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 = 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")
我知道有更好的方法可以做到这一点,但我没有看到它(称之为编码块)
非常感谢你的帮助
如果需要,三种类型是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)
编辑:
我用卡诺图做了一个快速的电路模拟(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地图
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 |
-----------------
我不知道这是否对你更有利,但这样做会奏效:
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")
bool
是int
的一个子类,因为Python最初缺少bool
,使用int
表示布尔值,因此:
if self.months + self.dollars + self.lifetime > 1:
...
这是因为
False==0
和True==1
都是真的。您的代码看起来不错。原因如下:
1.)是你写的,你是描述逻辑的人。你可以使用各种各样的语法技巧来减少代码行数(如果self.months else 0,那么true_count+=1,如果self.months else 0,那么巨大的if语句,等等),但我认为你的方法是完美的,因为这是你在试图描述逻辑时首先想到的
留下可爱的代码来迎接编程挑战,这是真实的世界
2.)如果您决定需要添加另一种类型的息票价值类型,您完全知道您需要做什么:添加另一种If语句。在一个复杂的if语句中,完成这项任务会更加困难。将数量保留在单个字段中,并将类型设置为使用
选项的单独字段。
您还可以使用列表comp来过滤假值:
if len([x for x in [self.months, self.dollars, self.lifetime] if x]) > 1:
raise ValueError()
或建造:
我认为将其扩展到几行就可以了——如果将来有更多的属性需要测试,那么维护起来就更容易了。使用
len
或sum
感觉有点太模糊了
# 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)
我在类似情况下做过的一件事是:
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")
现在更容易添加新的优惠券类型,以检查在未来 比以前更好的解决方案,使用
组合
、任意
和全部
。
假设您在一个名为attributes
的序列中拥有所有要测试的属性:
from itertools import combinations
any(map(all, combinations(attributes, 2)))
在英语中,它是这样写的
任何长度为2的属性组合都是真的吗
此解决方案适用于任意数量的属性,可以修改以测试任意数量的属性是否为真
尽管承认它效率很低,但我认为它很可爱,可读性也很好。如果你有Python2.7或更新版本的话
from collections import Counter
items_to_test = (self.months, self.dollars, self.lifetime)
true_count = Counter(map(bool, items_to_test))[True]
怎么样
if len(filter([self.months, self.dollars, self.lifetime])) > 1:
...
我发现它与带有
if
子句的列表理解一样可读,而且更简洁。虽然代码行数较少,但它使代码更难理解。我会在更少的代码行上增加可读性。请查看我编辑的答案。它更容易阅读,也是我能想到的最有效的算法。当潜在值的数量达到4时,它是如何扩展的?也就是说,如果业务需求发生变化,并且凭证也适用于整数数量的小马,则凭证将变为6和
s,由逻辑或
s分隔。它可以像任何具有多个输入的逻辑电路一样扩展,就像K-Map思想一样;我认为我对它的实现(如下)更具可读性,因为它的功能性很差,而且扩展性更好。如果self.months为2,这将给出一个假阳性。在求和之前,你需要将变量转换成布尔值。在我发布之后,我一直在考虑这个可能的解决方案。我想知道您提出的方法或当前的方法是否具有更好的容错性。我认为这里的主要问题是“数量”字段需要存储不同的类型。一个float可以存储所有这些,但是您真的想在float字段中存储布尔值吗?然而,我赞成另一个明确列出类型的字段,因此我使用+1。无论如何,不应该将钱存储在浮点数中。把它改为整数美分。将货币存储为整数美分和浮点的基本原理是什么?在某些应用程序中(不是这一个),有部分美分需要跟踪。无需担心-快速访问谷歌找到了答案。谢谢。这个解决方案让我有点发笑——如果没有一个包含列表理解的解决方案,这将不是一个python问题。sum(bool(x)代表x in(self.months,self.dollars,self.life))
我不同意这种观点,我认为OP希望改进他的代码是件好事。一旦你有超过几个像这样的if语句,它就会开始看起来很混乱。一个更优雅的解决方案是可能的,而不会失去清晰度。还有一些其他的解决方案(包括我的)是非常清楚的,并且在将来更容易扩展。我也不同意。虽然有可能变得过于聪明,但它非常清晰易懂,而且易于扩展。在问了9个深思熟虑的答案后1小时,这就是为什么我喜欢StackOverflow。这会让许多程序员在手册页上停留半小时或更长时间,所以我不能说它在大多数商店都是可维护的。但是我必须同意,这很可爱。@Ted:哈哈,是的,对于建设性的批评+1,但我确实认为如果你对代码进行了很好的注释,即使是新手程序员也可能理解su背后的逻辑
from collections import Counter
items_to_test = (self.months, self.dollars, self.lifetime)
true_count = Counter(map(bool, items_to_test))[True]
if len(filter([self.months, self.dollars, self.lifetime])) > 1:
...