减少 if/elif 的使用

Reducing the use of if's / elif

有什么方法可以减少此代码中 if 和 elif 的数量 lists/dict/tuples?

cod =
    ['menu'],
    ['dimension 1"'],
    ['dimension 2”'],
    ['dimension 3”'],
    ['dimension 4'],
    ['dimension 5'],
    ['dimension 6'],
    ['dimension 7'],
    ['dimension 8'],
    ['dimension 9'],
    ['dimension 10']
]
return cod

    if choice == 1:
    quantify = float(input(('Quantify: ')))
    if 0 < quantify <= 6:
        print('± 0,1mm')
    elif 6 < quantify <= 30:
        print('± 0,2mm')
    elif 30 < quantify <= 120:
        print('± 0,3mm')
    elif 120 < quantify <= 400:
        print('± 0,5mm')
    elif 400 < quantify <= 1000:
        print('± 0,8mm')
    elif 1000 < quantify <= 2000:
        print('± 1,2mm')
    elif 2000 < quantify <= 4000:
        print('± 2mm')
    else:
        print('<<< Min = 0,5mm | Max = 4000mm >>>')
elif choice == 2:
    quantify = float(input(('Quantify: ')))
    if 0 < quantify <= 3:
        print('± 0,2mm')
    elif 3 < quantify <= 6:
        print('± 0,5mm')
    elif 6 < quantify:
        print('± 1mm')
    else:
        print('<<< Min = 0,5mm | Max = ∞ >>>')

*我不得不更改一些东西以使其可读,因为它是葡萄牙语和其他一些功能。

谢谢, 卖卡瓦略

替换包含严格相等比较的 if...elif 链非常简单。但是,当您有一个范围时,字典并不是正确的解决方案。相反,您将需要一个自定义数据结构,将每个范围的开始和结束与 if 语句进行比较。然后您可以使用 for 循环遍历范围列表中的所有范围。

您对使用字典和列表的想法是正确的。这就是我如何简化您的代码以不使用一堆 if else 语句。

from collections import namedtuple

tolerance = namedtuple( 'tolerance', ['lower_bound', 'upper_bound', 'tolerance'] )

tolerances = {
    '1': [
        tolerance(0, 6, '± 0,1mm'),
        tolerance(6, 30, '± 0,2mm'),
        tolerance(30, 120, '± 0,3mm'),
        tolerance(120, 400, '± 0,5mm'),
        tolerance(400, 1000, '± 0,8mm'),
        tolerance(1000, 2000, '± 1,2mm'),
        tolerance(2000, 4000, '± 2mm')
    ],
    '2': [
        tolerance(0, 3, '± 0,2mm'),
        tolerance(3, 6, '± 0,5mm'),
        tolerance(6, 'inf', '± 1mm'),
    ]
}

def get_tolerance(quantify, choice):
    for t in tolerances[choice]:
        if t.lower_bound <= quantify <= t.upper_bound:
            return t.tolerance

    # if we didn't find a matching tolerance, raise an error
    min_tolerance = min( t.lower_bound for t in tolerances[choice] )
    max_tolerance = max( t.lower_bound for t in tolerances[choice] )
    raise ValueError(f'<<< Min = {min_tolerance} | Max = {max_tolerance} >>>')


choice = input('Choice: ')
while choice not in tolerances:
    print('Invalid choice. Choice must be one of the following:', ', '.join(tolerances.keys()))
    choice = input('Choice: ')

quantify = float(input('Quantify: '))
print('Tolerance is:', get_tolerance(quantify, choice))

我选择使用一种叫做 named tuple 的东西来存储公差信息而不是列表。如果您从未听说过命名元组,它们的工作方式是您指定一些字段(例如 lower_bound、upper_bound、tolerance),然后在我们的例子中返回 tolerance,您可以使用它来创建命名元组。当我们使用 tolerance 创建一个命名元组时,例如 t = tolerance(0, 6, '± 0,1mm') 然后我们可以 t.lower_bound 获得下限, t.upper_bound 获得上限, 和 t.tolerance。当您需要存储固定数量的项目时,使用这样的命名元组而不是列表可以使代码更容易阅读。

这里有两种方法。注意到其他人也发布了类似的内容:)

""" SO QA """

import bisect


class Tolerance:
    def __init__(self, lower_limit, text):
        self._lower_limit = lower_limit
        self._text = text

    def __eq__(self, other):
        return self.lower_limit == other.lower_limit

    def __lt__(self, other):
        return self.lower_limit < other.lower_limit

    def __gt__(self, other):
        return self.lower_limit > other.lower_limit

    def __str__(self):
        return '{:4d} {}'.format(self.lower_limit, self.text)

    @property
    def lower_limit(self):
        return self._lower_limit

    @property
    def text(self):
        return self._text

    @staticmethod
    def get(tolerance_list, value):
        if isinstance(value, int):
            value = Tolerance(value, "")
        if isinstance(value, Tolerance):
            idx = bisect.bisect_right(tolerance_list, value)
            if idx:
                return tolerance_list[idx-1]
            raise ValueError('value {} is out of range'.format(value))
        raise TypeError('unexpected type: {}'.format(type(value)))


_TOLERANCES = [
    Tolerance(   0, '± 0,1mm'),
    Tolerance(   6, '± 0,2mm'),
    Tolerance(  30, '± 0,3mm'),
    Tolerance( 120, '± 0,5mm'),
    Tolerance( 400, '± 0,8mm'),
    Tolerance(1000, '± 1,2mm'),
    Tolerance(2000, '± 2,0mm'),
    Tolerance(4000, 'over range'),
]


def get_tolerance_using_dict(value):
    """
    Based on the value, get the tolerance entry.
    :param value: The integer value that is within the expected range.
    :returns:     A entry dict {lower_limit: value, text: ""}.
    """
    tolerances = [
        {'lower_limit':    0, 'text': '± 0,1mm'},
        {'lower_limit':    6, 'text': '± 0,2mm'},
        {'lower_limit':   30, 'text': '± 0,3mm'},
        {'lower_limit':  120, 'text': '± 0,5mm'},
        {'lower_limit':  400, 'text': '± 0,8mm'},
        {'lower_limit': 1000, 'text': '± 1,2mm'},
        {'lower_limit': 2000, 'text': '± 2,0mm'},
        {'lower_limit': 4000, 'text': 'over range'},
    ]

    prev_entry = None
    for entry in tolerances:
        if entry['lower_limit'] <= value:
            prev_entry = entry
        else:
            return prev_entry
    return tolerances[len(tolerances) - 1]
    # Perhaps a better way to handle the range error:
    # raise ValueError('value is out of range'.format(value))


def main():
    """ Test the stuff """
    values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 29, 30, 100, 119, 120, 121,
              1999, 2000, 2001, 3999, 4000, 4001]

    print('using class:')
    for value in values:
        print('{:3} : {}'.format(value, Tolerance.get(_TOLERANCES, value)))

    print('using dict:')
    for value in values:
        print('{:3} : {}'.format(value, get_tolerance_using_dict(value)))



if __name__ == "__main__":
    main()