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)。 由于对提供给案例的操作的延迟计算,我们可以使用递归。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
JavaGCP:一个VM可以位于具有相同地址的不同网络上吗?   java查找安卓中第一个数组的第二个数组中存在的元素的索引   java摄像头活动不工作,设置参数失败   用于python的Base64转换的Java等效代码   为什么JPA/Hibernate在我尝试运行@Query时抛出“java.lang.NegativeArraySizeException:1”?   如何在ubuntu for java的终端上设置路径?   java为什么这两个IP不同?   java JPA:如何将本机查询结果集转换为POJO类集合   java如何在|(12)|(23)中用数字拆分字符串   异常处理如何让程序在达到Java目标后停止运行   java如何不添加以特定字符开头的元素   java如何通过字符串获得swing按钮名称?   java如何在响应不成功时读取改装中的错误体?   java*更新*现在我的程序可以编译但不运行了?