2014-10-01 27 views
0

我正在一個簡單的映射問題,即我有其中其值落在給定範圍內的值,並且在該範圍確定相關聯的標籤。這是一個典型的if/else問題,但我想知道是否可以使用字典來完成映射,將lambda放在關鍵端。Python的開關功能的變體:鍵lambda函數

mapping = { 
lambda x: x >=1 and x <= 3590:'management', 
lambda x: x >= 3600 and x <= 4690: 'service', 
lambda x: x >= 4700 and x <= 5990: 'sales', 
lambda x: x >= 6000 and x <= 7690: 'natl recs/constr/maint', 
lambda x: x >= 7700 and x <= 9790: 'product/transp', 
lambda x: x >= 9800 and x <= 9830: 'milit' 
} 

但是mapping[2],例如,返回一個關鍵錯誤,所以看起來這不是猶太教。這一系列if/else語句是否是最佳途徑?

更新:

下面的版本比上面的版本更簡單,更簡潔。

def mapping(x): 
    if x >=1 and x <= 3590: y= 'management' 
    if x >= 3600 and x <= 4690: y= 'service' 
    if x >= 4700 and x <= 5990: y= 'sales' 
    if x >= 6000 and x <= 7690: y= 'natl recs/constr/maint' 
    if x >= 7700 and x <= 9790: y= 'product/transp' 
    if x >= 9800 and x <= 9830: y= 'milit' 
    return y 
mapping(5556) 
+0

?如果9800 <= x <= 9830'與你當前的if check相同, – 2014-10-01 19:29:33

+1

請記住,在Python中你可以做'如果1 <= x <= 3590'。我同意第二個問題比較簡單,這裏還有一個問題要問嗎? – Korem 2014-10-01 19:29:47

+1

你可以通過繼承'dict'並定義一個新的'__getitem__'來達到你想要的效果,但它並不完美。如果時間允許,我會寫點東西 – 2014-10-01 19:34:54

回答

3

的最佳方式,在這個恕我直言去將是:

def mapping(x): 
    if 1 <= x <= 3590: return 'management' 
    if 3600 <= x <= 4690: return 'service' 
    if 4700 <= x <= 5990: return 'sales' 
    if 6000 <= x <= 7690: return 'natl recs/constr/maint' 
    if 7700 <= x <= 9790: return 'product/transp' 
    if 9800 <= x <= 9830: return 'milit' 

你的建議的方法做多餘的檢查。如果您使用if S和存儲,而不是立即返回的結果中y,你檢查所有的,即使你是成功的第一個(以3000爲例)的條件。這不是這種情況,但是這可以用yelif來書寫。

另外你的代碼會拋出異常的3595(而這個方法返回None

+2

當你使用'return'時'if'與'elif'無關。 – Ryan 2014-10-01 19:40:50

+0

@minitech真的,謝謝。習慣。 – Korem 2014-10-01 19:41:14

+0

如果在範圍(1,3590)中使用'x而不是'if's,可能會對性能產生一些影響? – 2014-10-01 19:46:57

2

我會用elif S,你並不需要檢查每個if聲明如果前面的語句評估爲True這是除非您在每個if語句中使用return,否則使用全部if會發生什麼情況。

def mapping(x): 
    if 1 <= x <= 3590: 
     return 'management' 
    if 3600 <= x <= 4690: 
     return 'service' 
    if 4700 <= x <= 5990: 
     return 'sales' 
    if 6000 <= x <= 7690: 
     return 'natl recs/constr/maint' 
    if 7700 <= x <= 9790: 
     return 'product/transp' 
    if 9800 <= x <= 9830: 
     return 'milit' 

在你的代碼將使用elif's和集合Y的值在任何範圍

def mapping(x): 
    y = None 
    if x >=1 and x <= 3590: y= 'management' 
    elif x >= 3600 and x <= 4690: y= 'service' 
    elif x >= 4700 and x <= 5990: y= 'sales' 
    elif x >= 6000 and x <= 7690: y= 'natl recs/constr/maint' 
    elif x >= 7700 and x <= 9790: y= 'product/transp' 
    elif x >= 9800 and x <= 9830: y= 'milit' 
    return y 
+0

打敗你吧:) – Korem 2014-10-01 19:34:26

+0

@Korem,我已經評論過使用如果早在你的答案之前;) – 2014-10-01 19:39:21

+0

是的,絕對是一個共同的努力:) – Korem 2014-10-01 19:39:50

1

邊界的排序列表的使用bisect_right不處理輸入可能的工作:

from bisect import bisect_right 

keys = [1, 3600, 4700, 6000, 7700, 9800] 
values = ['management', 'service', 'sales', 'natl recs/constr/maint', 
      'product/transp', 'milit'] 

print(values[bisect_right(keys, 5556)]) # natl recs/constr/maint 
+0

應該是'values [bisect_right(keys,x)]'。但是在OP的情況下,這是一個問題,因爲他有一個非連續的範圍 - 例如,他必須在(3590,3600)範圍內插入'None' – Korem 2014-10-01 19:37:14

+0

@Korem:由於它們都是10的倍數,我認爲兩者之間的任何價值都是可能的。 (OP的現有代碼和理想字典的東西會拋出一個錯誤。) – Ryan 2014-10-01 19:38:17

+0

他在他的問題中明確指定了「映射(5556)」...... – Korem 2014-10-01 19:39:01

2

你可以做一些事情來使這個映射工作,但所有這些都是很糟糕的。最低總辦法(我想)會做這樣的事情:在Python2

class MapToRange(dict): 

    def __getitem__(self, item): 
     try: 
      super(MapToRange, self).__getitem__(item) 
     except KeyError: 
      for key in self: 
       if item in key: 
        return self[key] 
      else: 
       raise 

mapping = MapToRange({range( 1, 3591): 'management', 
         range(3600, 4691): 'service', 
         range(4700, 5991): 'sales', 
         range(6000, 7691): 'natl recs/constr/maint', 
         range(7700, 9791): 'product/transp', 
         range(9800, 9831): 'milit'}) 

>>> mapping[3500] 
management 
>>> mapping[0] 
Traceback (most recent call last): 
    File "<pyshell#53>", line 1, in <module> 
    mapping[0] 
    File "<pyshell#46>", line 4, in __getitem__ 
    return super(MapToRange, self).__getitem__(item) 
KeyError: 0 

需要注意的是:

如果你真的想要的東西,看起來像項目的訪問要做到這一點,你可以試試這個,因爲range創建的列表不是迭代器,所以這會花費很多內存。嘗試使用xrange而不是Python2。

略快的排列將是爲每個鍵指定的驗證函數,並返回附連到返回True的第一個的值。請注意,結果可能是意外,因爲dict s爲無序的,所以考慮子類OrderedDict這個像這樣:你爲什麼要使用所有`if's`

class MapToFunc(collections.OrderedDict): 
    def __getitem__(self, item): 
     try: 
      super(MapToFunc, self).__getitem__(item) 
     except KeyError: 
      for key,value in self.items(): 
       if key(item): 
        return value 
      else: 
       raise 

>>> mapping = MapToFunc([(lambda x: 1 <= x <= 3590, 'management'), 
         (lambda x: 3600 <= x <= 4690, 'service'), 
         (lambda x: 4700 <= x <= 5990, 'sales'), 
         (lambda x: 6000 <= x <= 7690, 'natl recs/const/maint'), 
         (lambda x: 7700 <= x <= 9790, 'product/transp'), 
         (lambda x: 9800 <= x <= 9830, 'milit')]) 
>>> mapping[3500] 
management 
>>> mapping[0] 
Traceback (most recent call last): 
    File "<pyshell#94>", line 1, in <module> 
    mapping[0] 
    File "<pyshell#90>", line 4, in __getitem__ 
    super(MapToFunc, self).__getitem__(item) 
KeyError: 0