{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 函数也是数据:初级篇"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "前面我们已经提过一次,**函数也是一种对象**。\n",
    "\n",
    "或者说:**函数也是数据**(*function is data*),是差不多的。\n",
    "\n",
    "这是个重要的时刻,从现在开始我们对函数的理解将有一个新的飞跃。函数虽然是操作数据的工具,但它自己也是个数据对象,我们可以对它进行各种各样的操作,甚至在运行时动态的构造出一个函数来,这些都是 Python(以及很多流行的现代化编程语言)的亮点。我们在这一部分的最后还会就这个话题进行展开,从而引入一系列非常有用的“**函数式**(*functional*)”编程工具,在目前这一章我们先稍微品尝一下这个概念,来看看函数别名和匿名函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数别名"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Python 中,所有函数也是对象,证据就是它们都有对象 id。Python 会为创建的每一个对象(不管基本数据类型,还是某个 *class* 的实例)指定一个唯一的 id,可以用内置函数 `id()` 来查看,比如:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4356419792"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n = 42\n",
    "id(n)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数也有这个 id,比如:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def _is_leap(year):\n",
    "    return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4394147424"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(_is_leap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "既然函数有 id,是个对象,那是什么类型的对象呢?可以用内置函数 `type` 来看:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "function"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(_is_leap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以函数是个 `function` 类型的对象。\n",
    "\n",
    "既然是个对象,我们就可以用赋值语句来创建函数的**别名**(*alias*):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "is_leap = _is_leap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4394147424"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(is_leap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到,这两个函数的 id 完全一样,是同一个对象的两个名字而已。我们可以用这两个名字来调用这个函数,完全没区别:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_is_leap(2018)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "is_leap(2018)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么,我们为什么需要给函数取别名呢?\n",
    "\n",
    "很多时候是为了提供更好的代码可读性,比如在特定上下文让某个函数的作用更显而易见,比如以前的例子里,我们曾经在 `Cat` 类里给父类 `Animal` 的 `voice()` 方法定义别名叫 `meow()`。\n",
    "\n",
    "还有一种情况是一个函数需要在运行时动态指向不同的实现版本。这里只简单描述一个典型场景:假定我们要渲染一段视频,如果系统里有兼容的显卡(GPU),就调用显卡来渲染,会更快更省电,如果没有则用 CPU 来渲染,会慢一点和更耗电一点,于是我们把用 GPU 渲染的算法写成函数 `_render_by_gpu()`,用 CPU 渲染的算法写成函数 `_render_by_cpu()`,而检测是否存在可用 GPU 的算法写成函数 `is_gpu_available()`,然后可以用下面的方法来定义一个函数 `render`:\n",
    "\n",
    "```python\n",
    "if is_gpu_available():\n",
    "    render = _render_by_gpu\n",
    "else:\n",
    "    render = _render_by_cpu\n",
    "```\n",
    "\n",
    "这样 `render()` 就成为一个当前系统中最优化的渲染函数,在程序的其他地方就不用管细节,直接用这个函数就好。这就是动态函数别名的价值。\n",
    "\n",
    "顺便说一句,在任何一个工程里,为函数或者变量取名都是**很简单却不容易**的事情——因为可能会重名(虽然已经尽量用变量的作用域隔离了),可能会因取名含混而令后来者费解,所以,仅仅为了少敲几下键盘而给一个函数取个更短的别名,实际上并不是好主意,更不是好习惯。尤其现在的编辑器都支持自动补全和多光标编辑的功能,变量名长点不是什么大问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 匿名函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有的函数需要两个甚至更多名字,有的函数却一个也不要,人生就是这么丰富多彩啊!\n",
    "\n",
    "所谓匿名函数,就是有时候我们需要一个函数,但就在一个地方,用完就扔,再也不会用了,Python 对这种情况提供了一个方便的语法,不需要 `def` 那套严肃完整的语法,一行就可以写完一个函数,这个语法使用关键字 `lambda`。`lambda` 是希腊字母 `λ` 的英语音译,在计算机领域是个来头不小的词儿,代表了一系列高深的理论,[和阿伦佐·丘奇(Alonzo Church)的理论有关](https://en.wikipedia.org/wiki/Lambda_calculus),有兴趣的话可以自行研究。\n",
    "\n",
    "不过目前我们不需要管那么多,只要了解怎么快速创建“用过即扔”的小函数就好了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "比如下面这个很简单的函数:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add(x, y):\n",
    "    return x + y\n",
    "add(3, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以用 `lambda` 来改写:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add = lambda x, y: x + y\n",
    "add(3, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "甚至更简单一点,名字也不要了:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(lambda x, y: x + y)(3, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后这种形式,就是典型的匿名函数了。简单地说,`lambda` 可以生成一个函数对象,出现在所有需要一个函数的地方,可以将其赋给一个变量(如上面的 `add`),这个变量就称为函数变量(别名),可以当函数用;也可以直接把 `lambda` 语句用括号括起来当一个函数用(上面后一种形式)。\n",
    "\n",
    "在 Python 官方文档中,`lambda` 语句的语法定义是这样的:\n",
    "\n",
    "`lambda_expr ::= \"lambda\" [parameter_list] \":\" expression`\n",
    "\n",
    "这个语法定义采用的是 [巴克斯范式(BNF)](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form)标注,现在不明白没关系(虽然对照上面的例子也能猜出个大概吧),以后我们会专门介绍。\n",
    "\n",
    "其实也很简单,就是这个样子:\n",
    "\n",
    "```python\n",
    "lambda x, y: x + y\n",
    "```\n",
    "\n",
    "先写上 `lambda` 关键字,其后分为两个部分,`:` 之前是参数表,之后是表达式,这个表达式的值,就是这个函数的返回值。**注意**:`lambda` 语句中,`:` 之后有且只能有一个表达式,所以它搞不出很复杂的函数,比较适合一句话的函数。\n",
    "\n",
    "而这个函数呢,没有名字,所以被称为 “匿名函数”。\n",
    "\n",
    "`add = lambda x, y: x + y`\n",
    "\n",
    "就相当于是给一个没有名字的函数取了个名字。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们在后面的学习中会遇到很多 `lambda` 的例子,因为有很多地方需要这种就用一次而且一句话的小函数,目前大家只要对这个概念和语法有所理解并且记住就可以了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 函数也是对象,有 *id*,是 `function` 类型;\n",
    "* 所以函数也可以被赋值给一个变量,把那个变量变成自己的别名(*alias*);\n",
    "* 可以用 `lambda` 来创建一次性、一句话的小函数,在很多场景下都很有用。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}