我正在研究一个简单的映射问题,其中我有一个值,其值在给定范围内,并且该范围决定了相关的标签。这是一种典型的if / else问题,但我想知道是否可以使用字典来进行映射,将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 :(得分: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
并将结果存储在y
而不是立即返回,则即使您在第一个条件上成功(例如,以3000为单位),也会检查所有条件。这不是这种情况,但可以使用y
和elif
编写。
此外,您的代码将为3595抛出异常(而此代码将返回None
)
答案 1 :(得分:2)
我会使用elif
s,如果前面的语句评估为if
,则不需要检查每个True
语句,这是使用所有if
发生的情况s除非你在每个if语句中return
。
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
答案 2 :(得分:2)
您可以做一些事情来使映射工作,但所有这些都很糟糕。最糟糕的方式(我想)会做类似的事情:
如果您真的想要使用看起来像项目访问权限的东西,可以试试这个:
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
请注意,在Python2中,由于range
创建列表而不是迭代器,因此会占用大量内存。请在Python2中尝试使用xrange
。
稍微快一点的安排是为每个键指定验证函数,并返回附加到返回True
的第一个值的值。请注意,结果可能是意外的,因为dict
是无序的,因此请考虑为此进行子类化OrderedDict
:
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
答案 3 :(得分: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