{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# 一、调用函数\n",
    "当代码出现有规律的重复的时候，可借助抽象把这种完成同一工作的代码包装成函数。<br/>\n",
    "用户不关心底层的具体计算过程，而直接在更高的层次上思考问题。\n",
    "\n",
    "\n",
    "\n",
    "## Python内置了很多有用的函数，我们可以直接调用。\n",
    "\n",
    "要调用一个函数，需要知道函数的名称和参数，<br/>\n",
    "#### (1)绝对值的函数abs\n",
    "\n",
    "求绝对值的函数abs只有一个参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n",
      "20\n",
      "12.34\n"
     ]
    }
   ],
   "source": [
    "print(abs(100))\n",
    "print(abs(-20))\n",
    "print(abs(12.34))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调用函数的时候，如果传入的**参数数量不对**，会报TypeError的错误，并且Python会明确地告诉你：abs()有且仅有1个参数，但给出了两个："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "abs() takes exactly one argument (2 given)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-2-6c188a838f2b>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mabs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: abs() takes exactly one argument (2 given)"
     ]
    }
   ],
   "source": [
    "abs(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果传入的参数数量是对的，但**参数类型不能被函数所接受**，也会报TypeError的错误，并且给出错误信息：str是错误的参数类型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "bad operand type for abs(): 'str'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-3-f2001f88707b>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mabs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: bad operand type for abs(): 'str'"
     ]
    }
   ],
   "source": [
    "abs('a')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (2)max函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max(2, 3, 1, -5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max([2, 3, 1, -5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (3)数据类型转换\n",
    "Python内置的常用函数还包括数据类型转换函数，比如int()函数可以把其他数据类型转换为整数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "123\n",
      "12\n",
      "12.34\n",
      "1.23\n",
      "True\n",
      "False\n",
      "False\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "print(int('123'))\n",
    "print(int(12.34))\n",
    "print(float('12.34'))\n",
    "print(str(1.23))\n",
    "print(bool(-4))\n",
    "print(bool(0))\n",
    "print(bool(''))\n",
    "print(bool([]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (4)函数的内存结构<br/>\n",
    "**函数名其实就是指向一个函数对象的引用**，完全可以把函数名赋给一个变量，相当于给这个函数起了一个“别名”："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = abs # 变量a指向abs函数\n",
    "a(-1) # 所以也可以通过a调用abs函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 二、定义函数\n",
    "**def:**\n",
    "\n",
    "\n",
    "在Python中，定义一个函数要使用**def语句**，依次写出函数名、括号、括号中的参数和冒号:，然后，在缩进块中编写函数体，函数的返回值用return语句返回。\n",
    "\n",
    "我们以自定义一个求绝对值的my_abs函数为例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "99\n"
     ]
    }
   ],
   "source": [
    "def my_abs(x):\n",
    "    if x >= 0:\n",
    "        return x\n",
    "    else:\n",
    "        return -x\n",
    "    \n",
    "print(my_abs(-99))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**空函数**<br/>\n",
    "如果想定义一个什么事也不做的空函数，可以用**pass语句**：<br/>\n",
    "pass可以用来作为占位符，比如现在还没想好怎么写函数的代码，就可以先放一个pass，让代码能运行起来。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def nop():\n",
    "    pass\n",
    "\n",
    "a =1 \n",
    "b = nop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# pass还可以用在其他语句里，比如：\n",
    "age =19\n",
    "if age >= 18:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**参数检查**<br/>\n",
    "调用函数时，如果**参数个数不对**，Python解释器会自动检查出来，并抛出TypeError："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "my_abs() takes 1 positional argument but 2 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-10-f9ac958f5dc5>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: my_abs() takes 1 positional argument but 2 were given"
     ]
    }
   ],
   "source": [
    " my_abs(1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'>=' not supported between instances of 'str' and 'int'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-11-3a4b4137ec2c>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'A'\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<ipython-input-8-6f681765edec>\u001b[0m in \u001b[0;36mmy_abs\u001b[1;34m(x)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\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----> 2\u001b[1;33m     \u001b[1;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\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      3\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m     \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[1;33m-\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: '>=' not supported between instances of 'str' and 'int'"
     ]
    }
   ],
   "source": [
    "my_abs('A')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "bad operand type for abs(): 'str'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-12-6eee3e7a739d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mabs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'A'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: bad operand type for abs(): 'str'"
     ]
    }
   ],
   "source": [
    "abs('A')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**自定义函数加入参数检查**<br/>\n",
    "当传入了不恰当的参数时，内置函数abs会检查出参数错误，而我们定义的my_abs没有参数检查，会导致if语句出错，<br/>\n",
    "出错信息和abs不一样。所以，这个函数定义不够完善。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "修改一下my_abs的定义，对参数类型做检查，只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_abs(x):\n",
    "    if not isinstance(x, (int, float)):\n",
    "        raise TypeError('bad operand type')\n",
    "    if x >= 0:\n",
    "        return x\n",
    "    else:\n",
    "        return -x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "bad operand type",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-22-33563144f726>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'A'\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      2\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'aaaa'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-21-c24a51d84b1e>\u001b[0m in \u001b[0;36mmy_abs\u001b[1;34m(x)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mmy_abs\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\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[0;32m      2\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mint\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfloat\u001b[0m\u001b[1;33m)\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----> 3\u001b[1;33m         \u001b[1;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'bad operand type'\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      4\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: bad operand type"
     ]
    }
   ],
   "source": [
    "my_abs('A')\n",
    "print('aaaa')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 三、返回多个值\n",
    "函数可以返回多个值吗？答案是肯定的。\n",
    "\n",
    "比如在游戏中经常需要从一个点移动到另一个点，给出坐标、位移和角度，就可以计算出新的新的坐标："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "151.96152422706632 70.0\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "def move(x, y, step, angle=0):\n",
    "    nx = x + step * math.cos(angle)\n",
    "    ny = y - step * math.sin(angle)\n",
    "    return nx, ny\n",
    "\n",
    "x, y = move(100, 100, 60, math.pi / 6)\n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "其实，这只是一种假象，Python函数返回的仍然是单一值："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "返回值是一个tuple！但是，在语法上，返回一个tuple可以省略括号，**多个变量可以同时接收一个tuple，**按位置赋给对应的值，<br/>\n",
    "所以，Python的函数返回多值其实就是返回一个tuple，但写起来更方便。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(151.96152422706632, 70.0)\n"
     ]
    }
   ],
   "source": [
    "r = move(100, 100, 60, math.pi / 6)\n",
    "print(r)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 函数定义小结\n",
    "* 定义函数时，需要确定函数名和参数个数；<br/>\n",
    "* 如果有必要，可以先对参数的数据类型做检查；<br/>\n",
    "* 函数体内部可以用return随时返回函数结果；<br/>\n",
    "* 函数执行完毕也没有return语句时，自动return None。<br/>\n",
    "* 函数可以同时返回多个值，但其实就是一个tuple。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "# 定义函数解一元二次方程\n",
    "def quadratic(a, b, c):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "quadratic(2, 3, 1) = None\n",
      "quadratic(1, 3, -4) = None\n"
     ]
    }
   ],
   "source": [
    "# 测试: 查看返回值\n",
    "print('quadratic(2, 3, 1) =', quadratic(2, 3, 1)) \n",
    "print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数字：2\n",
      "数字：3\n",
      "数字：-2\n",
      "(0.5, -2.0)\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "def quadratic(a, b, c):\n",
    "    tmp = (b * b) - (4 * a * c)\n",
    "    if tmp > 0:\n",
    "        n1 = (-b + math.sqrt(tmp)) / (2 * a)\n",
    "        n2 = (-b - math.sqrt(tmp)) / (2 * a)\n",
    "        return n1, n2\n",
    "    elif tmp == 0:\n",
    "        n1 = (-b + math.sqrt(tmp)) / (2 * a)\n",
    "        n2 = n1\n",
    "        return n1\n",
    "    else:\n",
    "        return ('此方程无解。')\n",
    "    \n",
    "a = float(input('数字：'))\n",
    "b = float(input('数字：'))\n",
    "c = float(input('数字：'))\n",
    "print(quadratic(a, b, c))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 四、函数的参数\n",
    "\n",
    "定义函数的时候，我们把参数的名字和位置确定下来，函数的接口定义就完成了。对于**函数的调用**者来说，只需要知道如何**传递正确的参数**，以及函数将**返回什么样的值**就够了。\n",
    "\n",
    "Python的函数定义非常简单，但灵活度却非常大。除了正常定义的必选参数外，还可以使用**默认参数、可变参数和关键字参数**，使得函数定义出来的接口，不但能处理复杂的参数，还可以简化调用者的代码。\n",
    "#### （1）函数调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def power(x, n):\n",
    "    s = 1\n",
    "    while n > 0:\n",
    "        n = n - 1\n",
    "        s = s * x\n",
    "    return s\n",
    "\n",
    "power(5, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "power() missing 1 required positional argument: 'n'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-4-fe7074d26c14>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 参数传递错误报错\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mpower\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m5\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: power() missing 1 required positional argument: 'n'"
     ]
    }
   ],
   "source": [
    "# 参数传递错误报错\n",
    "power(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (2)默认参数\n",
    "默认参由于我们经常计算x2，可以把第二个参数n的**默认值**设定为2："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2,)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power.__defaults__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def power(x, n=2):\n",
    "    s = 1\n",
    "    while n > 0:\n",
    "        n = n - 1\n",
    "        s = s * x\n",
    "    return s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当我们调用power(5)时，相当于调用power(5, 2)："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "125"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(5,3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "设**置默认参数时，有几点要注意**：\n",
    "\n",
    "一是**必选参数在前，默认参数在后**，否则Python的解释器会报错（思考一下为什么默认参数不能放在必选参数前面）；<br/>\n",
    "\n",
    "二是如何设置默认参数。\n",
    "\n",
    "当函数有多个参数时，把变化大的参数放前面，变化小的参数放后面。变化小的参数就可以作为默认参数。\n",
    "\n",
    "使用默认参数有什么好处？最大的好处是能降低调用函数的难度"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "eg:函数需要传入name和gender两个参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (<ipython-input-30-aaedb20a5048>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-30-aaedb20a5048>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    fun0(a=2,b):\u001b[0m\n\u001b[1;37m                ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "fun0(a=2,b):\n",
    "    pass\n",
    "\n",
    "fun0(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "def enroll(name, gender):\n",
    "    print('name:', name)\n",
    "    print('gender:', gender)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Sarah\n",
      "gender: F\n"
     ]
    }
   ],
   "source": [
    "enroll('Sarah', 'F')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果要继续传入年龄、城市等信息怎么办？这样会使得调用函数的复杂度大大增加。\n",
    "\n",
    "我们可以把年龄和城市设为默认参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(6, 'Beijing')\n",
      "name: Sarah\n",
      "gender: F\n",
      "age: 6\n",
      "city: Beijing\n"
     ]
    }
   ],
   "source": [
    "def enroll(name, gender, age=6, city='Beijing'):\n",
    "    print('name:', name)\n",
    "    print('gender:', gender)\n",
    "    print('age:', age)\n",
    "    \n",
    "    print('city:', city)\n",
    "print(enroll.__defaults__)    \n",
    "enroll('Sarah', 'F')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "只有与默认参数不符的学生才需要提供额外的信息："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Adam\n",
      "gender: M\n",
      "age: 6\n",
      "city: Tianjin\n"
     ]
    }
   ],
   "source": [
    "# enroll('Bob', 'M', 7)  # 使用默认city\n",
    "enroll('Adam', 'M', city='Tianjin') # 使用默认age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(6, 'Beijing')"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 默认参数以字典存放在function的object中\n",
    "enroll.__defaults__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可见，默认参数降低了函数调用的难度，而一旦需要更复杂的调用时，又可以传递更多的参数来实现。无论是简单调用还是复杂调用，函数只需要定义一个。\n",
    "\n",
    "有多个默认参数时，调用的时候，既可以按顺序提供默认参数，比如调用enroll('Bob', 'M', 7)，意思是，除了name，gender这两个参数外，最后1个参数应用在参数age上，city参数由于没有提供，仍然使用默认值。\n",
    "\n",
    "也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时，需要把参数名写上。比如调用enroll('Adam', 'M', city='Tianjin')，意思是，city参数用传进去的值，其他默认参数继续使用默认值。\n",
    "\n",
    "默认参数很有用，但使用不当，也会掉坑里。默认参数有个最大的坑，演示如下：\n",
    "\n",
    "#### 默认参数的不当使用\n",
    "先定义一个函数，传入一个list，添加一个END再返回："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 'END']"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add_end(L=[]):\n",
    "    L.append('END')\n",
    "    return L\n",
    "\n",
    "add_end([1, 2, 3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['x', 'y', 'z', 'END']"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 正常调用\n",
    "add_end(['x', 'y', 'z'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END']"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用默认参数调用时，一开始结果也是对的：\n",
    "add_end()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END', 'END']"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 但是，再次调用add_end()时，结果就不对了：\n",
    "add_end()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END', 'END', 'END']"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_end()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "很多初学者很疑惑，默认参数是[]，但是函数似乎每次都“记住了”上次添加了'END'后的list。\n",
    "\n",
    "原因解释如下：\n",
    "\n",
    "Python**函数在定义的时候，默认参数L的值就被计算出来了**，即**[ ]**，因为默认参数L也是一个变量，\n",
    "它指向对象[]，每次调用该函数，如果改变了L的内容，则下次调用时，默认参数的内容就变了，不再是函数定义时的[]了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " 定义默认参数要牢记一点：**默认参数必须指向不变对象！**\n",
    " \n",
    " #### 上面程序应该如何修改？？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 要修改上面的例子，我们可以用None这个不变对象来实现,这样不会出错了\n",
    "def add_end(L=None):\n",
    "    if L is None:\n",
    "        L = []\n",
    "    L.append('END')\n",
    "    return L"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，无论调用多少次，都不会有问题："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END']"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_end()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END']"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_end()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为什么要设计str、None这样的不变对象呢？因为不变对象一旦创建，对象内部的数据就不能修改，\n",
    "这样就减少了由于修改数据导致的错误。此外，由于对象不变，多任务环境下同时读取对象不需要加锁，同时读一点问题都没有。\n",
    "我们在编写程序时，如果可以设计一个不变对象，那就尽量设计成不变对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 五、可变参数\n",
    "在Python函数中，还可以定义可变参数。顾名思义，可变参数就是传入的**参数个数是可变的**，可以是1个、2个到任意个，还可以是0个。\n",
    "\n",
    "我们以数学题为例子，给定一组数字a，b，c……，请计算a2 + b2 + c2 + ……。\n",
    "\n",
    "要定义出这个函数，我们必须确定输入的参数。由于参数个数不确定，我们首先想到可以把a，b，c……作为一个list或tuple传进来，这样，函数可以定义如下：\n",
    "\n",
    "#### （1）参数个数不定"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calc(numbers):\n",
    "    sum = 0\n",
    "    for n in numbers:\n",
    "        sum = sum + n * n\n",
    "    return sum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "calc() takes 1 positional argument but 3 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-22-eabd6e9978a9>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 如果利用可变参数，调用函数的方式可以简化成这样：\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mcalc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: calc() takes 1 positional argument but 3 were given"
     ]
    }
   ],
   "source": [
    "# 如果利用可变参数，调用函数的方式可以简化成这样：\n",
    "calc(1, 2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "14\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "84"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用的时候，需要先组装出一个list或tuple：\n",
    "print(calc([1, 2, 3]))\n",
    "calc((1, 3, 5, 7))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (2)函数定义时：把函数的参数改为可变参数：\n",
    "\n",
    "定义函数时，<font color='red'>\\*代表收集参数，\\**代表收集关键字参数 </font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calc(*numbers):  # *号  把调用的参数收集成元组\n",
    "    sum = 0\n",
    "    for n in numbers:\n",
    "        sum = sum + n * n\n",
    "    return sum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "calc(1, 2, 3)  # "
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "定义可变参数和定义一个list或tuple参数相比，仅仅在参数前面加了一个*号。在函数内部，参数numbers接收到的是一个tuple，因此，函数代码完全不变。但是，调用该函数时，可以传入任意个参数，包括0个参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 5)"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "calc(),calc(1,2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### （3）调用默认参数的函数时，在list或tuple前加* 可以把序列变成可变参数\n",
    "调用时用**list或者tuple封装**了参数，那么可以这样做："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "nums = [1, 2, 3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# calc(nums)  #  error\n",
    "calc(nums[0], nums[1], nums[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这种写法当然是可行的，问题是太繁琐<br/>\n",
    "\n",
    "#### （4）函数调用时：使用*号 分配参数\n",
    "在**list或tuple前面加一个*号，把list或tuple的元素变成可变参数**传进去："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "calc(*nums)  # *nums可以把 list拆开"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### （5）*nums表示把nums这个list的所有元素作为可变参数传进去。\n",
    "这种写法相当有用，而且很常见。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 1 3 3\n",
      "1 2 3\n"
     ]
    }
   ],
   "source": [
    "# *[1, 2,1,3, 3]        # 拆开后存在很多个数据，无法引用\n",
    "print(*[1, 2,1,3, 3])   # 但以用print打印\n",
    "print(*(1,2,3)) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "can't use starred expression here (<ipython-input-2-1968918f525b>, line 8)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-2-1968918f525b>\"\u001b[1;36m, line \u001b[1;32m8\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m can't use starred expression here\n"
     ]
    }
   ],
   "source": [
    "# {*[1, 2,1,3, 3]}  #ok\n",
    "# [*(1, 2,1,3, 3)]  # ok\n",
    "# (*[1, 2,1,3, 3])  #error\n",
    "# [*[1, 2,1,3, 3]]  # ok\n",
    "# (*(1, 2,1,3, 3))   #error"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'d': 88, 'x': '#'}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "kw = {'d': 88, 'x': '#'}  # 字典拆开还是只能封装字典\n",
    "dict(**kw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'d' is an invalid keyword argument for this function",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-12-073ae61ff1f2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# [**kw]  # error\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m**\u001b[0m\u001b[0mkw\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      3\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;33m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[1;33m}\u001b[0m    \u001b[1;31m# 只能封装成字典\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: 'd' is an invalid keyword argument for this function"
     ]
    }
   ],
   "source": [
    "# [**kw]  # error\n",
    "print(**kw)\n",
    "{**kw}    # 只能封装成字典"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1,2,3,4,5,\n"
     ]
    }
   ],
   "source": [
    "j = ''\n",
    "for i in \"12345\":\n",
    "    j += i + ','\n",
    "print(j)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'PYTHON'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "j=0\n",
    "j+=1\n",
    "\n",
    "\n",
    "s='PYTHON'\n",
    "\"{0:3}\".format(s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "如果在定义和调用函数时，都使用*号呢？\n",
    "这样将值传递元组或字典，没有实际意义，不发挥收集参数或者分配参数的意义，不如不用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "参考： https://blog.csdn.net/zkk9527/article/details/88675129"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 六、关键字参数\n",
    "可变参数允许你传入0个或任意个参数，这些**可变参数在函数调用时自动组装为一个tuple**。\n",
    "而关键字参数允许你传入0个或任意个含参数名的参数，这些**关键字参数在函数内部自动组装为一个dict**。请看示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, **kw):  # **双星号形参是接收字典\n",
    "    print('name:', name, 'age:', age, 'other:', kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数person除了必选参数name和age外，还接受关键字参数kw。在调用该函数时，可以只传入必选参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Michael age: 30 other: {}\n"
     ]
    }
   ],
   "source": [
    "person('Michael', 30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Bob age: 35 other: {'city': 'Beijing'}\n"
     ]
    }
   ],
   "source": [
    "# **号的形式参数接收字典，调用时使用关键字赋值\n",
    "person('Bob', 35, city='Beijing')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}\n"
     ]
    }
   ],
   "source": [
    "person('Adam', 45, gender='M', job='Engineer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关键字参数有什么用？它可以扩展函数的功能。\n",
    "比如，在person函数里，我们保证能接收到name和age这两个参数，但是，如果调用者愿意提供更多的参数，\n",
    "我们也能收到。试想你正在做一个用户注册的功能，除了用户名和年龄是必填项外，其他都是可选项，利用关键字参数来定义这个函数就能满足注册的需求。\n",
    "\n",
    "和可变参数类似，也可以先组装出一个dict，然后，把该dict转换为关键字参数传进去："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}\n"
     ]
    }
   ],
   "source": [
    "extra = {'city': 'Beijing', 'job': 'Engineer'}\n",
    "person('Jack', 24, city=extra['city'], job=extra['job'])  # 这样写有点傻"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当然，上面复杂的调用可以用简化的写法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}\n"
     ]
    }
   ],
   "source": [
    "extra = {'city': 'Beijing', 'job': 'Engineer'}\n",
    "person('Jack', 24, **extra)   # 较好的写法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数，kw将获得一个dict，注意kw获得的dict是extra的一份拷贝，对kw的改动不会影响到函数外的extra。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 七、命名关键字参数\n",
    "由于函数的调用者可以**传入任意不受限制的关键字参数**。至于到底传入了哪些，就需要在函数内部通过kw检查。\n",
    "\n",
    "仍以person()函数为例，我们希望检查是否有city和job参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, **kw):\n",
    "    if 'city' in kw:\n",
    "        print('有city参数')\n",
    "    if 'job' in kw:\n",
    "        # 有job参数\n",
    "        pass\n",
    "    print('name:', name, 'age:', age, 'other:', kw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "有city参数\n",
      "name: Jack age: 24 other: {'city': 'Beijing', 'addr': 'Chaoyang', 'zipcode': 123456}\n"
     ]
    }
   ],
   "source": [
    "# 但是调用者仍可以传入不受限制的关键字参数：\n",
    "person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果要**限制关键字参数的名字**，就可以用命名关键字参数，例如，只接收city和job作为关键字参数。这种方式定义的函数如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 限制关键字名称\n",
    "def person(name, age, *, city, job):\n",
    "    print(name, age, city, job)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和关键字参数**kw不同，命名关键字参数需要一个特殊分隔符*，*后面的参数被视为命名关键字参数。\n",
    "\n",
    "调用方式如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 Beijing Engineer\n"
     ]
    }
   ],
   "source": [
    "person('Jack', 24, city='Beijing', job='Engineer')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "person() got an unexpected keyword argument 'cityyy'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-85-c26d3159b0ec>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mperson\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Jack'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m24\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcityyy\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'Beijing'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mjob\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'Engineer'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: person() got an unexpected keyword argument 'cityyy'"
     ]
    }
   ],
   "source": [
    "person('Jack', 24, cityyy='Beijing', job='Engineer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**函数定义中已经有了一个可变参数**，后面跟着的命名关键字参数就不再需要一个特殊分隔符*了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 () Beijing Engineer\n"
     ]
    }
   ],
   "source": [
    "def person(name, age, *args, city, job):\n",
    "    print(name, age, args, city, job)\n",
    "person('Jack', 24, city='Beijing', job='Engineer')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "person() missing 2 required keyword-only arguments: 'city' and 'job'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-88-d3e3a315b65a>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 命名关键字参数必须传入参数名，这和位置参数不同。如果没有传入参数名，调用将报错：\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mperson\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Jack'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m24\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Beijing'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Engineer'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: person() missing 2 required keyword-only arguments: 'city' and 'job'"
     ]
    }
   ],
   "source": [
    "# 命名关键字参数必须传入参数名，这和位置参数不同。如果没有传入参数名，调用将报错：\n",
    "person('Jack', 24, 'Beijing', 'Engineer')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 ('Beijing', 'Engineer') Nancang Engineer\n"
     ]
    }
   ],
   "source": [
    "person('Jack', 24, 'Beijing', 'Engineer',city='Nancang',job='Engineer')  # 应该这样调用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于调用时缺少参数名city和job，Python解释器把这4个参数均视为位置参数，但person()函数仅接受2个位置参数。\n",
    "\n",
    "命名关键字参数可以有缺省值，从而简化调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, *, city='Beijing', job):\n",
    "    print(name, age, city, job)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack 24 Beijing Engineer\n"
     ]
    }
   ],
   "source": [
    "# 由于命名关键字参数city具有默认值，调用时，可不传入city参数：\n",
    "person('Jack', 24, job='Engineer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用命名关键字参数时，要特别注意，如果没有可变参数，就必须加一个*作为特殊分隔符。如果缺少*，Python解释器将无法识别位置参数和命名关键字参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, city, job):\n",
    "    # 缺少 *，city和job被视为位置参数\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 八、参数组合\n",
    "在Python中定义函数，可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数，这5种参数都可以组合使用。但是请注意，参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。\n",
    "\n",
    "**一个函数，包含上述若干种参数：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f1(a, b, c=0, *args, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)\n",
    "\n",
    "def f2(a, b, c=0, *, d, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**调用**的时候，Python解释器自动按照参数位置和参数名把对应的参数传进去。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 0 args = () kw = {}\n",
      "None\n",
      "a = 1 b = 2 c = 3 args = () kw = {}\n",
      "None\n",
      "a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}\n",
      "None\n",
      "a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}\n",
      "None\n",
      "\n",
      "a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "print(f1(1, 2))\n",
    "print(f1(1, 2, c=3))\n",
    "print(f1(1, 2, 3, 'a', 'b'))\n",
    "print(f1(1, 2, 3, 'a', 'b', x=99))\n",
    "print()\n",
    "print(f2(1, 2, d=99, ext=None))\n",
    "# 每行后面的None是f1,f2默认的返回值"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最神奇的是**通过一个tuple和dict**，你也可以调用上述函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f1(a, b, c=0, *args, **kw):\n",
    "    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}\n"
     ]
    }
   ],
   "source": [
    "args = (1, 2, 3, 4)\n",
    "kw = {'d': 99, 'x': '#'}\n",
    "\n",
    "f1(*args, **kw)  #args = (1, 2, 3, 4)被依次给a,b,c剩下的4给形参*args，字典给**kw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "args = (1, 2, 3)\n",
    "kw = {'d': 88, 'x': '#'}\n",
    "f1(*args, **kw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以，对于任意函数，都可以通过类似func(*args, **kw)的形式调用它，无论它的参数是如何定义的。\n",
    "\n",
    "\n",
    "虽然可以组合多达5种参数，但不要同时使用太多的组合，否则函数接口的可理解性很差。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 递归函数\n",
    "\n",
    "\n",
    "在函数内部，可以调用其他函数。如果一个函数在内部调用自身本身，这个函数就是递归函数。\n",
    "\n",
    "举个例子，我们来计算阶乘n! = 1 x 2 x 3 x ... x n，用函数fact(n)表示，可以看出：\n",
    "\n",
    "fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n\n",
    "\n",
    "所以，fact(n)可以表示为n x fact(n-1)，只有n=1时需要特殊处理。\n",
    "\n",
    "于是，fact(n)用递归的方式写出来就是："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fact(n):\n",
    "    if n==1:\n",
    "        return 1\n",
    "    return n * fact(n - 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "120\n",
      "3628800\n"
     ]
    }
   ],
   "source": [
    "print(fact(1))\n",
    "print(fact(5))\n",
    "print(fact(10))"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "如果我们计算fact(5)，可以根据函数定义看到计算过程如下：\n",
    "\n",
    "===> fact(5)\n",
    "===> 5 * fact(4)\n",
    "===> 5 * (4 * fact(3))\n",
    "===> 5 * (4 * (3 * fact(2)))\n",
    "===> 5 * (4 * (3 * (2 * fact(1))))\n",
    "===> 5 * (4 * (3 * (2 * 1)))\n",
    "===> 5 * (4 * (3 * 2))\n",
    "===> 5 * (4 * 6)\n",
    "===> 5 * 24\n",
    "===> 120\n",
    "\n",
    "递归函数的优点是定义简单，逻辑清晰。理论上，所有的递归函数都可以写成循环的方式，但循环的逻辑不如递归清晰。\n",
    "\n",
    "使用递归函数需要注意防止栈溢出。在计算机中，函数调用是通过栈（stack）这种数据结构实现的，每当进入一个函数调用，栈就会加一层栈帧，每当函数返回，栈就会减一层栈帧。由于栈的大小不是无限的，所以，递归调用的次数过多，会导致栈溢出。可以试试fact(1000)：\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用递归函数需要注意防止栈溢出。在计算机中，函数调用是通过栈（stack）这种数据结构实现的，<br/>\n",
    "每当进入一个函数调用，栈就会加一层栈帧，每当函数返回，栈就会减一层栈帧。<br/>\n",
    "栈的大小不是无限的，所以，递归调用的次数过多，会导致栈溢出。可以试试fact(1000)："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fact(1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "解决递归调用栈溢出的方法是通过尾递归优化，事实上尾递归和循环的效果是一样的，所以，把循环看成是一种特殊的尾递归函数也是可以的。\n",
    "\n",
    "尾递归是指，在函数返回的时候，调用自身本身，并且，return语句不能包含表达式。这样，编译器或者解释器就可以把尾递归做优化，使递归本身无论调用多少次，都只占用一个栈帧，不会出现栈溢出的情况。\n",
    "\n",
    "上面的fact(n)函数由于return n * fact(n - 1)引入了乘法表达式，所以就不是尾递归了。要改成尾递归方式，需要多一点代码，主要是要把每一步的乘积传入到递归函数中："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fact(n):\n",
    "    return fact_iter(n, 1)\n",
    "\n",
    "def fact_iter(num, product):\n",
    "    if num == 1:\n",
    "        return product\n",
    "    return fact_iter(num - 1, num * product)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def fact(n):\n",
    "    if n==1:\n",
    "        return 1\n",
    "    return n * fact(n - 1)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "可以看到，return fact_iter(num - 1, num * product)仅返回递归函数本身，num - 1和num * product在函数调用前就会被计算，不影响函数调用。\n",
    "\n",
    "fact(5)对应的fact_iter(5, 1)的调用如下：\n",
    "\n",
    "===> fact_iter(5, 1)\n",
    "===> fact_iter(4, 5)\n",
    "===> fact_iter(3, 20)\n",
    "===> fact_iter(2, 60)\n",
    "===> fact_iter(1, 120)\n",
    "===> 120"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "尾递归调用时，如果做了优化，栈不会增长，因此，无论多少次调用也不会导致栈溢出。\n",
    "\n",
    "遗憾的是，大多数编程语言没有针对尾递归做优化，Python解释器也没有做优化，所以，即使把上面的fact(n)函数改成尾递归方式，也会导致栈溢出。\n",
    "\n",
    "\n",
    "小结\n",
    "使用递归函数的优点是逻辑简单清晰，缺点是过深的调用会导致栈溢出。\n",
    "\n",
    "针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的，没有循环语句的编程语言只能通过尾递归实现循环。\n",
    "\n",
    "Python标准的解释器没有针对尾递归做优化，任何递归函数都存在栈溢出的问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# eval函数\n",
    " eval是Python的一个内置函数，功能十分强大，这个函数的作用是，返回**传入字符串的表达式的结果**。就是说：将字符串当成有效的表达式 来求值 并 返回计算结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "16\n"
     ]
    }
   ],
   "source": [
    "#1.eval无参实现字符串转化\n",
    "s = '1+2+3*5-2'\n",
    "print(eval(s))  #"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "#2.字符串中有变量也可以\n",
    "x = 1\n",
    "print(eval('x+2'))  #3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'linux', 'age': 18}\n"
     ]
    }
   ],
   "source": [
    "#eval传递本地变量，既有global和local时，变量值先从local中查找。\n",
    "age=18\n",
    "print(eval(\"{'name':'linux','age':age}\",{\"age\":1822},locals()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'linux', 'age': 1822}\n",
      "{'name': 'linux', 'age': 1823}\n"
     ]
    }
   ],
   "source": [
    "print(eval(\"{'name':'linux','age':age}\",{\"age\":1822}))\n",
    "#输出结果：{'name': 'linux', 'age': 1822}\n",
    "\n",
    "print(eval(\"{'name':'linux','age':age}\",{\"age\":1822},{\"age\":1823}))\n",
    "#输出结果：{'name': 'linux', 'age': 1823}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## exec() 函数执行指定的 Python 代码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bill\n"
     ]
    }
   ],
   "source": [
    "x = 'name = \"Bill\"\\nprint(name)'\n",
    "exec(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iter time: 0\n",
      "iter time: 1\n",
      "iter time: 2\n",
      "iter time: 3\n",
      "iter time: 4\n"
     ]
    }
   ],
   "source": [
    "#  多行语句字符串\n",
    "exec (\"\"\"for i in range(5):\n",
    " print (\"iter time: %d\" % i)\n",
    " \"\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "60\n",
      "33\n",
      "34\n"
     ]
    }
   ],
   "source": [
    "x = 10\n",
    "expr = \"\"\"\n",
    "z = 30\n",
    "sum = x + y + z\n",
    "print(sum)\n",
    "\"\"\"\n",
    "def func():\n",
    "    y = 20\n",
    "    exec(expr)\n",
    "    exec(expr, {'x': 1, 'y': 2})\n",
    "    exec(expr, {'x': 1, 'y': 2}, {'y': 3, 'z': 4})\n",
    "    \n",
    "func()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**eval() 函数的语法格式为：**<br/>\n",
    "**eval(expression, globals=None, locals=None, /)**<br/>\n",
    "\n",
    "\n",
    "而 exec() 函数的语法格式如下：\n",
    "exec(expression, globals=None, locals=None, /)\n",
    "\n",
    "\n",
    "可以看到，二者的语法格式除了函数名，其他都相同，其中各个参数的具体含义如下："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* expression：这个参数是一个字符串，代表要执行的语句 。该语句受后面两个字典类型参数 globals 和 locals 的限制，只有在 globals 字典和 locals 字典作用域内的函数和变量才能被执行。\n",
    "* globals：这个参数管控的是一个全局的命名空间，即 expression 可以使用全局命名空间中的函数。如果只是提供了 globals 参数，而没有提供自定义的 __builtins__，则系统会将当前环境中的 __builtins__ 复制到自己提供的 globals 中，然后才会进行计算；如果连 globals 这个参数都没有被提供，则使用 Python 的全局命名空间。\n",
    "* locals：这个参数管控的是一个局部的命名空间，和 globals 类似，当它和 globals 中有重复或冲突时，以 locals 的为准。如果 locals 没有被提供，则默认为 globals。<br/>\n",
    "**注意** :__builtins__ 是 Python 的内建模块，平时使用的 int、str、abs 都在这个模块中。通过 print(dic[\"__builtins__\"]) 语句可以查看 __builtins__ 所对应的 value。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dict_keys(['b'])\n",
      "dict_keys(['b', '__builtins__', 'a'])\n"
     ]
    }
   ],
   "source": [
    "dic={} #定义一个字典\n",
    "dic['b'] = 3 #在 dic 中加一条元素，key 为 b\n",
    "print (dic.keys()) #先将 dic 的 key 打印出来，有一个元素 b\n",
    "\n",
    "exec(\"a = 4\", dic) #在 exec 执行的语句后面跟一个作用域 dic\n",
    "print(dic.keys()) #exec 后，dic 的 key 多了一个"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的代码是在作用域 dic 下执行了一句 a = 4 的代码。可以看出，exec() 之前 dic 中的 key 只有一个 b。执行完 exec() 之后，系统在 dic 中生成了两个新的 key，分别是 a 和 __builtins__。其中，a 为执行语句生成的变量，系统将其放到指定的作用域字典里；__builtins__ 是系统加入的内置 key。\n",
    "\n",
    "locals参数的用法就很简单了，举个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "116\n"
     ]
    }
   ],
   "source": [
    "a=10;b=20;c=30\n",
    "g={'a':6, 'b':8} #定义一个字典\n",
    "t={'b':100, 'c':10} #定义一个字典\n",
    "print(eval('a+b+c', g, t)) #定义一个字典 a=6;b=100;c=10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**exec()和eval()的区别**\n",
    "前面已经讲过，它们的区别在于，eval() 执行完会返回结果，而 exec() 执行完不返回结果。举个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "None\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "a = 1\n",
    "exec(\"a = 2\") #相当于直接执行 a=2\n",
    "print(a)\n",
    "\n",
    "a = exec(\"2+3\") #相当于直接执行 2+3，但是并没有返回值，a 应为 None\n",
    "print(a)\n",
    "\n",
    "a = eval('2+3') #执行 2+3，并把结果返回给 a\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (<string>, line 1)",
     "output_type": "error",
     "traceback": [
      "Traceback \u001b[1;36m(most recent call last)\u001b[0m:\n",
      "  File \u001b[0;32m\"C:\\Users\\root\\Anaconda3\\envs\\py_36\\lib\\site-packages\\IPython\\core\\interactiveshell.py\"\u001b[0m, line \u001b[0;32m3343\u001b[0m, in \u001b[0;35mrun_code\u001b[0m\n    exec(code_obj, self.user_global_ns, self.user_ns)\n",
      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-13-74611c877940>\"\u001b[1;36m, line \u001b[1;32m3\u001b[1;36m, in \u001b[1;35m<module>\u001b[1;36m\u001b[0m\n\u001b[1;33m    a= eval(\"a = 2\")\u001b[0m\n",
      "\u001b[1;36m  File \u001b[1;32m\"<string>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    a = 2\u001b[0m\n\u001b[1;37m      ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "# exec() 中最适合放置运行后没有结果的语句，而 eval() 中适合放置有结果返回的语句。\n",
    "# 如果 eval() 里放置一个没有结果返回的语句会怎样呢？例如下面代码：\n",
    "a= eval(\"a = 2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**eval() 和 exec() 函数的应用场景**<br/>\n",
    "在使用 Python 开发服务端程序时，这两个函数应用得非常广泛。例如，客户端向服务端发送一段字符串代码，服务端无需关心具体的内容，直接跳过 eval() 或 exec() 来执行，这样的设计会使服务端与客户端的耦合度更低，系统更易扩展。\n",
    "\n",
    "另外，如果读者以后接触 TensorFlow 框架，就会发现该框架中的静态图就是类似这个原理实现的：<br/>\n",
    "* TensorFlow 中先将张量定义在一个静态图里，这就相当将键值对添加到字典里一样；\n",
    "* TensorFlow 中通过 session 和张量的 eval() 函数来进行具体值的运算，就当于使用 eval() 函数进行具体值的运算一样。\n",
    "\n",
    "需要注意的是，在使用 eval() 或是 exec() 来处理请求代码时，函数 eval() 和 exec() 常常会被黑客利用，成为可以执行系统级命令的入口点，进而来攻击网站。**解决方法是：**通过设置其命名空间里的可执行函数，来限制 eval() 和 exec() 的执行范围。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "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.13.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "302px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
