{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 类型提示\n",
    "\n",
    "{mod}`typing` 提供了对类型提示的运行时支持。最基本的支持包括 {data}`Any`, {data}`Union`, {data}`Callable`, {class}`TypeVar`, 和 {class}`Generic`。\n",
    "\n",
    "示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': str, 'return': str}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def greeting(name: str) -> str:\n",
    "    return 'Hello ' + name\n",
    "\n",
    "getattr(greeting, '__annotations__', None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类型别名\n",
    "\n",
    "把类型赋给别名，就可以定义类型别名。本例中，`Vector` 和 `list[float]` 相同，可互换："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2.0, -8.4, 10.8]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Vector = list[float]\n",
    "\n",
    "def scale(scalar: float, vector: Vector) -> Vector:\n",
    "    return [scalar * num for num in vector]\n",
    "\n",
    "# 类型检查；一个浮点数的列表可以作为一个向量\n",
    "new_vector = scale(2.0, [1.0, -4.2, 5.4])\n",
    "new_vector"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类型别名适用于简化复杂的类型签名。例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections.abc import Sequence\n",
    "from typing import NoReturn\n",
    "\n",
    "\n",
    "ConnectionOptions = dict[str, str]\n",
    "Address = tuple[str, int]\n",
    "Server = tuple[Address, ConnectionOptions]\n",
    "\n",
    "def broadcast_message(message: str, servers: Sequence[Server]) -> NoReturn:\n",
    "    ...\n",
    "\n",
    "# 静态类型检查器会将之前的类型签名视为与此完全等价。\n",
    "def broadcast_message(\n",
    "        message: str,\n",
    "        servers: Sequence[tuple[tuple[str, int], dict[str, str]]]) -> NoReturn:\n",
    "    ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## NewType\n",
    "\n",
    "使用 {class}`typing.NewType` 创建简单的唯一类型，几乎没有运行时的开销。`NewType(name, tp)` 被认为是 `tp` 的子类型。在运行时，`NewType(name, tp)` 简单地返回其参数的 dummy 函数。使用方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import NewType\n",
    "\n",
    "UserId = NewType('UserId', int)\n",
    "\n",
    "def name_by_id(user_id: UserId) -> str: ...\n",
    "\n",
    "UserId('user') # 类型检查失败\n",
    "name_by_id(42) # 类型检查失败\n",
    "name_by_id(UserId(42))  # 正确\n",
    "num = UserId(5) + 1     # 类型：`int`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 静态类型检查器把新类型当作原始类型的子类，这种方式适用于捕捉逻辑错误。\n",
    "2. `NewType` 声明把一种类型当作另一种类型的子类型。`Derived = NewType('Derived', Original)` 时，静态类型检查器把 `Derived` 当作 `Original` 的子类 ，即，`Original` 类型的值不能用在预期 `Derived` 类型的位置。这种方式适用于以最小运行时成本防止逻辑错误。\n",
    "3. 继承 `NewType` 声明的子类型是无效的。\n",
    "\n",
    "## 可调对象\n",
    "\n",
    "预期特定签名回调函数的框架可以用 `Callable[[Arg1Type, Arg2Type], ReturnType]` 实现类型提示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Callable\n",
    "\n",
    "def feeder(get_next_item: Callable[[], str]) -> None:\n",
    "    # Body\n",
    "    ...\n",
    "\n",
    "def async_query(on_success: Callable[[int], None],\n",
    "                on_error: Callable[[int, Exception], None]) -> None:\n",
    "    # Body\n",
    "    ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "无需指定调用签名，用省略号字面量替换类型提示里的参数列表： `Callable[..., ReturnType]`，就可以声明可调对象的返回类型。\n",
    "\n",
    "以其他可调用对象为参数的可调用对象可以使用 `ParamSpec` 来表明其参数类型是相互依赖的。此外，如果该可调用对象增加或删除了其他可调用对象的参数，可以使用 `Concatenate` 操作符。它们分别采取 `Callable[ParamSpecVariable, ReturnType]` 和 `Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]` 的形式。\n",
    "\n",
    "## 泛型\n",
    "\n",
    "容器中，对象的类型信息不能以泛型方式静态推断，因此，抽象基类扩展支持下标，用于表示容器元素的预期类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "from typing import Mapping, Sequence\n",
    "\n",
    "def notify_by_email(employees: Sequence[Employee],\n",
    "                    overrides: Mapping[str, str]) -> None: ...\n",
    "```\n",
    "\n",
    "{class}`typing.TypeVar` 工厂函数实现泛型参数化。\n",
    "\n",
    "```python\n",
    "from typing import TypeVar\n",
    "\n",
    "T = TypeVar('T')      # Declare type variable\n",
    "\n",
    "def first(l: Sequence[T]) -> T:   # Generic function\n",
    "    return l[0]\n",
    "```\n",
    "\n",
    "## 用户定义的泛型类型\n",
    "\n",
    "用户定义的类可以定义为泛型类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import TypeVar, Generic\n",
    "from logging import Logger\n",
    "\n",
    "T = TypeVar('T')\n",
    "\n",
    "class LoggedVar(Generic[T]):\n",
    "    def __init__(self, value: T, name: str, logger: Logger) -> None:\n",
    "        self.name = name\n",
    "        self.logger = logger\n",
    "        self.value = value\n",
    "\n",
    "    def set(self, new: T) -> None:\n",
    "        self.log('Set ' + repr(self.value))\n",
    "        self.value = new\n",
    "\n",
    "    def get(self) -> T:\n",
    "        self.log('Get ' + repr(self.value))\n",
    "        return self.value\n",
    "\n",
    "    def log(self, message: str) -> None:\n",
    "        self.logger.info('%s: %s', self.name, message)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Generic[T]` 是定义类 `LoggedVar` 的基类，该类使用单类型参数 `T`。在该类体内，`T` 是有效的类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "86577e01b9bbd5de63e032859e2e39dd70f0ebe6eae000062a9f8f6bb0eed1e8"
  },
  "kernelspec": {
   "display_name": "Python 3.10.0 64-bit ('cpp': conda)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.0"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
