2011-07-14 24 views
8

我在寫一個django模型,允許我的網站擁有優惠券。Python:如果以上三件事中有一件是真的,則返回false

優惠券可以有三種類型:終生賬戶代金券,某個月份代金券,一定數量的美元代金券。

爲了簡單起見,我只允許優惠券擁有三種可能的值中的一種(即優惠券不能用於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") 

我知道有更好的方式來做到這一點,但我沒有看到它(稱之爲編碼的塊)。

非常感謝幫助。

如果它的事宜中,三種類型是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) 
+0

1個小時,這就是爲什麼我愛#1 – Ted

回答

9

有一件事我已經在類似情況下所做的是這樣的:

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") 

它現在更容易增加新的優惠券類型檢查的未來!

+0

+1,* *很好 - Python繼續令我驚歎。 –

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快速電路mimization: //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") 

按道理我的兩個語句是equivelant,但第二個是技術上更快/更有效。

編輯#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 | 
    ----------------- 
+2

雖然這是較少的代碼行,它使代碼更難以理解。我會通過更少的代碼行來提高可讀性。 –

+0

看看我編輯的答案。閱讀起來更容易,而且我能想到的最有效的算法。 – Swift

+1

當潛在值的數量達到四時,這是如何縮放的?即如果業務需求發生變化,憑證也可以適用於整數小馬? – Ted

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") 
-2

bool是子類的int因爲Python最初缺乏bool,使用int布爾,所以:

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

這工作,因爲False == 0True == 1都是真實的。

+5

如果自己。月份是2,這會產生誤報。在求和之前,你需要將變量轉換爲布爾值。 –

2

你的代碼看起來不錯。原因如下:

1.)你寫的,你是描述邏輯的人。你可以使用各種語法技巧來削減代碼行(如果self.months else 0,if語句等等,則true_count + = 1),但我認爲你擁有它的方式是完美的,因爲這是你第一次想到當試圖描述邏輯。

給編程挑戰留下可愛的代碼,這是現實世界。

2。)如果您決定需要添加其他類型的優惠券值類型,則您確切知道需要執行的操作:添加另一個if語句。在一個複雜的if語句中,你最終會做出更難的任務。

+4

我不同意這種觀點,我認爲OP很希望改善他的代碼。只要你有更多的這樣的陳述,它開始看起來很雜亂。更優雅的解決方案是可能的,而不會失去清晰度還有其他一些解決方案(包括我的)可以在將來很清楚並且更容易擴展。 – randlet

+0

我也不得不不同意。雖然有可能過於聰明,[檢查答案](http://stackoverflow.com/questions/6687557/python-if-more-than-one-of-three-things-is-true-return-false/6687869#6687869)*非常清楚,易於理解,並且易於擴展。 –

2

保持量在單個字段中,並且具有類型是使用choices的單獨字段。

+0

我在發佈後正在考慮這種可能的解決方案。我想知道您提出的方法或當前的方法是否具有更好的容錯性。 – Ted

+0

我認爲這裏的主要問題是「數量」字段需要存儲不同的類型。一個浮點數可以存儲所有的浮點數,但是你真的想要在浮點數域中存儲一個布爾值嗎?然而,我支持另一個明確列出類型的字段,因此我是+1。 –

+2

無論如何,都不應該在浮點數中儲存貨幣。只需改爲整數美分。 –

3

你也可以使用列表比較來過濾假值:

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() 
+0

謝謝。這個解決方案讓我笑了一下 - 沒有包含列表理解的解決方案,這不會是一個蟒蛇問題。 – Ted

+2

'sum(bool(x)for x in(self.months,self.dollars,self.lifetime))' –

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) 
0

甚至比以前更好的解決方案,與combinations,anyall。 假設你已經所有要測試的屬性按順序叫attributes

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

在英語中,它讀取

是屬性的任意長度爲2的組合都是真的?

該解決方案適用於屬性的任意數量,並且可以被修改,以測試它們是否真實的任意數量。

但無可否認這是非常低效的,我會說這是非常可愛和可讀性。

+1

這會讓很多程序員花半個小時或更多的時間在手冊頁上,所以我不能說它在大多數商店中都是可維護的。但我必須同意,這很可愛。 – Ted

+0

@Ted:哈哈是的,+1對於建設性的批評,但我認爲如果你很好地評論你的代碼,即使是新手程序員也可能會看到這樣一個可愛的代碼片段背後的邏輯。 –

+0

如果在未來添加屬性,你應該用'len(屬性)-1'替換文字'2' –

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] 
+1

您可以通過執行true_count = sum(map(bool,items_to_test))來使這個Python版本不可知 – randlet

0

如何

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

我覺得它就像可讀性與if條款清單理解和更簡潔。問9個周到的答案後

相關問題