{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 对象注解\n",
    "\n",
    "参考：{daobook}`howto/annotations.html`\n",
    "\n",
    "{guilabel}`限制`： Python 3.10 及其以上版本\n",
    "\n",
    "## inspect 用于对象检查\n",
    "\n",
    "{mod}`inspect` 模块支持：类型检查、获取源代码、检查类与函数、检查解释器的调用堆栈。\n",
    "\n",
    "### 签名与形参\n",
    "\n",
    "- {class}`inspect.Signature(parameters=None, *, return_annotation=Signature.empty)` 代表了一个函数的整体签名。它为每个被函数接受的参数存储一个 {class}`~inspect.Parameter` 对象。可以使用辅助函数 {func}`inspect.signature(obj)` 获取对象 `obj` 的签名。\n",
    "    - 可选的 `parameters` 实参是一个 `Parameter` 对象的序列，它被验证以检查是否有名称重复的形参，以及形参的顺序是否正确，即先是仅有位置的形参，然后是有位置或关键字的形参，以及有默认值的形参紧随没有默认值的形参。\n",
    "    - 可选的 `return_annotation` 实参，可以是一个任意的 Python 对象，是可调用对象的 \"return\" 注解。\n",
    "- {class}`inspect.Parameter(name, kind, *, default=Parameter.empty, annotation=Parameter.empty)` 代表函数签名中的一个参数。\n",
    "\n",
    "```{note}\n",
    "- {class}`~inspect.Signature` 与 {class}`~inspect.Parameter` 对象均是不可变的。分别使用 {func}`Signature.replace` 与 {func}`Parameter.replace` 来制作一个修改的副本。\n",
    "- {class}`~inspect.Signature` 与 {class}`~inspect.Parameter` 对象是可提取（picklable）和可散列的。\n",
    "```\n",
    "\n",
    "看一个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Signature (a, *, b: int, **kwargs)>"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from inspect import signature\n",
    "\n",
    "def foo(a, *, b:int, **kwargs):\n",
    "    ...\n",
    "    \n",
    "# 获取 `foo` 的签名\n",
    "sig = signature(foo)\n",
    "sig"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'(a, *, b: int, **kwargs)'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "str(sig) # 转换为字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "mappingproxy({'a': <Parameter \"a\">,\n",
       "              'b': <Parameter \"b: int\">,\n",
       "              'kwargs': <Parameter \"**kwargs\">})"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sig.parameters # 形参名称与相应的 Parameter 对象的有序映射。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Parameter \"b: int\">"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sig.parameters['b'] # 获取给定名称的参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "int"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sig.parameters['b'].annotation # 获取参数的注解"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"(a, *, b: int, **kwargs) -> 'new return anno'\""
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 创建新的签名\n",
    "new_sig = sig.replace(return_annotation=\"new return anno\")\n",
    "str(new_sig)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'foo=42'"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from inspect import Parameter\n",
    "\n",
    "# 创建参数实例\n",
    "param = Parameter('foo', Parameter.KEYWORD_ONLY, default=42)\n",
    "str(param)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'foo=42'"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "str(param.replace()) # param 的浅拷贝"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"foo: 'spam'\""
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 添加注解\n",
    "str(param.replace(default=Parameter.empty, annotation='spam'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "参数传值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"<BoundArguments (a='spam')>\""
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def foo(a, b='ham', *args): ...\n",
    "ba = signature(foo).bind('spam')\n",
    "\n",
    "str(ba)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 'spam'}"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ba.arguments # 查看实参"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'a': 'spam', 'b': 'ham', 'args': ()}"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ba.apply_defaults() # 应用默认值\n",
    "ba.arguments # 查看实参"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 注解\n",
    "\n",
    "{func}`inspect.get_annotations(obj, *, globals=None, locals=None, eval_str=False)` 计算一个对象的注解 dict。\n",
    "\n",
    "`obj` 可以是一个 callable，类，或模块。传入任何其他类型的对象会引发 {exc}`TypeError`。\n",
    "\n",
    "{func}`inspect.get_annotations` 每次调用都会返回一个新的 dict；对同一个对象调用两次会返回两个不同但等价的 dict。\n",
    "\n",
    "对象注解属性的最佳实践：\n",
    "\n",
    "Python 3.10 以上版本的最佳做法：使用三个参数去调用 {func}`getattr`，比如 `getattr(o, '__annotations__', None)`。"
   ]
  },
  {
   "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
}
