0. 概览
如果我们分别在平静如水、和谐感人的 Swift 和 Python 社区抛出诸如“Python 是天下最好的语言…” 和 “Swift 是宇宙第一语言…”之类的言论会有怎样的“下场”?
我们并不想对可能发生的“炸裂”景象做出什么预测,也无意比较 Swift 与 Python 的孰强孰弱。我们只是想聊聊 Swift 和 Python 语言中一个非常有趣的话题:看看谁实现带关联信息的错误(异常)类型更优雅?
在本篇博文中,您将了解到如下内容:
- 0. 概览
- 1. “抛出?还是不抛出错误?这是个问题!”
- 2. 带关联信息的错误
- 3. Python 中的实现
- 4. Swift 中实现
- 5. 那么问题来了:谁更优雅呢?
- 总结
还等什么呢?Let’s go!!!😉
1. “抛出?还是不抛出错误?这是个问题!”
在秃头码农们辛勤操劳的每一天都会遇到多如牛毛的代码错误,俗话说得好:“事无常形,人无完人”,没有错误的代码只能是还没有写出来的代码。
所幸的是几乎所有语言均对错误做出了良好和友善的支持,Swift 与 Python 自然也不例外!
在 Swift 中我们把代码能够抛出的某种“不安分”的东东称为错误(Error),而在 Python 中这被称之为异常(Exception)。其实从这种意义上来说它们是同一种东西,所以在本篇博文中统统称之为错误。
当函数或方法出现“不按常理出牌”的结果时,我们就有必要抛出一个错误。比如除以零:
enum AppError: Error {case divisionByZero
}func div(dividend: Int, divisor: Int) throws -> Int {guard divisor != 0 else {throw AppError.divisionByZero}return dividend / divisor
}
上面是 Swift 中对此的一个实现,在 Python 中有类似的表达:
class DivisionByZero(Exception):passdef div(dividend, divisor):if divisor == 0:raise DivisionByZero()return dividend / divisor
2. 带关联信息的错误
在某些情况下,我们希望抛出的错误中携带一些有用的信息,这种错误称之为:带关联信息的错误。
比如当用户输入的文本太短不符合要求时,我们希望进一步了解到用户输入文本的实际长度以及最短需要的长度。
在 Swift 中我们可以通过枚举关联类型来实现带关联信息的错误,类似的在 Python 中我们可以用异常类的附加实例属性来达到这一相同目的。
关于更多 Swift 中枚举使用的奥秘,请小伙伴们移步我的专题视频观看:
- Swift 基本功修炼:深入浅出 Swift 中的枚举类型
如果小伙伴们还想进一步学习 Swift 语言的开发技巧,可以到我的专题专栏中继续研究:
- Swift 语言开发精讲(文章平均质量分 97)
下面我们就来分别看看它们的实现吧!
3. Python 中的实现
在 Python 中可以简单的为自定义异常类增加实例属性变量:
class TextTooShortException(Exception):def __init__(self, actual, min):super().__init__(self)self.actual = actualself.min = minMIN_LEN = 3try:text = "wo"if len(text) < MIN_LEN:raise TextTooShortException(len(text), MIN_LEN)
except TextTooShortException as error:print("错误:文本太短({}),希望不小于 {} 个字符!".format(len(text), MIN_LEN))
如上代码所示,我们创建了一个自定义异常类 TextTooShortException,其中分别用 actual 和 min 实例属性变量来存放输入文本实际和最短所需的长度。
我们看看运行结果:
4. Swift 中实现
在 Swift 中对于错误这种不需要实例化的对象,一般可以用枚举来表示(当然也可以用 struct,这要分情况而定)。
对于带关联信息的错误,我们只需要创建带关联类型的枚举元素即可:
enum MyError: Error {case textTooShort(actual: Int, min: Int)
}let text = "wo"do {if text.count < MIN_LEN {throw MyError.textTooShort(actual: text.count, min: MIN_LEN)}
} catch let my_error as MyError {if case MyError.textTooShort(let actual, let min) = my_error {print("输入文字长度(\(actual))太短了,不能小于(\(min))")} else {print("其它 MyError: \(my_error)")}
} catch {print("Other ERR: \(error)")
}
在上面的代码中,我们创建了一个 MyError 类型的错误,并添加了一个 textTooShort 枚举子项。其中我们为其嵌入了两个 Int 值作为关联类型,分别用来表示 actual 和 min 值。
以下是 Playground 中的运行结果:
5. 那么问题来了:谁更优雅呢?
看过上面两种实现,各位小伙伴们可能都会有自己的考量。
在 Swift 中使用类似于命名元组的枚举关联类型,显得更轻量级。表达 Error 这一概念无需动用 Class 这一重型武器,一个简单的值类型(enum)足以!
在 Python 中的实现也很简单,不过使用类来作为异常的载体显得更加“厚重”,更加中规中矩。
平心而论:Python 在捕获并处理异常时更加简洁,而 Swift 在定义错误时更轻量级,可惜关联类型枚举在错误解析时比较拖垮。
不过,谁说在 Swift 中不能用 Python 的方式自定义错误呢?😉
struct TextTooShortError: Error {var actual: Intvar min: Int
}let text = "wo"do {if text.count < MIN_LEN {throw TextTooShortError(actual: text.count, min: MIN_LEN)}
} catch let tooShortError as TextTooShortError {print("输入文字长度(\(tooShortError.actual))太短了,不能小于(\(tooShortError.min))")
} catch {print("Other ERR: \(error)")
}
总结
在本篇博文中,我们讨论了在 Swift 和 Python 两种语言中对于带关联信息的错误(异常)类型是如何实现的这一话题,并对哪种实现更优雅给出笔者自己的感悟。
感谢观赏,再会!😎