python 中是否真的存在联合类型?
Do union types actually exist in python?
既然python是动态类型的,当然我们可以这样做:
def f(x):
return 2 if x else "s"
但这就是 python 实际打算使用的方式吗?或者换句话说,联合类型是否存在于它们在 Racket 中的意义中?还是我们只像这样使用它们:
def f(x):
if x:
return "s"
我们唯一需要的 "union" 是 None?
仅当您使用静态类型语言时才需要联合类型,因为您需要声明一个对象可以 return 多种类型之一(在您的情况下是 int
或 str
,或者在另一个示例中 str
或 NoneType
).
Python 只处理 对象 ,因此根本不需要考虑 'union types'。 Python 函数 return 它们 return 的功能,如果程序员想要 return 不同的类型以获得不同的结果,那是他们的选择。然后选择是架构选择,对 Python 解释器没有影响(因此 'benchmark' 这里没有任何区别)。
Python 3.5 确实引入了创建可选类型 hints 的标准,并且该标准包括 Union[...]
and Optional[...]
注释。类型提示在运行时之外添加了可选的静态类型检查,就像 TypeScript 中的类型不是 JavaScript 运行时的一部分一样。
添加到@MartijnPieters 回答:
But is the way python was actually intended to be used?
根据参数返回不同的类型在任何语言中都不是好的做法。这使得测试、维护和扩展代码变得非常困难,并且恕我直言是一种反模式(当然有时是必要的邪恶)。结果至少应该通过通用接口关联起来。
union
被引入 C 的唯一原因是性能提升。但是在 Python 中,由于语言的动态特性,您没有这种性能提升(正如 Martijn 所注意到的)。实际上引入 union
会降低性能,因为 union
的大小始终是最大成员的大小。因此 Python 永远不会有 C-like union
.
类型本身不存在,因为 Python 只是一种动态类型语言,但是,在较新的 Python 版本中,联合类型是 Type Hinting、[=12 的一个选项=]
from typing import Union,TypeVar
T = TypeVar('T')
def f(x: T) -> Union[str, None]:
if x:
return "x"
您可以使用它来注释您的代码,从而启用 IDE/Editor 级别的语法检查。
这里有几个选项可以处理在 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
class 而不是元组:
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模块
当然,这些方法都有各自的缺点。
以前的答案没有解决的一个用例是从预先存在的类型构建联合类型,和 isinstance()
认为预先存在的任何实例类型是联合类型的实例。
Python 通过 抽象基础 类 支持此功能。例如:
>>> import abc
>>> class IntOrString(abc.ABC): pass
...
>>> IntOrString.register(int)
<class 'int'>
>>> IntOrString.register(str)
<class 'str'>
现在int
和str
可以看作是IntOrString
的子类:
>>> issubclass(int, IntOrString)
True
>>> isinstance(42, IntOrString)
True
>>> isinstance("answer", IntOrString)
True
从 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 documentation and PEP 604。
既然python是动态类型的,当然我们可以这样做:
def f(x):
return 2 if x else "s"
但这就是 python 实际打算使用的方式吗?或者换句话说,联合类型是否存在于它们在 Racket 中的意义中?还是我们只像这样使用它们:
def f(x):
if x:
return "s"
我们唯一需要的 "union" 是 None?
仅当您使用静态类型语言时才需要联合类型,因为您需要声明一个对象可以 return 多种类型之一(在您的情况下是 int
或 str
,或者在另一个示例中 str
或 NoneType
).
Python 只处理 对象 ,因此根本不需要考虑 'union types'。 Python 函数 return 它们 return 的功能,如果程序员想要 return 不同的类型以获得不同的结果,那是他们的选择。然后选择是架构选择,对 Python 解释器没有影响(因此 'benchmark' 这里没有任何区别)。
Python 3.5 确实引入了创建可选类型 hints 的标准,并且该标准包括 Union[...]
and Optional[...]
注释。类型提示在运行时之外添加了可选的静态类型检查,就像 TypeScript 中的类型不是 JavaScript 运行时的一部分一样。
添加到@MartijnPieters 回答:
But is the way python was actually intended to be used?
根据参数返回不同的类型在任何语言中都不是好的做法。这使得测试、维护和扩展代码变得非常困难,并且恕我直言是一种反模式(当然有时是必要的邪恶)。结果至少应该通过通用接口关联起来。
union
被引入 C 的唯一原因是性能提升。但是在 Python 中,由于语言的动态特性,您没有这种性能提升(正如 Martijn 所注意到的)。实际上引入 union
会降低性能,因为 union
的大小始终是最大成员的大小。因此 Python 永远不会有 C-like union
.
类型本身不存在,因为 Python 只是一种动态类型语言,但是,在较新的 Python 版本中,联合类型是 Type Hinting、[=12 的一个选项=]
from typing import Union,TypeVar
T = TypeVar('T')
def f(x: T) -> Union[str, None]:
if x:
return "x"
您可以使用它来注释您的代码,从而启用 IDE/Editor 级别的语法检查。
这里有几个选项可以处理在 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
class 而不是元组: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模块
当然,这些方法都有各自的缺点。
以前的答案没有解决的一个用例是从预先存在的类型构建联合类型,和 isinstance()
认为预先存在的任何实例类型是联合类型的实例。
Python 通过 抽象基础 类 支持此功能。例如:
>>> import abc
>>> class IntOrString(abc.ABC): pass
...
>>> IntOrString.register(int)
<class 'int'>
>>> IntOrString.register(str)
<class 'str'>
现在int
和str
可以看作是IntOrString
的子类:
>>> issubclass(int, IntOrString)
True
>>> isinstance(42, IntOrString)
True
>>> isinstance("answer", IntOrString)
True
从 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 documentation and PEP 604。