【问题标题】:Do union types actually exist in python?联合类型实际上存在于 python 中吗?
【发布时间】:2016-12-15 17:00:46
【问题描述】:

既然python是动态类型的,我们当然可以这样做:

def f(x):
    return 2 if x else "s"

但这就是python的实际使用方式吗?或者换句话说,联合类型是否存在于它们在 Racket 中的意义?还是我们只像这样使用它们:

def f(x):
    if x:
        return "s"

我们需要的唯一“联合”是与 None?

【问题讨论】:

标签: python types unions dynamic-typing


【解决方案1】:

仅当您使用静态类型语言时才需要联合类型,因为您需要声明一个对象可以返回多种类型之一(在您的情况下为 intstr,或者在其他示例中为 @987654325 @ 或 NoneType)。

Python 仅处理对象,因此甚至不需要考虑“联合类型”。 Python 函数返回它们返回的内容,如果程序员想要为不同的结果返回不同的类型,那么这是他们的选择。那么选择就是架构选择,对 Python 解释器没有任何影响(因此这里没有什么要“基准测试”)。

Python 3.5 确实引入了创建可选类型hints 的标准,该标准包括Union[...]Optional[...] 注释。类型提示在运行时之外添加了可选的静态类型检查,就像 TypeScript 中的类型不是 JavaScript 运行时的一部分一样。

【讨论】:

  • 谢谢!我知道python不是静态类型的。但我想知道,在实践中,是否有必要拥有一个基于参数返回多种类型的函数,或者在 python 中是否总是有办法解决它?
  • @Lana: 就是这样,太宽泛了。但是看看pickle.loads()json.loads()。这些根据正在加载的数据返回任意对象。
  • @Lana:同样,对于函数返回的内容,这是一个软件架构选择。保持一致并限制返回的内容是一种很好的做法,但“解决方法”只是使用良好的软件工程实践。例如,如果您的函数可以返回True, False, None or an integer,您需要重新考虑您的函数设计。
  • “甚至不需要考虑‘联合类型’”。这与引入typing.Union 的决定有何一致?
  • @joel:Python 类型提示是静态类型,已添加到 Python。 typing.Union 不是运行时类型。
【解决方案2】:

类型本身并不存在,因为 Python 只是一种动态类型语言,但是,在较新的 Python 版本中,Union Type 是Type Hinting 的一个选项,

from typing import Union,TypeVar

T = TypeVar('T')
def f(x: T) -> Union[str, None]:
    if x:
        return "x"

您可以使用它来注释您的代码,从而启用 IDE/编辑器级别的语法检查。

【讨论】:

  • 你能解释一下 T = TypeVar('T')
  • @AlenPaulVarghese 刚刚阅读了手册:docs.python.org/3/library/typing.html#typing.TypeVar
  • @AlenPaulVarghese T = TypeVar('T') 生成一个命名的泛型。此答案中提供的方法将接受任何输入,如果提供的不是None,将返回字符串“x”。在这里使用命名泛型是完全没有必要的,但我确实建议研究它们,因为它们允许创建非常有用的模板函数。
【解决方案3】:

这里有几个选项可用于处理您在 Python 中需要 tagged union/sum type 的用例:

  • 枚举 + 元组

    from enum import Enum
    Token = Enum('Token', ['Number', 'Operator', 'Identifier', 'Space', 'Expression'])
    
    (Token.Number, 42)                            # int
    (Token.Operator, '+')                         # str
    (Token.Identifier, 'foo')                     # str
    (Token.Space, )                               # void
    (Token.Expression, ('lambda', 'x', 'x+x'))    # tuple[str]
    

    对此略有不同,使用专用的SumType 类而不是元组:

    from dataclasses import dataclass
    from typing import Any
    
    @dataclass
    class SumType:
        enum: Enum
        data: Any
    
    SumType(Token.Number, 42)
    
  • isinstance

    if isinstance(data, int):
        ...
    if isinstance(data, str):
        ...
    

    或者结合上面的“枚举”想法:

    token = SumType(Token.Number, 42)
    
    if token.enum == Token.Number:
        ...
    
  • sumtypes模块

当然,这些方法都有各自的缺点。

【讨论】:

    【解决方案4】:

    添加到@MartijnPieters 答案:

    但是,python 的实际使用方式是这样的吗?

    根据参数返回不同的类型在任何语言中都不是一个好习惯。这使得测试、维护和扩展代码变得非常困难,恕我直言,这是一种反模式(但当然有时是必要的邪恶)。结果至少应该通过通用接口相关联。

    union 被引入 C 的唯一原因是性能提升。但是在 Python 中,由于语言的动态特性(正如 Martijn 所注意到的),您没有这种性能提升。实际上引入union 会降低性能,因为union 的大小始终是最大成员的大小。因此 Python 永远不会有类似 C 的 union

    【讨论】:

    • 谢谢!这正是我想知道的。什么时候在 python 中使用联合是必要的邪恶?当我们谈论“工会”时,我们谈论的是没有工会吗? (当我在 python 中注意到很多时)或不同类型之间的联合?我想知道是否有任何示例代码可以说明这一点。
    • 请注意,我认为 OP 并不是在谈论 C 联合。我更认为他们有 Java 或 C# 类型系统。
    • @Lana 正如 Martijn 注意到的那样,json.loads() 是一个必要邪恶的例子。与None 的“联合”是一种普遍做法,但也应避免使用 IMO。尤其是在更大的项目中,您无法停止阅读这些 NoneType object has no attribute xxx 日志。我个人的看法:一个函数 == 一个返回类型。
    • @MartijnPieters 我不知道工会在其他语言中是如何工作的。抱歉,我只能参考 C 联合体。
    • tagged unions/sum types。这些在应用上与“C union”有很大不同。 Haskell 和 Rust 等强大的静态类型语言广泛使用这些语言。
    【解决方案5】:

    以前的答案未解决的一个用例是从预先存在的类型构建联合类型,并且 isinstance() 认为预先存在的类型的任何实例都是联合类型的实例.

    这在 Python 中通过 抽象基类 得到支持。例如:

    >>> import abc
    >>> class IntOrString(abc.ABC): pass
    ... 
    >>> IntOrString.register(int)
    <class 'int'>
    >>> IntOrString.register(str)
    <class 'str'>
    

    现在intstr 可以看作是IntOrString 的子类:

    >>> issubclass(int, IntOrString)
    True
    >>> isinstance(42, IntOrString)
    True
    >>> isinstance("answer", IntOrString)
    True
    

    【讨论】:

      【解决方案6】:

      从 Python 3.10 开始,您可以对联合类型使用 | 分隔符。以What's New In Python 3.10为例:

      def square(number: int | float) -> int | float:
          return number ** 2
      
      # Instead of 
      def square(number: Union[int, float]) -> Union[int, float]:
          return number ** 2
      

      此外,如果您使用的是 Python 3.7+,则可以通过使用 __future__ 包来获得该功能,但有一些限制:

      from __future__ import annotations
      
      # Works in Python 3.7+
      def square(number: int | float) -> int | float:
          return number ** 2
      
      # Works only in Python 3.10+
      isinstance(3.10, int | float)
      numeric = int | float
      

      有关详细信息,请参阅Union Types documentationPEP 604

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-09
        • 2020-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-10
        相关资源
        最近更新 更多