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第三方库


热门话题
java Gradle嵌套项目配置   java一些mipmap没有显示   java如何使用按钮设置值以在wicket中输入文本   可分页的java自定义排序参数   java Android AdMob NoSuchMethodError:没有静态方法zzand()   java安卓工作室;启动到USB设备(手机),已安装但无法打开   java如何使用jsoup从HTML解析表   java如何自动生成arraylist?   java定制外观和感觉Nimbus   在Java中使用气泡排序对24位数组进行排序   java我无法将javafx与spring集成   java Play 2.7 http请求post请求   Java哈希表在“put”期间用新密钥覆盖现有密钥   如何找到java更新版本?