python中一个简单的函数式类型安全模式匹配器
matcher的Python项目详细描述
这是类型脚本模式匹配库Rematch的端口。 matcher允许在python中进行类型安全的功能模式匹配。
基本用法
不进行类型检查
from matcher import Matcher m = Matcher() def powerLevel(hero): return m.match(hero, [ m.Type(Speedster, lambda hero: print('Speedsters are too fast!'), lambda hero: math.inf), m.Values(['Goku', 'Vegeta'], lambda hero: 9001), m.Value('Iron Man', lambda hero: 616) ]) print(powerLevel('Goku')) # 9001 print(powerLevel(Speedster.Flash)) # Speedsters are too fast! # inf print(powerLevel('Captain America')) # matcher.MatchError: Captain America doesn't match any of the provided clauses
类型检查
from matcher import Matcher m = Matcher[int, str]() def wrongInput(s: str) -> str: return m.match(s, [ m.Value(1, lambda s: s), m.Else(lambda s: s) ]) # Argument 1 to "match" of "Matcher" has incompatible type "str"; expected "int" def wrongOutput(n: int) -> Any: return m.match(n, [ m.Values((1, 2, 3), lambda n: n + "Hello World"), m.Else(lambda n: n**2) ]) # Argument 2 to "Values" of "Matcher" has incompatible type Callable[[int], int]; expected Callable[[int], str]
matcher.match函数接受一个参数和一组案例来测试参数。
有4种情况:
- Value - argument matches single value
- Values - argument matches one of multiple values
- Type - argument matches a type
- Else - argument does not match any previous cases
如果没有有效的案例,则抛出matcherror。switch语句中没有类似于“fall through”的语句。
为什么在if/else上使用模式匹配?
对于大多数对性能不敏感的代码,使用模式匹配胜过if/else有很多很好的原因:
- it enforces a common return value and type for each of your branches (when using type definitions)
- in languages with exhaustiveness checks, it forces you to explicitly consider all cases and noop the ones you don’t need
- it prevents early returns, which become harder to reason about if they cascade, grow in number, or the branches grow longer than the height of your screen (at which point they become invisible). Having an extra level of indentation goes a long way towards warning you you’re inside a scope.
- it can help you identify logic to pull out, rewriting it into a more DRY, debuggable, and testable form.
较长的示例
举个例子吧!我们正在构建一个webapp,我们需要验证我们的用户并更新他们的状态。这里有一个简单的解决方案:
if isinstance(user, BlacklistedUser): warnBlacklistMonitor() return elif user.password == enteredPassword: login() print("You're logged in!") else: onUserFailedLogin() print("Mistyped your password? Try again or do a password reset.")
这个代码有效。让我们看看模式匹配解决方案是如何堆积起来的:
from matcher import Matcher m = Matcher[User, None]() m2 = Matcher[str, str]() m.match(user, [ m.Type(BlacklistedUser, lambda user: warnBlacklistMonitor()), m.Else(lambda user: print( m2.match(enteredPassword, [ m2.Value(user.password, lambda password: login(), lambda password: "You're logged in!"), m2.Else(lambda password: onUserFailedLogin(), lambda password: f"Your password isn't {password}!") ]) )) ])
很明显有3个返回点,其中2个依赖于另一个。我们已经分解了print语句,这将使调试/测试更加容易。最后,所有的返回点始终不返回任何内容。
一个更有趣的例子
我们也可以使用匹配来计算斐波那契数!
from matcher import Matcher m = Matcher[int, int]() cases = [ m.Values([1, 2], lambda n: 1), m.Else(lambda n: m.match(n-1, cases) + m.match(n-2, cases)) ] print(m.match(10, cases)) # 55
这更符合函数定义fib(1)==fib(2)==1,fib(n)==fib(n-1)+fib(n-2)。 由于对提供给案例的操作的延迟计算,我们可以使用递归。