返回

Python try-except 输出难题:同时执行转换与异常

python

剖析 try-except 输出问题:同时执行转换与异常处理

在使用 try...except 代码块时,我们可能遇到一种看似矛盾的情况:期望捕获的异常与实际执行的转换代码都同时输出了。这并非异常处理机制失效,而是代码结构与逻辑导致的常见问题。

问题根源

通常情况下,try 语句块内部的某行代码发生异常后,剩余代码理应不再执行。而 except 代码块则会在捕获到特定异常时执行。但出现 “既执行了 try 语句内的操作,又执行了 except 语句”的情况,通常由以下两个原因导致:

  1. 异常发生前有输出 :如果 try 语句块内、发生异常代码之前,有输出语句(如 print)或其他操作被执行了,那么这些代码的结果自然会出现在最终输出中。因为在异常发生时,这些语句已经完成了。

  2. 异常发生于函数内部 :当在 try 代码块内调用了一个函数,而这个函数本身并没有自己的异常处理时,函数内引发的异常会被传播回 try 代码块,触发外部的 except 捕获。 然而,如果函数在引发异常之前有输出,那么这个输出会正常执行,在 except 语句执行之后出现在终端。

解决方法

针对上述两种情况,主要采用细化异常捕获,或者添加边界验证方式来避免或妥善处理问题:

方法一:细化异常捕获

当预期会发生特定类型的异常时,应该捕获该特定异常而不是使用通用的 except: ,这样能帮助我们更好地区分不同错误类型并做不同的处理。这让问题查找变得更为高效。比如上文的代码,在option = int(input('Menu Option:'))这行如果输入了非数字,则会触发ValueError类型的异常。所以except应该捕获ValueError 而非是捕获所有异常类型:

def main():
    print('What would you like to convert?')
    print('1. Miles to Kilometers')
    print('2. Fahrenheit to Celcius')
    print('3. Gallons to Liters')
    print('4. Pounds to Kilograms')
    print('5. Inches to Centimeters')
    try:
        option = int(input('Menu Option:'))
        if option == 1:
            Inptmiles = float(input('Enter number of miles to convert:'))
            milestokm(Inptmiles)
            usercontinue()
        if option == 2:
            InptFrnht = float(input('Enter degrees that you would like to convert:'))
            FrnhttoClcs(InptFrnht)
            usercontinue()
        if option == 3:
            InptGllns = float(input('Enter number of Gallons you would like to convert:'))
            GllnstoLtrs(InptGlls)
            usercontinue()
        if option == 4:
            InptLbs = float(input('Enter number of pounds you would like to convert:'))
            LbstoKgs(InptLbs)
            usercontinue()
        if option == 5:
            InptInches = float(input('Enter number of Inches you would like to convert:'))
            InchestoCm(InptInches)
            usercontinue()
    except ValueError: #捕获具体的ValueError
        print('输入错误,请输入有效的数字!')

def milestokm(miles):
    convkm = miles * 1.6
    print(f'{miles} Miles is equal to {convkm:.2f} Kilometers')

def FrnhttoClcs(Fahrenheit):
    convClcs = (Fahrenheit - 32) * 5/9
    print(f'{Fahrenheit} Degrees Fahrenheit is equal to {convClcs:.2f} Degrees Celcius')

def GllnstoLtrs(Gallons):
    convLtrs = Gallons * 3.9
    print(f'{Gallons} Gallons is equal to {convLtrs:.2f} Liters')

def LbstoKgs(Pounds):
    convKgs = Pounds * 0.45
    print(f'{Pounds} Pounds is equal to {convKgs:.2f} Kilograms')

def InchestoCm(Inches):
    convCm = Inches * 2.54
    print(f'{Inches} Inches is equal to {convCm:.2f} Centimeters')
    
def usercontinue():
  pass
  #实现用户是否继续的函数,这里为了简单,就不实现了

main()

操作步骤:

  1. except: 改为 except ValueError:
  2. 再次运行程序,输入非数字值,仅打印错误信息,转换代码不再执行。

方法二:数据类型与边界验证

在实际应用中,最好先对用户的输入进行类型与边界验证。避免后续转换发生错误。对option的值添加数字判断以及float数值边界判断能够确保输入符合预期。比如代码可以修改如下:

def main():
    print('What would you like to convert?')
    print('1. Miles to Kilometers')
    print('2. Fahrenheit to Celcius')
    print('3. Gallons to Liters')
    print('4. Pounds to Kilograms')
    print('5. Inches to Centimeters')
    option_input = input('Menu Option:') # 先保存输入的字符串
    try:
        option = int(option_input) # 对用户输入的字符串做类型转换
    except ValueError: # 对字符串转数字失败进行异常处理
         print('输入错误,请输入有效的数字选项!')
         return
        
    if option < 1 or option > 5: # 判断是否输入的数字是有效的菜单选项
        print('请输入 1 到 5 之间的有效选项!')
        return 

    try:
      if option == 1:
          Inptmiles_str = input('Enter number of miles to convert:') # 获取需要转换的字符串形式
          Inptmiles = float(Inptmiles_str)  # 把获取的字符串转换成float形式
          milestokm(Inptmiles)
          usercontinue()
      if option == 2:
            InptFrnht_str= input('Enter degrees that you would like to convert:')
            InptFrnht = float(InptFrnht_str)
            FrnhttoClcs(InptFrnht)
            usercontinue()
      if option == 3:
           InptGllns_str= input('Enter number of Gallons you would like to convert:')
           InptGllns = float(InptGllns_str)
           GllnstoLtrs(InptGllns)
           usercontinue()
      if option == 4:
          InptLbs_str= input('Enter number of pounds you would like to convert:')
          InptLbs = float(InptLbs_str)
          LbstoKgs(InptLbs)
          usercontinue()
      if option == 5:
           InptInches_str = input('Enter number of Inches you would like to convert:')
           InptInches = float(InptInches_str)
           InchestoCm(InptInches)
           usercontinue()
    except ValueError:
         print('输入错误,请输入有效的数值!')



def milestokm(miles):
    convkm = miles * 1.6
    print(f'{miles} Miles is equal to {convkm:.2f} Kilometers')

def FrnhttoClcs(Fahrenheit):
    convClcs = (Fahrenheit - 32) * 5/9
    print(f'{Fahrenheit} Degrees Fahrenheit is equal to {convClcs:.2f} Degrees Celcius')

def GllnstoLtrs(Gallons):
    convLtrs = Gallons * 3.9
    print(f'{Gallons} Gallons is equal to {convLtrs:.2f} Liters')

def LbstoKgs(Pounds):
    convKgs = Pounds * 0.45
    print(f'{Pounds} Pounds is equal to {convKgs:.2f} Kilograms')

def InchestoCm(Inches):
    convCm = Inches * 2.54
    print(f'{Inches} Inches is equal to {convCm:.2f} Centimeters')
    
def usercontinue():
  pass

main()

操作步骤:

  1. 将输入获取放到单独的变量里
  2. 加入对于用户选项输入合法性的判断
  3. 在类型转换外层添加try except语句块。
  4. 再次运行程序,尝试输入非数字、超出范围的选项或无效数值,观察程序的行为变化。

额外建议

  • 记录异常信息: 建议将异常信息记录到日志中,方便后期追踪和调试。
  • 提供用户友好的错误信息: 确保用户看到清晰、有指导性的错误信息,而非技术性的异常堆栈。
  • 充分的测试: 对于可能出现异常的场景需要编写单元测试。确保这些边界情况得到处理。

通过仔细分析问题、选择合适的异常处理方式,我们可以编写出更为健壮的应用程序,有效地处理异常,避免用户产生不佳的使用体验。