{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# !nvidia-smi"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 匿名函数\n",
    "\n",
    "当我们在传入函数时，有些时候，不需要显式地定义函数，直接传入匿名函数更方便。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**关键字lambda表示匿名函数，冒号**前面的$x$表示函数参数。\n",
    "\n",
    "匿名函数有个限制，就是**只能有一个表达式，不用写return**，返回值就是该表达式的结果。\n",
    "\n",
    "用匿名函数有个好处，因为函数没有名字，不必担心函数名冲突。\n",
    "此外，匿名函数也是一个函数对象，也可以把匿名函数赋值给一个变量，再利用变量来调用该函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def func(x):\n",
    "    return x*x\n",
    "\n",
    "func(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function <lambda> at 0x7f49800bb0d0>\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = lambda x: x * x  # 相当于f具有了求平方的功能\n",
    "print(f)\n",
    "f(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "18\n"
     ]
    }
   ],
   "source": [
    "# 定义匿名函数列表\n",
    "g=[lambda a:a*2,lambda b:b*3]\n",
    "print(g[0](5))  #g[0]表示列表中的第一项，也即第0个匿名函数\n",
    "print(g[1](6))  #g[1]列表第二项， 第1个匿名函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 匿名函数精简代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def func(x,y):\n",
    "    return x - y\n",
    "func(3,4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1\n"
     ]
    }
   ],
   "source": [
    " print((lambda x,y:x-y)(3,4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "同样，也可以把匿名函数作为返回值返回，比如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function build.<locals>.<lambda> at 0x7f49800bb620>\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "13"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def build(x, y):\n",
    "    return lambda: x * x + y * y\n",
    "f = build(2,3)\n",
    "print(f)\n",
    "f()  # 这种一般是跟闭包使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " eg： 用匿名函数改造一般函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def is_odd(n):\n",
    "    return n % 2 == 1\n",
    "\n",
    "L = list(filter(is_odd, range(1, 20)))\n",
    "L"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "L = list(filter(lambda x: x % 2 == 1,range(1, 20)))\n",
    "L"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<filter at 0x7f49800f8fd0>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ff = filter(lambda x: x % 2 == 1,range(1, 20))\n",
    "ff"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "next(ff) # filter实际上是生成器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在Python中，对匿名函数提供了有限支持。还是以map()函数为例，计算f(x)=x2时，除了定义一个f(x)的函数外，还可以直接传入匿名函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 4, 9, 16, 25, 36, 49, 64, 81]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#通过对比可以看出，匿名函数lambda x: x * x实际上就是：\n",
    "def f(x):\n",
    "    return x * x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 4, 9, 16, 25, 36, 49, 64, 81]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一、变量指向函数\n",
    "以Python内置的求绝对值的函数abs()为例，调用该函数用以下代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "abs(-10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function abs>"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 但是，如果只写abs呢？\n",
    "abs   #abs是函数本身"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**abs(-10)是函数调用**<br/>\n",
    "**abs是函数本身。**\n",
    "\n",
    "**获得函数调用结果**，我可以把结果赋值给变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = abs(-10)\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 把函数本身赋值给变量呢"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 但是，如果把函数本身赋值给变量呢？\n",
    "f = abs\n",
    "f"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "结论：函数本身也可以赋值给变量，即：变量可以指向函数。\n",
    "\n",
    "如果一个变量指向了一个函数，那么，可否通过该变量来调用这个函数？用代码验证一下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = abs\n",
    "f(-10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数名也是变量\n",
    "那么函数名是什么呢？函数名其实就是指向函数的变量！对于abs()这个函数，完全可以把函数名abs看成变量，它指向一个可以计算绝对值的函数！\n",
    "\n",
    "**如果把abs指向其他对象**，会有什么情况发生？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'int' object is not callable",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-20-ca6a49bc4c99>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mabs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: 'int' object is not callable"
     ]
    }
   ],
   "source": [
    "abs = 10\n",
    "abs(-10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f(-10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%reset -f"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 所以千万不要乱用python的保留字"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把abs指向10后，就无法通过abs(-10)调用该函数了！因为abs这个变量已经不指向求绝对值函数而是指向一个整数10！\n",
    "\n",
    "当然实际代码绝对不能这么写，这里是为了说明函数名也是变量。要恢复abs函数，请重启Python交互环境。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注：由于abs函数实际上是定义在import builtins模块中的，所以要让修改abs变量的指向在其它模块也生效，\n",
    "要用import builtins; builtins.abs = 10。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二、传入函数\n",
    "既然变量可以指向函数，函数的参数能接收变量，那么**一个函数就可以接收另一个函数作为参数**，这种函数就称之为高阶函数。\n",
    "\n",
    "一个最简单的高阶函数：<br/>\n",
    "比如:我们要求-5，6的绝对值的和<br/>\n",
    "当我们调用add(-5, 6, abs)时，参数x，y和f分别接收-5，6和abs，根据函数定义，我们可以推导计算过程为："
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "x = -5\n",
    "y = 6\n",
    "f = abs\n",
    "f(x) + f(y) ==> abs(-5) + abs(6) ==> 11\n",
    "return 11"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11\n"
     ]
    }
   ],
   "source": [
    "f=abs\n",
    "def add(x, y, f):\n",
    "    return f(x) + f(y)\n",
    "\n",
    "print(add(-5, 6, f))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 三、map/reduce\n",
    "\n",
    "**Python内建了map()和reduce()函数。**\n",
    "\n",
    "如果你读过Google的那篇大名鼎鼎的论文“MapReduce: Simplified Data Processing on Large Clusters”，你就能大概明白map/reduce的概念。\n",
    "\n",
    "我们先看map。\n",
    "### (1)map()函数  ：把函数施加在可迭代数据上\n",
    "接收两个参数，一个是**函数，一个是Iterable**，map将传入的函数依次作用到序列的每个元素，并把结果作为新的Iterator返回。\n",
    "\n",
    "举例说明，比如我们有一个函数f(x)=x2，要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上，就可以用map()实现如下：\n",
    "\n"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "f(x) = x * x\n",
    "\n",
    "                  │\n",
    "                  │\n",
    "  ┌───┬───┬───┬───┼───┬───┬───┬───┐\n",
    "  │   │   │   │   │   │   │   │   │\n",
    "  ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼\n",
    "\n",
    "[ 1   2   3   4   5   6   7   8   9 ]\n",
    "\n",
    "  │   │   │   │   │   │   │   │   │\n",
    "  │   │   │   │   │   │   │   │   │\n",
    "  ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼   ▼\n",
    "\n",
    "[ 1   4   9  16  25  36  49  64  81 ]\n",
    "\n",
    "现在，我们用Python代码实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f(x):\n",
    "    return x * x\n",
    "\n",
    "r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "print(r)\n",
    "list(r)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "next(r)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 4, 9, 16, 25, 36, 49, 64, 81]"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(map(lambda x: x*x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "map()传入的第一个参数是f，即函数对象本身。由于结果r是一个Iterator，Iterator是惰性序列，因此通过list()函数让它把整个序列都计算出来并返回一个list。\n",
    "\n",
    "你可能会想，不需要map()函数，写一个循环，也可以计算出结果："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 4, 9, 16, 25, 36, 49, 64, 81]\n"
     ]
    }
   ],
   "source": [
    "L = []\n",
    "for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:\n",
    "    L.append(f(n))\n",
    "print(L)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "的确可以，但是，从上面的循环代码，能一眼看明白“把f(x)作用在list的每一个元素并把结果生成一个新的list”吗？\n",
    "\n",
    "**map()作为高阶函数，事实上它把运算规则抽象了**，因此，我们不但可以计算简单的f(x)=x2，还可以计算任意复杂的函数，比如，把这个list所有数字转为字符串："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['1', '2', '3', '4', '5', '6', '7', '8', '9']"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "只需要一行代码。**小结：**相当于对列表中的每个元素施加函数操作\n",
    "\n",
    "### (2)reduce函数。把函数施加在迭代数据上，每次处理2个参数\n",
    "reduce把一个函数作用在一个序列[x1, x2, x3, ...]上，这个函数必须接收两个参数，reduce把结果继续和序列的下一个元素做累积计算，其效果就是：\n",
    "#### reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**序列求和**，就可以用reduce实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from functools import reduce\n",
    "# 定义函数\n",
    "def add(x, y):\n",
    "     return x + y\n",
    "# 使用reduce求和\n",
    "reduce(add, [1, 3, 5, 7, 9])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reduce(lambda x,y: x+y, [1, 3, 5, 7, 9])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 当然如果数列求和可以直接使用sum\n",
    "sum([1, 3, 5, 7, 9])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是如果要把序列[1, 3, 5, 7, 9]转化成整数13579："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13579"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from functools import reduce\n",
    "def fn(x, y):\n",
    "    return x * 10 + y\n",
    "\n",
    "reduce(fn, [1, 3, 5, 7, 9])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个例子本身没多大用处，但是，如果考虑到字符串str也是一个序列，对上面的例子稍加改动，配合**map()**，就可以设计<br/>\n",
    "**str转换int的函数：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13579"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from functools import reduce\n",
    "def fn(x, y):\n",
    "    return x * 10 + y\n",
    "\n",
    "def char2num(s):\n",
    "    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}\n",
    "    return digits[s]\n",
    "\n",
    "reduce(fn, map(char2num, '13579'))  \n",
    "#  map(char2num, '13579')把字符串'13579'转换成[1, 3, 5, 7, 9];  list(map(char2num, '13579'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 3, 5, 7, 9]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(map(char2num, '13579'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 整理成一个str2int的函数就是："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from functools import reduce\n",
    "\n",
    "DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}\n",
    "# python允许函数嵌套\n",
    "def str2int(s):\n",
    "    def fn(x, y):\n",
    "        return x * 10 + y\n",
    "    def char2num(s):\n",
    "        return DIGITS[s]\n",
    "    return reduce(fn, map(char2num, s))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "13579"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "str2int('13579')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 可以用lambda函数进一步简化成："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "239"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from functools import reduce\n",
    "\n",
    "DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}\n",
    "\n",
    "def char2num(s):\n",
    "    return DIGITS[s]\n",
    "\n",
    "def str2int(s):\n",
    "    return reduce(lambda x, y: x * 10 + y, map(char2num, s))\n",
    "\n",
    "str2int('239')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也就是说，假设Python没有提供int()函数，你完全可以自己写一个把字符串转化为整数的函数，而且只需要几行代码！\n",
    "\n",
    "lambda函数的用法在后面介绍。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 四、filter ， 使用函数对一个序列进行过滤。\n",
    "\n",
    "Python内建的filter()函数用于过滤序列。\n",
    "\n",
    "和map()类似，**filter()也接收一个函数和一个序列。**和map()不同的是，filter()把传入的函数依次作用于每个元素，然后根据返回值是True还是False决定保留还是丢弃该元素。\n",
    "\n",
    "例如，在一个list中，**删掉偶数，只保留奇数**，可以这么写：\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 5, 9, 15]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def is_odd(n):\n",
    "    return n % 2 == 1\n",
    "\n",
    "list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))\n",
    "# 要用list()函数获得所有结果并返回list"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<filter at 0x1fc946005f8>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fil_1 = filter(lambda x:x % 2 == 1, [1, 2, 4, 5, 6, 9, 10, 15])\n",
    "fil_1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "next(fil_1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "把一个序列中的**空字符串删掉**，可以这么写："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['A', 'B', 'C']"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def not_empty(s):\n",
    "    return s and s.strip()\n",
    "\n",
    "list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可见用$filter()$这个高阶函数，关键在于正确实现一个**“筛选”**函数。<br/>\n",
    "\n",
    "**注意:**$filter()$函数**返回的是一个Iterator，**也就是一个惰性序列，所以要强迫filter()完成计算结果，需要用list()函数获得所有结果并返回list。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**用filter求素数:**\n",
    "\n",
    "计算素数的一个方法是埃氏筛法，它的算法理解起来非常简单：\n",
    "\n",
    "首先，列出从2开始的所有自然数，构造一个序列：\n",
    "\n",
    "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...\n",
    "\n",
    "取序列的第一个数2，它一定是素数，然后用2把序列的2的倍数筛掉：\n",
    "\n",
    "3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...\n",
    "\n",
    "取新序列的第一个数3，它一定是素数，然后用3把序列的3的倍数筛掉：\n",
    "\n",
    "5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...\n",
    "\n",
    "取新序列的第一个数5，然后用5把序列的5的倍数筛掉：\n",
    "\n",
    "7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...\n",
    "\n",
    "不断筛下去，就可以得到所有的素数。\n",
    "\n",
    "用Python来实现这个算法，可以先构造一个从3开始的奇数序列："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# (1)生成器，并且是一个无限序列。\n",
    "def _odd_iter():\n",
    "    n = 1\n",
    "    while True:\n",
    "        n = n + 2\n",
    "        yield n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# (2)定义一个筛选函数：\n",
    "def _not_divisible(n):\n",
    "    return lambda x: x % n > 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# (3)定义一个生成器，不断返回下一个素数：\n",
    "def primes():\n",
    "    yield 2\n",
    "    it = _odd_iter() # 初始序列\n",
    "    while True:\n",
    "        n = next(it) # 返回序列的第一个数\n",
    "        yield n\n",
    "        it = filter(_not_divisible(n), it) # 构造新序列\n",
    "# 生成器先返回第一个素数2，然后，利用filter()不断产生筛选后的新的序列。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "3\n",
      "5\n",
      "7\n",
      "11\n",
      "13\n",
      "17\n",
      "19\n"
     ]
    }
   ],
   "source": [
    "# (4)打印1000以内的素数:\n",
    "for n in primes():\n",
    "    if n < 20:   # 由于primes()也是一个无限序列，所以调用时需要设置一个退出循环的条件：\n",
    "        print(n)\n",
    "    else:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# sorted\n",
    "\n",
    "**排序算法**<br/>\n",
    "排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序，排序的核心是比较两个元素的大小。如果是数字，我们可以直接比较，但如果是字符串或者两个dict呢？直接比较数学上的大小是没有意义的，因此，比较的过程必须通过函数抽象出来。\n",
    "\n",
    "Python内置的sorted()函数就可以对list进行排序：\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[-21, -12, 5, 9, 36]"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted([36, 5, -12, 9, -21])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**sorted()函数也是一个高阶函数**，它还可以**接收一个key函数**来实现自定义的排序，例如按绝对值大小排序："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[5, 9, -12, -21, 36]"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted([36, 5, -12, 9, -21], key=abs)   # 对列表中的元素施加key指向的函数操作，然后根据结果排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def abs2(x):\n",
    "    return abs(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[5, 9, -12, -21, 36]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted([36, 5, -12, 9, -21], key=abs2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "字符串排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Credit', 'Zoo', 'about', 'bob']"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted(['bob', 'about', 'Zoo', 'Credit'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "默认情况下，对字符串排序，是按照ASCII的大小比较的，由于'Z' < 'a'，结果，大写字母Z会排在小写字母a的前面。\n",
    "\n",
    "现在，我们提出排序应该忽略大小写，按照字母序排序。要实现这个算法，不必对现有代码大加改动，只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串，实际上就是先把字符串都变成大写（或者都变成小写），再比较。\n",
    "\n",
    "**给sorted传入key函数，即可实现忽略大小写**的排序："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['about', 'bob', 'Credit', 'Zoo']"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**反向排序，**不必改动key函数，可以传入第三个参数**reverse=True：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Zoo', 'Credit', 'bob', 'about']"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Zoo', 'Credit', 'bob', 'about']"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sl = ['bob', 'about', 'Zoo', 'Credit']\n",
    "sl.sort(key=str.lower, reverse=True)\n",
    "sl"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 五、返回函数\n",
    "\n",
    "\n",
    "函数作为返回值\n",
    "高阶函数除了可以接受函数作为参数外，还可以把函数作为结果值返回。\n",
    "\n",
    "通常情况下，求和的函数是这样定义的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def calc_sum(*args):\n",
    "    ax = 0\n",
    "    for n in args:\n",
    "        ax = ax + n\n",
    "    return ax   \n",
    "# 正常情况下函数调用会立即求和"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**如果不需要立刻求和，**而是在后面的代码中，根据需要再计算怎么办？可以不返回求和的结果，而是返回求和的函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def lazy_sum(*args):\n",
    "    def sum_in(a):\n",
    "        ax = 0\n",
    "        for n in args:\n",
    "            ax = ax + n\n",
    "        return ax + a\n",
    "    return sum_in  #返回内部定义的函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.lazy_sum.<locals>.sum_in(a)>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 当我们调用lazy_sum()时，返回的并不是求和结果，而是求和函数：\n",
    "f = lazy_sum(1, 3, 5, 7, 9)\n",
    "f"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "27"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用函数f时，才真正计算求和的结果：\n",
    "f(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个例子中，我们**在函数lazy_sum中又定义了函数sum，**并且，**内部函数sum可以引用外部函数lazy_sum的参数和局部变量**，<br />\n",
    "当**lazy_sum返回函数sum时，相关参数和变量都保存在返回的函数中**，这种称为**“闭包（Closure）**”的程序结构拥有极大的威力。\n",
    "\n",
    "请再注意一点，当我们调用lazy_sum()时，每次调用都会返回一个新的函数，即使传入相同的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f1 = lazy_sum(1, 3, 5, 7, 9)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f2 = lazy_sum(1, 3, 5, 7, 9)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f1==f2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 六、闭包\n",
    "**闭包概念：**<br/>在一个**内部函数中，对外部作用域的变量进行引用**，(并且一般外部函数的返回值为内部函数)，那么内部函数就被认为是闭包。<br/>\n",
    "举个栗子先："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fun_out(a):\n",
    "    def fun_in(b):\n",
    "        return a + b\n",
    "    return fun_in\n",
    "\n",
    "fun1 = fun_out(1) # 先把1给a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function __main__.fun_out.<locals>.fun_in>"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fun1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fun1(3)  # 把3传给b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fun1(6)  # 把6给b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fun5 = fun_out(5) # fun5 使用初始值5去加，而fun1使用初始值1去加\n",
    "fun5(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "fun1和fun5两个函数的定义相同，只是携带的自由变量不同，便成为了两个函数。**闭包可以作为函数工厂，生产出功能类似，但是会有细微差别的函数**。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fun_out(a):\n",
    "    c = 1\n",
    "    def fun_in(b):\n",
    "        return a + b + c\n",
    "    c = 3\n",
    "    return fun_in"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fun1 = fun_out(1)  # a=1\n",
    "fun1(2)  # b=2     # 此时c=3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fun1(6)   #  a=1, b=6 ,c=3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**闭包的作用**<br/>\n",
    "闭包可以保存当前的运行环境，以一个类似棋盘游戏的例子来说明。假设棋盘大小为50*50，左上角为坐标系原点(0,0)，我需要一个函数，接收2个参数，分别为方向(direction)，步长(step)，该函数控制棋子的运动。 这里需要说明的是，每次运动的起点都是上次运动结束的终点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "origin = [0, 0] # 坐标系统原点 \n",
    "legal_x = [0, 50] # x轴方向的合法坐标 \n",
    "legal_y = [0, 50] # y轴方向的合法坐标 \n",
    "def create(pos=origin): \n",
    "    def player(direction,step): \n",
    "      # 这里应该首先判断参数direction,step的合法性，比如direction不能斜着走，step不能为负等 \n",
    "      # 然后还要对新生成的x，y坐标的合法性进行判断处理，这里主要是想介绍闭包，就不详细写了。 \n",
    "            new_x = pos[0] + direction[0]*step \n",
    "            new_y = pos[1] + direction[1]*step \n",
    "            pos[0] = new_x \n",
    "            pos[1] = new_y \n",
    "            #注意！此处不能写成 pos = [new_x, new_y]，原因在上文有说过 \n",
    "            return pos \n",
    "    return player \n",
    "  \n",
    "player = create() # 创建棋子player，起点为原点 \n",
    "print (player([1,0],10)) # 向x轴正方向移动10步 \n",
    "print (player([0,1],20)) # 向y轴正方向移动20步 \n",
    "print( player([-1,0],10)) # 向x轴负方向移动10步 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 闭包引用循环出现的错误\n",
    "\n",
    "原本设想f1,f2,f3返回1，2，3的平方"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def count():\n",
    "    fs = []\n",
    "    for i in range(1, 4):\n",
    "        def f():\n",
    "             return i*i\n",
    "        fs.append(f)\n",
    "    return fs\n",
    "\n",
    "f1, f2, f3 = count()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上面的例子中，每次循环，都创建了一个新的函数，然后，把创建的3个函数都返回了。\n",
    "\n",
    "你可能认为调用f1()，f2()和f3()结果应该是1，4，9，但实际结果是："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9\n",
      "9\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "print(f1())\n",
    "print(f2())\n",
    "print(f3())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**为什么全部都是9？**原因就在于返回的函数引用了变量i，但**它并非立刻执行**。等到3个函数都返回时，它们所引用的变量i已经变成了3，因此最终结果为9。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "返回闭包时牢记一点：返回函数不要引用任何循环变量，或者后续会发生变化的变量。 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**如果一定要引用循环变量怎么办？**<br/>\n",
    "方法是再创建一个函数，用该函数的参数绑定循环变量当前的值，无论该循环变量后续如何更改，已绑定到函数参数的值不变："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def count():\n",
    "    def f(j):\n",
    "        def g():\n",
    "            return j*j\n",
    "        return g\n",
    "    fs = []\n",
    "    for i in range(1, 2):\n",
    "        fs.append(f(i)) # f(i)立刻被执行，因此i的当前值被传入f()\n",
    "    return fs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "f1, f2, f3 = count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "4\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "print(f1())\n",
    "print(f2())\n",
    "print(f3())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 装饰器是闭包的一个应用，只是携带的自由  变量是一个函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 (70, 80) {'name': 'aaa'}\n",
      "2 70 80 aaa\n"
     ]
    }
   ],
   "source": [
    "def print1(func):\n",
    "    def wrapper(*args, **kw):\n",
    "        print(1,args,kw)\n",
    "        return func(*args, **kw)\n",
    "    return wrapper\n",
    "\n",
    "@print1\n",
    "def print2(a,b,name='nn'):\n",
    "    print(2,a,b,name)\n",
    "\n",
    "print2(70,80,name='aaa')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 七、装饰器\n",
    "\n",
    "#### 是一种代码运行期间动态增加功能的方式<br/>\n",
    "由于函数也是一个对象，而且函数对象可以被赋值给变量，所以，通过变量也能调用函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2015-3-25\n"
     ]
    }
   ],
   "source": [
    "def now():\n",
    "     print('2015-3-25')\n",
    "\n",
    "f = now\n",
    "f()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数对象有一个__name__属性，可以拿到函数的名字："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('now', 'now')"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "now.__name__, f.__name__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，假设我们要增强now()函数的功能，比如，在函数调用前后自动打印日志，但又不希望修改now()函数的定义，<br />\n",
    "这种**在代码运行期间动态增加功能的方式**，称之为**“装饰器”（Decorator）。**  **接受一个函数作为参数，并返回一个函数。**\n",
    "\n",
    "**本质上，decorator就是一个返回函数的高阶函数**。比如，我们要定义一个能打印日志的decorator，可以定义如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def log(func):  #接收一个函数func作为参数\n",
    "    def wrapper(*args, **kw):\n",
    "        print('call %s():' % func.__name__)\n",
    "        print(args,kw)\n",
    "        return func(*args, **kw)\n",
    "    return wrapper  #返回内部定义的一个函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "观察上面的log，因为它是一个decorator，所以接受一个函数作为参数，并返回一个函数。<br/>\n",
    "**借助Python的@语法，把decorator置于函数的定义处**："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 把@log放到now()函数的定义处"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "@log  # 相当于now=log(now)\n",
    "def now():\n",
    "    print('2015-3-25')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "call now():\n",
      "() {}\n",
      "2015-3-25\n"
     ]
    }
   ],
   "source": [
    "now()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "调用now()函数，不仅会运行now()函数本身，还会在运行now()函数前打印一行日志："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于log()是一个decorator，返回一个函数，所以，原来的now()函数仍然存在，只是现在同名的now变量指向了新的函数，于是调用now()将执行新函数，即在log()函数中返回的wrapper()函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 看完一脸懵"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1、 什么是装饰器？？\n",
    "装饰器可以让一个Python函数拥有原本没有的功能，也就是你可以通过装饰器，让一个平淡无奇的函数变的强大，变的漂亮。\n",
    "#### 2、为什么Python要引入装饰器？\n",
    "因为引入装饰器会便于开发，便于代码复用，可以把烂泥扶上墙，就跟开美颜照相一样\n",
    "\n",
    "#### 装饰器有利于解决哪些问题？\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "####  （1）当你执行 孙悟空() 这个函数，就打印出它目前的技能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "吃桃子\n"
     ]
    }
   ],
   "source": [
    "def 孙悟空():\n",
    "    print('吃桃子')\n",
    "孙悟空()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (2)你希望孙悟空有火眼金睛，该怎么做呢？\n",
    "当然，可以直接在**孙悟空()**这个函数中修改，但这样做需要**改动原来的函数**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "吃桃子\n",
      "火眼金睛\n"
     ]
    }
   ],
   "source": [
    "def 孙悟空():\n",
    "    print('吃桃子')\n",
    "    print('火眼金睛')\n",
    "孙悟空()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (3)用装饰器来装饰他，让他在原本基础上扩展出新的功能。\n",
    "这样可以以**不改动原来的函数**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function 炼丹炉.<locals>.变身 at 0x7f80e81de510>\n",
      "有火眼金睛了\n",
      "吃桃子\n"
     ]
    }
   ],
   "source": [
    "# a、方案一\n",
    "def 孙悟空():  \n",
    "    print('吃桃子')\n",
    "\n",
    "def 炼丹炉(func): \n",
    "    def 变身(*args, **kwargs):  \n",
    "        print('有火眼金睛了') \n",
    "        return func(*args, **kwargs) \n",
    "    return 变身 \n",
    "\n",
    "# 把“孙悟空()”放入“炼丹炉()”,此时“新_孙悟空”就是“炼丹炉()”中的“变身()”\n",
    "新_孙悟空 = 炼丹炉(孙悟空) \n",
    "\n",
    "print(新_孙悟空)\n",
    "新_孙悟空() # 执行炼丹程序，新的孙悟空出世"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "有火眼金睛了\n",
      "吃桃子\n"
     ]
    }
   ],
   "source": [
    "# b,方案二,使用方案一写起来比较麻烦，python给出了快捷的方式，叫语法糖，就是用@注释\n",
    "def 炼丹炉(func): # func就是‘孙悟空’这个函数\n",
    "    #*args, **kwargs就是‘孙悟空’的参数列表，这里的‘孙悟空’函数没有传参数，建议都写上\n",
    "    def 变身(*args, **kwargs):   \n",
    "            print('有火眼金睛了') # 加特效，增加新功能，比如孙悟空的进了炼丹炉后，有了火眼金睛技能  \n",
    "            return func() #保留原来的功能，原来孙悟空的技能，如吃桃子\n",
    "    return 变身 # 炼丹成功，更强大的，有了火眼金睛技能的孙悟空出世\n",
    "\n",
    "@炼丹炉  #相当于执行了“孙悟空=炼丹炉(孙悟空)”\n",
    "def 孙悟空():\n",
    "   print('吃桃子')\n",
    "\n",
    "孙悟空()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### (4)可以一次性在一个函数上用多个装饰器吗？\n",
    "\n",
    "当然可以，下面我们给孙悟空，弄个金箍棒，让他学会72变，学会飞"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "学会飞、72变了\n",
      "有金箍棒了\n",
      "有火眼金睛了\n",
      "吃桃子\n"
     ]
    }
   ],
   "source": [
    "def 炼丹炉(func):\n",
    "    def 变身(*args, **kwargs):\n",
    "        print('有火眼金睛了')\n",
    "        return func(*args, **kwargs)\n",
    "    return 变身\n",
    "\n",
    "def 龙宫走一趟(func):\n",
    "    def 你好(*args, **kwargs):\n",
    "        print('有金箍棒了')\n",
    "        return func(*args, **kwargs)\n",
    "    return 你好\n",
    "\n",
    "def 拜师学艺(func):\n",
    "    def 师傅(*args, **kwargs):\n",
    "        print('学会飞、72变了')\n",
    "        return func(*args, **kwargs)\n",
    "    return 师傅\n",
    "\n",
    "@拜师学艺\n",
    "@龙宫走一趟\n",
    "@炼丹炉  \n",
    "def 孙悟空():\n",
    "    print('吃桃子')\n",
    "\n",
    "孙悟空()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sharp eyes \n",
      "fly、72changes\n",
      "eat peaches\n"
     ]
    }
   ],
   "source": [
    "def alchemyStove(func2):\n",
    "    def transformer(*args, **kwargs):\n",
    "        print('Sharp eyes ')\n",
    "        return func2(*args, **kwargs)\n",
    "    return transformer\n",
    "\n",
    "def master(func1):\n",
    "    def masterWorker(*args, **kwargs):\n",
    "        print('fly、72changes')\n",
    "        return func1(*args, **kwargs)\n",
    "    return masterWorker\n",
    "\n",
    "@alchemyStove  \n",
    "@master\n",
    "def monkeyKing():\n",
    "    print('eat peaches')\n",
    "\n",
    "monkeyKing()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#引入装饰器的原因主要是为了在不修改原有函数代码的情况下，增加额外功能，比如：插入日志、性能测试、事务处理、缓存、权限校验等场景。\n",
    "#装饰器可以抽离出大量与函数功能本身无关的雷同代码并继续重用。\n",
    "#实现的原理其实就是又写了一个日志函数(例如log函数)，将功能函数(func)作为参数传递进去，并把功能函数(func)的调用写在log函数中，除此之外，\n",
    "# 进行一些额外的操作. 考虑以下代码：\n",
    "def log(func):\n",
    "    def wrapper(*args, **kwargs):\n",
    "        print('run function %s' % func.__name__)\n",
    "        return func(*args, **kwargs)\n",
    "    return wrapper\n",
    "\n",
    "@log\n",
    "def add(a, b):\n",
    "    return a + b\n",
    "\n",
    "@log\n",
    "def sub(a, b):\n",
    "    return a - b\n",
    "\n",
    "\n",
    "print(add(1, 2))\n",
    "print(sub(1, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "add(1,2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a = [1,2,3,]\n",
    "b ='ss'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class aa:\n",
    "    @classmethod\n",
    "    @staticmethod\n",
    "    def classme():\n",
    "        passs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 八、偏函数\n",
    "\n",
    "Python的functools模块提供了很多有用的功能，其中一个就是偏函数（Partial function）。要注意，这里的偏函数和数学意义上的偏函数不一样。<br/>\n",
    "偏函数通过设定参数的默认值，可以降低函数调用的复杂度。<br/>\n",
    "\n",
    "举例如下：**int()函数**可以把字符串转换为整数，**按默认按十进制转换**如下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12345"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "int('12345')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**int()函数还提供额外的base参数**，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "64"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "int('1000000',base=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5349"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "int('12345', base=8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "18"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "int('12', 16)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假设要转换大量的二进制字符串，每次都传入int(x, base=2)非常麻烦，\n",
    "**可以定义一个int2()的函数，默认把base=2传进去：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "64\n",
      "85\n"
     ]
    }
   ],
   "source": [
    "def int2(x, base=2):\n",
    "    return int(x, base)\n",
    "\n",
    "# 这样，我们转换二进制就非常方便了：\n",
    "print(int2('1000000'))\n",
    "print(int2('1010101'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**functools.partial就是帮助我们创建一个偏函数**，不需要我们自己定义int2()，可以直接使用下面的代码创建一个新的函数int2："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import functools\n",
    "int2 = functools.partial(int, base=2)\n",
    "int2('1000000')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "int2('1010101')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "简单总结：**functools.partial的作用就是，把一个函数的某些参数给固定住（也就是设置默认值），返回一个新的函数，**调用这个新函数会更简单。\n",
    "\n",
    "注意到上面的新的int2函数，仅仅是把base参数重新设定默认值为2，但也可以在函数调用时传入其他值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "int2('1000000', base=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，创建偏函数时，实际上可以接收函数对象、*args和**kw这3个参数，当传入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "int2 = functools.partial(int, base=2)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "实际上固定了int()函数的关键字参数base，也就是：\n",
    "int2('10010')\n",
    "相当于：\n",
    "\n",
    "kw = { 'base': 2 }\n",
    "int('10010', **kw)\n",
    "\n",
    "当传入：\n",
    "\n",
    "max2 = functools.partial(max, 10)\n",
    "\n",
    "实际上会把10作为*args的一部分自动加到左边，也就是：\n",
    "\n",
    "max2(5, 6, 7)\n",
    "\n",
    "相当于：\n",
    "\n",
    "args = (10, 5, 6, 7)\n",
    "max(*args)\n",
    "\n",
    "结果为10。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**小结**\n",
    "当函数的参数个数太多，需要简化时，使用functools.partial可以创建一个新的函数，这个新函数可以固定住原函数的部分参数，从而在调用时更简单。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "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.8.0"
  },
  "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": "255.867px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
