如何在 Python 3.10 中使用要匹配的类型进行结构模式匹配?

Pie*_*ult 11 types pattern-matching python-3.10

我正在尝试使用控制台匹配 Python 3.10 中的类型:

t = 12.0
match type(t):
  case int:
    print("int")
  case float:
    print("float")
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

  File "<stdin>", line 2
SyntaxError: name capture 'int' makes remaining patterns unreachable
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个问题?

mmo*_*eri 11

丢失type()并在您的类型中添加括号:

t = 12.0
match t:
  case int():
    print("int")
  case float():
    print("float")
Run Code Online (Sandbox Code Playgroud)

我不确定为什么你写的不起作用,但这个有效。

  • @lsabi我的理解是其他语言主要有`switch-case`结构,这是一个奇特的`if-else`。但是 python 的结构模式匹配提供的功能要强大得多,它允许您根据要比较的对象的结构选择代码分支。所以在这里,你不是在说“t”的类型是否是“int”,而是“如果你可以将“t”放入“int”容器中”。但请对我的话持保留态度,因为这只是我从阅读过的有关此新功能的不同内容中得到的理解,我绝不是该领域的专家。 (2认同)

Dub*_*low 11

这是新语法的一个常见“问题”:case 子句不是表达式。也就是说,如果将变量名称放入case 子句中,则语法会分配给该名称而不是读取该名称。

matchswitch其他语言一样思考是一种常见的误解:不是,甚至不是很接近。switch cases 是测试表达式是否相等的表达式switch;相反,match cases 是解包表达式的结构化模式。它实际上更类似于广义的可迭代拆包。它提出这样的问题:“表达式的结构看起来像原因子句的结构吗?”,这是一个与语句所问的问题非常不同的问题。matchmatchswitch

例如:

t = 12.0
match t:
    case newvar: # This is equal to `newvar = t`
        print(f"bound a new variable called newvar: {newvar}")
        # prints "bound a new variable called newvar: 12.00000000"
        # this pattern matches anything at all, so all following cases never run

    case 13.0:
        print("found 13.0")

    case [a, b, c]: # matches an iterable with exactly 3 elements,
        # and *assigns* those elements to the variables `a`, `b` and `c`
        print(f"found an iterable of length exactly 3.")
        print(f"these are the values in the iterable: {a} {b} {c}")

    case [*_]:
        print("found some sort of iterable, but it's definitely")
        print("not of length 3, because that already matched earlier")

    case my_fancy_type(): # match statement magic: this is how to type check!
        print(f"variable t = {t} is of type {my_fancy_type}")

    case _:
        print("no match")
Run Code Online (Sandbox Code Playgroud)

所以你的OP实际上做的是这样的:

t = 12.0
tt = type(t) # float obviously
match tt:

    case int: # assigns to int! `tt = int`, overwriting the builtin
       print(f"the value of int: {int}")
       # output: "the value of int: <class 'float'>"
       print(int == float) # output: True (!!!!!!!!)
       # In order to get the original builtin type, you'd have to do
       # something like `from builtins import int as int2`

    case float: # assigns to float, in this case the no-op `float = float`
        # in fact this clause is identical to the previous clause:
        # match anything and bind the match to its new name
        print(f"match anything and bind it to name 'float': {float}")
        # never prints, because we already matched the first case

    case float(): # since this isn't a variable name, no assignment happens.
        # under the hood, this equates to an `isinstance` check. 
        # `float` is not an instance of itself, so this wouldn't match.
        print(f"tt: {tt} is an instance of float") # never prints
        # of course, this case never executes anyways because the
        # first case matches anything, skipping all following cases
Run Code Online (Sandbox Code Playgroud)

坦率地说,我并不完全确定底层实例检查是如何工作的,但它肯定像其他答案所说的那样工作:根据语法的定义match,类型检查是这样完成的:

match instance:
    case type():
        print(f"object {instance} is of type {type}!")
Run Code Online (Sandbox Code Playgroud)

所以我们回到我们开始的地方:case 子句不是表达式。正如 PEP 所说,最好将 case 子句视为类似于函数声明,我们在函数声明中命名函数的参数,并可能将一些默认值绑定到这些新命名的参数。但我们永远不会读取case 子句中的现有变量,只会创建新变量。(还涉及一些其他微妙之处,例如,点分访问不能算作此目的的“变量”,但这已经很复杂了,最好在这里结束这个答案。)


mpb*_*mpb 2

首先解释一下问题中的代码:

t = 12.0
match type(t):
  case int:
    print("int")
  case float:
    print("float")
Run Code Online (Sandbox Code Playgroud)

在 Python 中,该match语句通过尝试主语(type(t),即float)纳入其中一种模式来进行操作。

上面的代码有两种模式case int:case float:)。这些模式,就其语法性质而言,是捕获模式,这意味着它们尝试将主题( 的值type(t),即float)捕获到每个模式中指定的变量名称(分别为名称intfloat)中。

图案case int:case float:彼此相同。此外,它们都可以匹配任何主题。因此,错误:SyntaxError: name capture 'int' makes remaining patterns unreachable

另外,请注意,这两种模式在功能上与 的传统默认模式相同case _:case _:也只是一个捕获模式,仅此而已。case int:( 、和case float:之间的唯一区别case _:是将捕获主题变量名称。)

现在让我们解释一下@mmohaveri 的答案中的代码。

t = 12.0
match t:
  case int():
    print("int")
  case float():
    print("float")
Run Code Online (Sandbox Code Playgroud)

上面的match语句将尝试首先将值放入12.0模式中case int():,然后放入模式中case float():

这些模式就其语法本质而言是类模式。括号可用于指定模式的位置参数关键字参数。(括号不是函数调用。)

对于第一个模式 ( case int():),12.0不是 的实例int,因此模式匹配失败

对于第二个模式, ( case float():)12.0是 的实例float,并且括号内没有指定位置或关键字参数,因此模式匹配成功

以下是有关匹配过程的更多信息。