{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模板\n",
    "\n",
    "{mod}`string` 模块提供了通用的 {class}`string.Template` 类，具有适用于最终用户的简化语法。它允许用户在不更改应用逻辑的情况下自定义应用。\n",
    "\n",
    "实现方式：利用占位符 `$` 加上合法的 Python 标识符构成。一旦使用 `{ }` 将占位符括起来，就可以在后面直接跟上更多的字面和数字而无需空格分隔。`$` 的语义由 `$$` 提供转义。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Nottinghamfolk send $10 to the ditch fund.'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from string import Template\n",
    "t = Template('${village}folk send $$10 to $cause.')\n",
    "t.substitute(village='Nottingham', cause='the ditch fund')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果在字典或关键字参数中未提供某个占位符的值，那么 {meth}`string.Template.substitute` 方法将抛出 {exc}`KeyError`。对于 mail-merge 风格的应用，用户提供的数据有可能是不完整的，此时使用 {meth}`string.Template.safe_substitute` 方法更加合适 —— 如果数据缺失，它会直接将占位符原样保留。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "'owner'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_7880/1672461092.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mt\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mTemplate\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Return the $item to $owner.'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[0md\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdict\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mitem\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'unladen swallow'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mt\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msubstitute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0md\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m~\\.conda\\envs\\xpp\\lib\\string.py\u001b[0m in \u001b[0;36msubstitute\u001b[1;34m(self, mapping, **kws)\u001b[0m\n\u001b[0;32m    119\u001b[0m             raise ValueError('Unrecognized named group in pattern',\n\u001b[0;32m    120\u001b[0m                              self.pattern)\n\u001b[1;32m--> 121\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpattern\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msub\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mconvert\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtemplate\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    122\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    123\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0msafe_substitute\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmapping\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0m_sentinel_dict\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m/\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkws\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m~\\.conda\\envs\\xpp\\lib\\string.py\u001b[0m in \u001b[0;36mconvert\u001b[1;34m(mo)\u001b[0m\n\u001b[0;32m    112\u001b[0m             \u001b[0mnamed\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mmo\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgroup\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'named'\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mmo\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgroup\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'braced'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    113\u001b[0m             \u001b[1;32mif\u001b[0m \u001b[0mnamed\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 114\u001b[1;33m                 \u001b[1;32mreturn\u001b[0m \u001b[0mstr\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmapping\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mnamed\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    115\u001b[0m             \u001b[1;32mif\u001b[0m \u001b[0mmo\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgroup\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'escaped'\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    116\u001b[0m                 \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdelimiter\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mKeyError\u001b[0m: 'owner'"
     ]
    }
   ],
   "source": [
    "t = Template('Return the $item to $owner.')\n",
    "d = dict(item='unladen swallow')\n",
    "t.substitute(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Return the unladen swallow to $owner.'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t.safe_substitute(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Template` 的子类可以自定义分隔符。例如，以下是某个照片浏览器的批量重命名功能，采用了百分号作为日期、照片序号和照片格式的占位符：\n",
    "\n",
    "```python\n",
    "import time, os.path\n",
    "from string import Template\n",
    "\n",
    "photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']\n",
    "class BatchRename(Template):\n",
    "    delimiter = '%'\n",
    "fmt = input('Enter rename style (%d-date %n-seqnum %f-format):  ')\n",
    "\n",
    "t = BatchRename(fmt)\n",
    "date = time.strftime('%d%b%y')\n",
    "for i, filename in enumerate(photofiles):\n",
    "    base, ext = os.path.splitext(filename)\n",
    "    newname = t.substitute(d=date, n=i, f=ext)\n",
    "    print('{0} --> {1}'.format(filename, newname))\n",
    "```\n",
    "\n",
    "模板的另一个应用是将程序逻辑与多样的格式化输出细节分离开来。这使得对 XML 文件、纯文本报表和 HTML 网络报表使用自定义模板成为可能。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "217cbd5f9a308b7892384b08237230dd0dcfa951c152faf18c0b07a5660270cf"
  },
  "kernelspec": {
   "display_name": "Python 3.10.0 64-bit ('xpp': 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
}
