text: {
   "cells": [
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "# 函数的文档"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "你在调用函数的时候，你像是函数这个产品的用户。\n",
      "\n",
      "而你写一个函数，像是做一个产品，这个产品将来可能会被很多用户使用 —— 包括你自己。\n",
      "\n",
      "产品，就应该有产品说明书，别人用得着，你自己也用得着 —— 很久之后的你，很可能把当初的各种来龙去脉忘得一干二净，所以也同样需要产品说明书，别看那产品曾经是你自己设计的。\n",
      "\n",
      "Python 在这方面很用功，把函数的 “产品说明书” 当作语言内部的功能，这也是为什么 Python 有 [Sphinx](http://www.sphinx-doc.org) 这种工具，而绝大多数其他语言没有的原因之一罢。"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Docstring"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "在函数定义内部，我们可以加上 **Docstring**；将来函数的 “用户” 就可以通过 `help()` 这个内建函数，或者 `.__doc__` 这个 Method 去查看这个 Docstring，即，该函数的 “产品说明书”。\n",
      "\n",
      "先看一个 Docstring 以及如何查看某个函数的 Docstring 的例子："
     ]
    },
    {
     "cell_type": "code",
     "execution_count": 21,
     "metadata": {},
     "outputs": [
      {
       "name": "stdout",
       "output_type": "stream",
       "text": [
        "Help on function is_prime in module __main__:\n",
        "\n",
        "is_prime(n)\n",
        "    Return a boolean value based upon\n",
        "    whether the argument n is a prime number.\n",
        "\n",
        "\n",
        "    Return a boolean value based upon\n",
        "    whether the argument n is a prime number.\n",
        "    \n"
       ]
      },
      {
       "data": {
        "text/plain": [
         "'\\n    Return a boolean value based upon\\n    whether the argument n is a prime number.\\n    '"
        ]
       },
       "execution_count": 21,
       "metadata": {},
       "output_type": "execute_result"
      }
     ],
     "source": [
      "def is_prime(n):\n",
      "    \"\"\"\n",
      "    Return a boolean value based upon\n",
      "    whether the argument n is a prime number.\n",
      "    \"\"\"\n",
      "    if n < 2:\n",
      "        return False\n",
      "    if n == 2:\n",
      "        return True\n",
      "    for m in range(2, int(n**0.5)+1):\n",
      "        if (n % m) == 0:\n",
      "            return False\n",
      "    else:\n",
      "        return True\n",
      "\n",
      "\n",
      "help(is_prime)\n",
      "print(is_prime.__doc__)\n",
      "is_prime.__doc__"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Docstring 可以是多行字符串，也可以是单行字符串："
     ]
    },
    {
     "cell_type": "code",
     "execution_count": 16,
     "metadata": {},
     "outputs": [
      {
       "name": "stdout",
       "output_type": "stream",
       "text": [
        "Help on function is_prime in module __main__:\n",
        "\n",
        "is_prime(n)\n",
        "    Return a boolean value based upon whether the argument n is a prime number.\n",
        "\n",
        "Return a boolean value based upon whether the argument n is a prime number.\n"
       ]
      },
      {
       "data": {
        "text/plain": [
         "'Return a boolean value based upon whether the argument n is a prime number.'"
        ]
       },
       "execution_count": 16,
       "metadata": {},
       "output_type": "execute_result"
      }
     ],
     "source": [
      "def is_prime(n):\n",
      "    \"\"\"Return a boolean value based upon whether the argument n is a prime number.\"\"\"\n",
      "    \n",
      "    if n < 2:\n",
      "        return False\n",
      "    if n == 2:\n",
      "        return True\n",
      "    for m in range(2, int(n**0.5)+1):\n",
      "        if (n % m) == 0:\n",
      "            return False\n",
      "    else:\n",
      "        return True\n",
      "\n",
      "\n",
      "help(is_prime)\n",
      "print(is_prime.__doc__)\n",
      "is_prime.__doc__"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Docstring 如若存在，必须在函数定义的内部语句块的开头，也必须与其它语句一样保持相应的缩进（Indention）。Docstring 放在其它地方不起作用："
     ]
    },
    {
     "cell_type": "code",
     "execution_count": 12,
     "metadata": {},
     "outputs": [
      {
       "name": "stdout",
       "output_type": "stream",
       "text": [
        "Help on function is_prime in module __main__:\n",
        "\n",
        "is_prime(n)\n",
        "\n",
        "None\n"
       ]
      }
     ],
     "source": [
      "def is_prime(n):\n",
      "    if n < 2:\n",
      "        return False\n",
      "    if n == 2:\n",
      "        return True\n",
      "    for m in range(2, int(n**0.5)+1):\n",
      "        if (n % m) == 0:\n",
      "            return False\n",
      "    else:\n",
      "        return True\n",
      "    \"\"\"\n",
      "    Return a boolean value based upon\n",
      "    whether the argument n is a prime number.\n",
      "    \"\"\"\n",
      "\n",
      "help(is_prime)\n",
      "print(is_prime.__doc__)\n",
      "is_prime.__doc__"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## 书写 Docstring 的规范"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "规范，虽然是人们最好遵守的，但其实通常是很多人并不遵守的东西。"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "既然学，就要**像样** —— 这真的很重要。所以，非常有必要认真阅读 Python [PEP 257](https://www.python.org/dev/peps/pep-0257/) 关于 Docstring 的规范。"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "简要总结一下 PEP 257 中必须掌握的规范：\n",
      "\n",
      "> 1. 无论是单行还是多行的 Docstring，一概使用三个双引号括起；\n",
      "> 2. 在 Docstring 内部，文字开始之前，以及文字结束之后，都不要有空行；\n",
      "> 3. 多行 Docstring，第一行是概要，随后空一行，再写其它部分；\n",
      "> 4. 完善的 Docstring，应该概括清楚以下内容：参数、返回值、可能触发的错误类型、可能的副作用，以及函数的使用限制等等；\n",
      "> 5. 每个参数的说明都使用单独的一行……\n",
      "\n",
      "由于我们还没有开始研究 Class，所以，关于 Class 的 Docstring 应该遵守什么样的规范就暂时略过了。然而，这种规范你总是要反复去阅读参照的。关于 Docstring，有两个规范文件：\n",
      "\n",
      "> * [PEP 257: Docstring Convensions](https://www.python.org/dev/peps/pep-0257/)\n",
      "> * [PEP 258: Docutils Design Specification](https://www.python.org/dev/peps/pep-0258/)\n",
      "\n",
      "需要**格外注意**的是：\n",
      "\n",
      "> Docstring 是**写给人看的**，所以，在复杂代码的 Docstring 中，写 **Why** 要远比写 _What_ 更重要 —— 你先记住这点，以后的体会自然会不断加深。"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "## Sphinx 版本的 Docstring 规范"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Sphinx 可以从 `.py` 文件里提取所有 Docstring，而后生成完整的 Documentation。将来若是你写大型的项目，需要生成完善的文档的时候，你就会发现 Sphinx 是个 “救命” 的家伙，省时、省力、省心、省命……\n",
      "\n",
      "在这里，没办法一下子讲清楚 Sphinx 的使用，尤其是它还用它自己的一种标记语言，reStructureText，文件尾缀使用 `.rst`……\n",
      "\n",
      "但是，可以看一个例子："
     ]
    },
    {
     "cell_type": "code",
     "execution_count": 22,
     "metadata": {},
     "outputs": [
      {
       "name": "stdout",
       "output_type": "stream",
       "text": [
        "Help on class Vehicle in module __main__:\n",
        "\n",
        "class Vehicle(builtins.object)\n",
        " |  Vehicle(arg, *args, **kwargs)\n",
        " |  \n",
        " |  The Vehicle object contains lots of vehicles\n",
        " |  :param arg: The arg is used for ...\n",
        " |  :type arg: str\n",
        " |  :param `*args`: The variable arguments are used for ...\n",
        " |  :param `**kwargs`: The keyword arguments are used for ...\n",
        " |  :ivar arg: This is where we store arg\n",
        " |  :vartype arg: str\n",
        " |  \n",
        " |  Methods defined here:\n",
        " |  \n",
        " |  __init__(self, arg, *args, **kwargs)\n",
        " |      Initialize self.  See help(type(self)) for accurate signature.\n",
        " |  \n",
        " |  cars(self, distance, destination)\n",
        " |      We can't travel a certain distance in vehicles without fuels, so here's the fuels\n",
        " |      \n",
        " |      :param distance: The amount of distance traveled\n",
        " |      :type amount: int\n",
        " |      :param bool destinationReached: Should the fuels be refilled to cover required distance?\n",
        " |      :raises: :class:`RuntimeError`: Out of fuel\n",
        " |      \n",
        " |      :returns: A Car mileage\n",
        " |      :rtype: Cars\n",
        " |  \n",
        " |  ----------------------------------------------------------------------\n",
        " |  Data descriptors defined here:\n",
        " |  \n",
        " |  __dict__\n",
        " |      dictionary for instance variables (if defined)\n",
        " |  \n",
        " |  __weakref__\n",
        " |      list of weak references to the object (if defined)\n",
        "\n"
       ]
      }
     ],
     "source": [
      "class Vehicle(object):\n",
      "    '''\n",
      "    The Vehicle object contains lots of vehicles\n",
      "    :param arg: The arg is used for ...\n",
      "    :type arg: str\n",
      "    :param `*args`: The variable arguments are used for ...\n",
      "    :param `**kwargs`: The keyword arguments are used for ...\n",
      "    :ivar arg: This is where we store arg\n",
      "    :vartype arg: str\n",
      "    '''\n",
      "\n",
      "\n",
      "    def __init__(self, arg, *args, **kwargs):\n",
      "        self.arg = arg\n",
      "\n",
      "    def cars(self, distance, destination):\n",
      "        '''We can't travel a certain distance in vehicles without fuels, so here's the fuels\n",
      "\n",
      "        :param distance: The amount of distance traveled\n",
      "        :type amount: int\n",
      "        :param bool destinationReached: Should the fuels be refilled to cover required distance?\n",
      "        :raises: :class:`RuntimeError`: Out of fuel\n",
      "\n",
      "        :returns: A Car mileage\n",
      "        :rtype: Cars\n",
      "        '''  \n",
      "        pass\n",
      "\n",
      "help(Vehicle)"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "通过插件，Sphinx 也能支持 Google Style Docstring 和 Numpy Style Docstring。\n",
      "\n",
      "以下两个链接，放在这里，以便你将来查询：\n",
      "\n",
      "> * [sphinx.ext.napoleon – Support for NumPy and Google style docstrings](http://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html)\n",
      "> * [sphinx.ext.autodoc – Include documentation from docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html)"
     ]
    }
   ],
   "metadata": {
    "kernelspec": {
     "display_name": "Python 3",
     "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.7.1"
    },
    "toc-autonumbering": true
   },
   "nbformat": 4,
   "nbformat_minor": 2
  }
  