上传文件至 /

This commit is contained in:
lifulin 2024-06-30 12:03:17 +08:00
commit 669c8aeb20
5 changed files with 3432 additions and 0 deletions

603
函数.ipynb Normal file
View File

@ -0,0 +1,603 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 函数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"函数,实际上是可被调用的完整的程序。它具备输入、处理、输出的功能。\n",
"\n",
"函数包含两个方面:\n",
"\n",
"> * 它的**输入**是怎么构成的(都有哪些参数?如何指定?);\n",
"> * 以及它的**输出**是什么(返回值究竟是什么?)……\n",
"\n",
"```python\n",
"def cow(grass)\n",
" return milk\n",
"```\n",
"\n",
"我们使用函数的过程中,我们常常*有意忽略*它的内部如何完成从输入到输出之间的*处理过程* —— 这就好像我们平日里用灯泡一样,大多数情况下,我们只要知道开关的使用方法就够了 —— 至于为什么按到这个方向上灯会亮,为什么按到另外一个方向上灯会灭,并不是我们作为用户必须关心的事情……\n",
"\n",
"当然,如果你是设计开关的人就不一样了,你必须知道其中的运作原理;但是,最终,你还是希望你的用户用最简单方便的操作界面,而不是必须搞懂所有原理才能够使用你所设计的产品……\n",
"\n",
"当我们用 Python 编程的时候,更多的情况下,我们只不过是在使用别人已经写好的函数."
]
},
{
"cell_type": "markdown",
"metadata": {
"toc-hr-collapsed": true
},
"source": [
"## 示例 print()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 基本的使用方法"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"它最基本的作用就是把传递给它的值输出到屏幕上,如果不给它任何参数,那么它就输出一个空行:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"line 1st\n",
"line 2nd\n",
"\n",
"line 4th\n"
]
}
],
"source": [
"print('line 1st')\n",
"print('line 2nd')\n",
"print()\n",
"print('line 4th')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"也可以传递多个参数,参数之间用 `,` 分开,它就会把那些值逐个输出到屏幕,每个值之间默认用空格分开。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello, jack mike ... and all you guys!\n"
]
}
],
"source": [
"print('Hello,', 'jack', 'mike', '...', 'and all you guys!')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"当我们想把变量或者表达式的值插入字符串中的时候,可以用 f-string"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ann is 22 years old.\n"
]
}
],
"source": [
"name = 'Ann'\n",
"age = '22'\n",
"print(f'{name} is {age} years old.')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"但这并不是 `print()` 这个函数的功能,这实际上是 [`f-string`](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting) 的功能,`f-string` 中用花括号 `{}` 括起来的部分是表达式,最终转换成字符串的时候,那些表达式的值(而不是变量或者表达式本身)会被插入相应的位置……"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Ann is 22 years old.'"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"name = 'Ann'\n",
"age = '22'\n",
"f'{name} is {age} years old.'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"所以,`print(f'{name} is {age} years old.')` 这一句中,函数 `print()` 完成的还是它最基本的功能:给它什么,它就把什么输出到屏幕上。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### print() 的官方文档说明"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"最必须读懂的部分,就是这一行:\n",
"\n",
"> `print(*object, sep=' ', end='\\n', file=sys.stdout, flush=False)` <a href='#fn1' name='fn1b'><sup>[1]</sup></a>\n",
"\n",
"先只注意那些有着 `=` 的参数,`sep=' '`、`end='\\n'`\n",
"\n",
"这其中,先关注这三个 `sep=' '`、`end='\\n'`\n",
"\n",
"> * `sep=' '`:接收多个参数之后,输出时,分隔符号默认为空格,`' '`\n",
"> * `end='\\n'`:输出行的末尾默认是换行符号 `'\\n'`\n",
"\n",
"也就是说,这个函数中有若干个具有默认值的参数,即便我们在调用这个函数的时候,就算没有指定它们,它们也存在于此。\n",
"\n",
"即,当我们调用 `print('Hello', 'world!')` 的时候,相当于我们调用的是 `print('Hello', 'world!', sep=' ', end='\\n', file=sys.stdout, flush=False)`"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello world!\n",
"Hello-world!\tHello~world!\n",
"Hello\n",
"world!\n"
]
}
],
"source": [
"print('Hello', 'world!') # 下一行的输出和这一行相同\n",
"print('Hello', 'world!', sep='-', end='\\t')\n",
"print('Hello', 'world!', sep='~') # 上一行的末尾是 \\t所以这一行并没有换行显示\n",
"print('Hello', 'world!', sep='\\n') # 参数之间用换行 \\n 分隔 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> `print()` 这个函数的返回值是 `None` —— 注意,它向屏幕输出的内容,与 `print()` 这个函数的返回值不是一回事。\n",
"\n",
"做为例子,看看 `print(print(1))` 这个语句 —— `print()` 这个函数被调用了两次,第一次是 `print(1)`,它向屏幕输出了一次,完整的输出值实际上是 `str(1) + '\\n'`,而后返回一个值,`None`;而第二次调用 print(),这相当于是向屏幕输出这个 `None`"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"print(1)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"None\n"
]
}
],
"source": [
"print(print(1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 关键字参数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在 Python 中,函数的参数,有两种:\n",
"> * **位置参数**Positional Arguments在官方文档里常被缩写为 *arg*\n",
"> * **关键字参数**Keyword Arguments在官方文档里常被缩写为 *kwarg*\n",
"\n",
"在函数定义中,带有 `=` 的,即,已为其设定了默认值的参数,叫做 Keyword Arguments其它的是 Positional Arguments。\n",
"\n",
"在调用有 Keyword Arguments 的函数之时,如若不提供这些参数,那么参数在执行时,启用的是它在定义的时候为那些 Keyword Arguments 所设定的默认值;如若提供了这些参数的值,那么参数在执行的时候,启用的是接收到的相应值。\n",
"\n",
"比如,`sorted()` 函数,它的定义如下:\n",
"\n",
"> `sorted(iterable, *, key=None, reverse=False)`\n",
"\n",
"现在先只关注它的 Keyword Arguments`reverse`"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['a', 'b', 'c', 'd']"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"['d', 'c', 'b', 'a']"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"\n",
"sorted('abdc')\n",
"sorted('abdc', reverse=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 位置参数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"它接收且必须接收两个参数。\n",
"\n",
"> * 当你调用这个函数的时候,括号里写的第一个参数,是被除数,第二个参数是除数 —— 此为该函数的输入;\n",
"> * 而它的返回值是一个元组Tuple至于这是什么东西后面讲清楚其中包括两个值第一个是商第二个是余 —— 此为该函数的输出。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(3, 2)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(0, 3)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"(5, 0)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"\n",
"divmod(11, 3)\n",
"a, b = divmod(11, 3)\n",
"a\n",
"b\n",
"\n",
"divmod(3, 11)\n",
"a, b = divmod(3, 11)\n",
"a\n",
"b\n",
"\n",
"tup = divmod(15 , 3)\n",
"tup\n",
"tup[1]"
]
},
{
"cell_type": "markdown",
"metadata": {
"toc-hr-collapsed": false
},
"source": [
"## 可选位置参数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"有些函数,如 `pow()`,有**可选的位置参数**Optional Positional Arguments。\n",
"\n",
"于是,`pow()` 有两种用法,各有不同的结果:\n",
"\n",
"> * `pow(x, y)` —— 返回值是 `x ** y`\n",
"> * `pow(x, y, z)` —— 返回值是 `x ** y % z`\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"8"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"\n",
"pow(2, 3)\n",
"pow(2, 3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Class 也是函数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"虽然你现在还不一定知道 Class 究竟是什么,但在阅读官方文档的时候,遇到一些内建函数前面写着 Class比如 `Class bool([x])`,千万别奇怪,因为 Class 本质上来看就是一种特殊类型的函数,也就是说,它也是函数:"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"\n",
"bool()\n",
"bool(3.1415926)\n",
"bool(-3.1415926)\n",
"bool(1 == 2)\n",
"bool(None)"
]
}
],
"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.2"
},
"toc-autonumbering": true
},
"nbformat": 4,
"nbformat_minor": 2
}

565
化名与匿名.ipynb Normal file
View File

@ -0,0 +1,565 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 化名与匿名"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 化名"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在 Python 中,我们可以给一个函数取个**化名**alias。\n",
"\n",
"以下的代码,我们先是定义了一个名为 `_is_leap` 的函数,而后为它另取了一个化名:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<function __main__._is_leap(year)>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"4379191184"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"4379191184"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"function"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"function"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"\n",
"def _is_leap(year):\n",
" return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n",
"\n",
"year_leap_bool = _is_leap\n",
"year_leap_bool #<function __main__._is_leap(year)>\n",
"year_leap_bool(800) # _is_leap(800) -> True\n",
"\n",
"id(year_leap_bool) # id() 这个函数可以查询某对象的内存地址\n",
"id(_is_leap) # year_leap_bool 和 _is_leap 其实保存在同一个地址中,也就是说,它们是同一个对象。\n",
"\n",
"type(year_leap_bool)\n",
"type(_is_leap) # 它们都是 function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"我们可以看到的是,`id(year_leap_bool)` 和 `id(_is_leap)` 的内存地址是一样的 —— 它们是同一个对象,它们都是函数。所以,当你写 `year_leap_bool = _is_leap` 的时候,相当于给 `_is_leap()` 这个函数取了个化名。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## lambda"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"写一个很短的函数可以用 `lambda` 关键字。\n",
"\n",
"下面是用 `def` 关键字写函数:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"8"
]
},
"execution_count": 14,
"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": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"8"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add = lambda x, y: x + y\n",
"add(3, 5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"lambda 的语法结构如下:\n",
"\n",
"> `lambda_expr ::= \"lambda\" [parameter_list] \":\" expression`\n",
"\n",
"```python\n",
"lambda x, y: x + y\n",
"```\n",
"\n",
"先写上 `lambda` 这个关键字,其后分为两个部分,`:` 之前是参数,之后是表达式;这个表达式的值,就是这个函数的返回值。\n",
"\n",
"> **注意**`lambda` 语句中,`:` 之后有且只能有一个表达式。\n",
"\n",
"而这个函数呢,没有名字,所以被称为 “匿名函数”。\n",
"\n",
"`add = lambda x, y: x + y`\n",
"\n",
"就相当于是给一个没有名字的函数取了个名字。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## lambda 的使用场景"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"那 lambda 这种匿名函数的用处在哪里呢?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 作为某函数的返回值"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"第一个常见的用处是*作为另外一个函数的返回值*。\n",
"\n",
"让我们看看 [The Python Tutorial](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions) 中的一个例子。"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"42"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"43"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def make_incrementor(n):\n",
" return lambda x: x + n\n",
"\n",
"f = make_incrementor(42)\n",
"f(0)\n",
"\n",
"f(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"这个例子乍看起来很令人迷惑。我们先看看 `f = make_incrementor(42)` 之后,`f` 究竟是什么东西:"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<function __main__.make_incrementor.<locals>.<lambda>(x)>"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"4428443296"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"4428726888"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def make_incrementor(n):\n",
" return lambda x: x + n\n",
"\n",
"f = make_incrementor(42)\n",
"f\n",
"\n",
"id(make_incrementor)\n",
"id(f)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"首先,要注意,`f` 并不是 `make_incrementor()` 这个函数的化名,如果是给这个函数取个化名,写法应该是:\n",
"\n",
"```python\n",
"f = make_incrementor\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"那 `f` 是什么呢?它是 `<function __main__.make_incrementor.<locals>.<lambda>(x)>`\n",
"\n",
"> * `f = make_incrementor(42)` 是将 `make_incrementor(42)` 的返回值保存到 `f` 这个变量之中;\n",
"> * 而 `make_incrementor()` 这个函数接收到 `42` 这个参数之后,返回了一个函数:`lambda x: x + 42`\n",
"> * 于是,`f` 中保存的函数是 `lambda x: x + 42`\n",
"> * 所以,`f(0)` 是向这个匿名函数传递了 `0`,而后,它返回的是 `0 + 42`。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 作为某函数的参数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"可以拿一些可以接收函数为参数的内建函数做例子。比如,[`map()`](https://docs.python.org/3/library/functions.html#map)。\n",
"\n",
"> `map`(*function*, *iterable*, *...*)\n",
"> \n",
"> Return an iterator that applies *function* to every item of *iterable*, yielding the results. If additional *iterable* arguments are passed, *function* must take that many arguments and is applied to the items from all iterables in parallel. With multiple iterables, the iterator stops when the shortest iterable is exhausted. For cases where the function inputs are already arranged into argument tuples, see [`itertools.starmap()`](https://docs.python.org/3/library/itertools.html#itertools.starmap)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`map()` 这个函数的第一个参数,就是用来接收函数的。随后的参数,是 `iterable` —— 就是可被迭代的对象,比如,各种容器,例如:列表、元组、字典什么的。"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[2, 4, 6, 8, 10, 12]"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"[2, 4, 6, 8, 10, 12]"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def double_it(n):\n",
" return n * 2\n",
"\n",
"a_list = [1, 2, 3, 4, 5, 6]\n",
"\n",
"b_list = list(map(double_it, a_list))\n",
"b_list\n",
"\n",
"c_list = list(map(lambda x: x * 2, a_list))\n",
"c_list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"显然用 `lambda` 更为简洁。另外,类似完成 `double_it(n)` 这种简单功能的函数,常常有 “用过即弃” 的必要。"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'name': 'john', 'phone': 9876},\n",
" {'name': 'mike', 'phone': 5603},\n",
" {'name': 'stan', 'phone': 6898},\n",
" {'name': 'eric', 'phone': 7898}]"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"['john', 'mike', 'stan', 'eric']"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
"[9876, 5603, 6898, 7898]"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"phonebook = [\n",
" {\n",
" 'name': 'john',\n",
" 'phone': 9876\n",
" },\n",
" {\n",
" 'name': 'mike',\n",
" 'phone': 5603\n",
" },\n",
" {\n",
" 'name': 'stan',\n",
" 'phone': 6898\n",
" },\n",
" {\n",
" 'name': 'eric',\n",
" 'phone': 7898\n",
" }\n",
"]\n",
"\n",
"phonebook\n",
"list(map(lambda x: x['name'], phonebook))\n",
"list(map(lambda x: x['phone'], phonebook))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"可以给 map() 传递若干个可被迭代对象:"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[2, 12, 30]"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a_list = [1, 3, 5]\n",
"b_list = [2, 4, 6]\n",
"\n",
"list(map(lambda x, y: x * y, a_list, b_list))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"以上的例子都弄明白了,再去看 [The Python Tutorial](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions) 中的例子,就不会有任何疑惑了:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]\n",
"pairs.sort(key=lambda p: p[1])\n",
"pairs"
]
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

1139
参数.ipynb Normal file

File diff suppressed because it is too large Load Diff

652
流程控制.ipynb Normal file
View File

@ -0,0 +1,652 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 流程控制"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"无论多么复杂的流程,归根到底就两点:`分支`与`循环`\n",
"```python\n",
"for n in range(2, 100):\n",
" if n == 2:\n",
" print(n)\n",
" continue\n",
" for i in range(2, n):\n",
" if (n % i) == 0:\n",
" break\n",
" else:\n",
" print(n) \n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## if 语句"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`if` 语句的最简单构成是这样 —— 注意第 1 行末尾的冒号 `:` 和第 2 行的缩进:\n",
"\n",
"```python\n",
"if expression:\n",
" statements\n",
"```\n",
"\n",
"如果表达式 `expression` 返回值为真,执行 `if` 语句块内部的 `statements`,否则,什么都不做,执行 `if` 之后的下一个语句。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"r = random.randrange(1, 1000)\n",
"\n",
"if r % 2 == 0:\n",
" print(f'{r} is even.')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"如果,表达式 `expression` 返回值无论真假,我们都需要做一点相应的事情,那么我们这么写:\n",
"\n",
"```python\n",
"if expression:\n",
" statements_for_True\n",
"else:\n",
" statements_for_False\n",
"```\n",
"\n",
"如果表达式 `expression` 返回值为真,执行 `if` 语句块内部的 `statements_for_True`,否则,就执行 `else` 语句块内部的 `statements_for_False`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"945 is odd.\n"
]
}
],
"source": [
"import random\n",
"r = random.randrange(1, 1000)\n",
"\n",
"if r % 2 == 0:\n",
" print(f'{r} is even.')\n",
"else:\n",
" print(f'{r} is odd.')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**但是,可以简化!**"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"even\n"
]
}
],
"source": [
"import random\n",
"r = random.randrange(1, 1000)\n",
"\n",
"def EvenOdd(n):\n",
" if n % 2 == 0:\n",
" return(\"even\")\n",
" return(\"odd\")\n",
"\n",
"print(EvenOdd(r))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"有时,表达式 `<expression>` 返回的值有多种情况,并且针对不同的情况我们都要做相应的事情,那么可以这么写:\n",
"\n",
"```python\n",
"if expression_1:\n",
" statements_for_expression_1_True\n",
" \n",
"elif expression_2:\n",
" statements_for_expression_2_True\n",
"\n",
"elif expression_3:\n",
" statements_for_expression_3_True\n",
"\n",
"elif expression_...:\n",
" statements_for_expression_..._True\n",
"```\n",
"\n",
"`elif` 是 `else if` 的缩写,作用相同。\n",
"\n",
"以下程序模拟投两个骰子的结果 —— 两个骰子数字加起来,等于 `7` 算平,大于 `7` 算大,小于 `7` 算小:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Big!\n"
]
}
],
"source": [
"import random\n",
"r = random.randrange(2, 13)\n",
"\n",
"if r == 7:\n",
" print('Draw!')\n",
"elif r < 7:\n",
" print('Small!')\n",
"elif r > 7:\n",
" print('Big!')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"当然你还可以模拟投飞了的情况,即,最终的骰子数是 `0` 或者 `1`,即,`< 2`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Small!\n"
]
}
],
"source": [
"import random\n",
"r = random.randrange(0, 13) # 生成的随机数应该从 0 开始了;\n",
"\n",
"if r == 7:\n",
" print('Draw!')\n",
"elif r >= 2 and r < 7: # 如果这里直接写 elif r < 7:那么else: 那一部分永远不会被执行……\n",
" print('Small!')\n",
"elif r > 7:\n",
" print('Big!')\n",
"else:\n",
" print('Not valid!')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## for 循环"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"value of a: 0\n",
"value of a: 1\n",
"value of a: 2\n",
"value of a: 3\n",
"value of a: 4\n",
"value of a: 5\n",
"value of a: 6\n",
"value of a: 7\n",
"value of a: 8\n",
"value of a: 9\n"
]
}
],
"source": [
"for a in range(10):\n",
" print(f'value of a: {a}') #每次 a 的值都不同,从 0 递增至 9"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### range() 函数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`range()` 是个内建函数,[它的文档](https://docs.python.org/3/library/functions.html#func-range)是这样写的:\n",
"\n",
"> **range**(_stop_) \n",
">\n",
"> **range**(_start, stop[, step]_)\n",
"\n",
"只有一个参数的时候,这个参数被理解为 `stop`,生成一个从 `0` 开始,到 `stop - 1` 的整数数列。\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = \"all\"\n",
"\n",
"range(10)\n",
"list(range(10)) # 将 range(10) 转换成 list以便清楚看到其内容。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`start` 参数的默认值是 `0`。如需指定起点,那么得给 `range()` 传递两个参数,比如,`range(2, 13)`……"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(range(2, 13))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"第三个参数可选;`step`,步长,就相当于是 “等差数列” 当中的 “差”,默认值是 `1`。例如,`range(1, 10, 2)` 生成的是这样一个数列 `[1, 3, 5, 7, 9]`。所以,打印 `0 10` 之间的所有奇数,可以这样写:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"3\n",
"5\n",
"7\n",
"9\n"
]
}
],
"source": [
"for i in range(1, 10, 2):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"我们也可以生成负数的数列:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(range(0, -10, -1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Continue、Break 和 Pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在循环的过程中,还可以用 `continue` 和 `break` 控制流程走向,通常是在某条件判断发生的情况下 —— 正如你早就见过的那样:\n",
"\n",
"```python\n",
"for n in range(2, 100):\n",
" if n == 2:\n",
" print(n)\n",
" continue\n",
" for i in range(2, n):\n",
" if (n % i) == 0:\n",
" break\n",
" else:\n",
" print(n) \n",
"```\n",
"\n",
"`continue` 语句将忽略其后的语句开始下次循环,而 `break` 语句将从此结束当前循环.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`pass` 语句什么都不干:\n",
"\n",
"再比如,\n",
"```python\n",
"def someFunction():\n",
" pass\n",
"```\n",
"\n",
"又或者:\n",
"\n",
"```python\n",
"for i in range(100):\n",
" pass\n",
" if i % 2 == 0:\n",
" pass\n",
"```\n",
"\n",
"`pass` 这个语句更多是给写程序的人用的。当你写程序的时候,你可以用 `pass` 占位,而后先写别的部分,过后再回来补充本来应该写在 `pass` 所在位置的那一段代码。\n",
"\n",
"写嵌套的判断语句或循环语句的时候,最常用 `pass`,因为写嵌套挺费脑子的,一不小心就弄乱了。所以,经常需要先用 `pass` 占位,而后逐一突破。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## while 循环"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"今天,在绝大多数编程语言中,都提供两种循环结构:\n",
"\n",
"> * Collection-controlled loops以集合为基础的循环\n",
"> * Condition-controlled loops以条件为基础的循环\n",
"\n",
"之前的 `for ... in ...` 就是 Collection-controlled loops而在 Python 中提供的 Condition-controlled loops 是 `while` 循环。\n",
"\n",
"`while` 循环的格式如下:\n",
"\n",
"```python\n",
"while expression:\n",
" statements\n",
"```\n",
"\n",
"输出 1000 以内的斐波那契数列的程序如下:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 \n"
]
}
],
"source": [
"n = 1000\n",
"a, b = 0, 1\n",
"while a < n:\n",
" print(a, end=' ')\n",
" a, b = b, a+b\n",
"print()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`for` 和 `while` 的区别在哪里?什么时候应该用哪个?\n",
"\n",
"`for` 更适合处理序列类型的数据Sequence Type的迭代比如处理字符串中的每一个字符比如把 `range()` 返回的数列当作某种序列类型的索引。\n",
"\n",
"`while` 更为灵活,因为它后面只需要接上一个逻辑表达式即可。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 一个投骰子赌大小的游戏"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"以下是一个让用户和程序玩掷骰子赌大小的程序。规则如下:\n",
"\n",
">* 每次计算机随机生成一个 `2... 12` 之间的整数,用来模拟机器人投两个骰子的情况;\n",
"* 机器人和用户的起始资金都是 10 个硬币\n",
"* 要求用户猜大小:\n",
" * 用户输入 `b` 代表 “大”;\n",
" * 用户输入 `s` 代表 “小”;\n",
" * 用户输入 `q` 代表 “退出”;\n",
"* 用户的输入和随机产生的数字比较有以下几种情况:\n",
" * 随机数小于 `7`,用户猜小,用户赢;\n",
" * 随机数小于 `7`,用户猜大,用户输;\n",
" * 随机数等于 `7`,用户无论猜大还是猜小,结局平,不输不赢;\n",
" * 随机数大于 `7`,用户猜小,用户输;\n",
" * 随机数大于 `7`,用户猜大,用户赢;\n",
"* 游戏结束条件:\n",
" * 机器人和用户,若任意一方硬币数量为 `0`,则游戏结束;\n",
" * 用户输入了 `q` 主动终止游戏。\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"You: 10\t Bot: 10\n",
"The dice is 12;\n",
"You WIN!\n",
"\n",
"You: 11\t Bot: 9\n",
"The dice is 9;\n",
"You WIN!\n",
"\n",
"You: 12\t Bot: 8\n",
"The dice is 9;\n",
"You WIN!\n",
"\n",
"You: 13\t Bot: 7\n",
"The dice is 8;\n",
"You WIN!\n",
"\n",
"You: 14\t Bot: 6\n",
"The dice is 8;\n",
"You WIN!\n",
"\n",
"You: 15\t Bot: 5\n",
"The dice is 8;\n",
"You WIN!\n",
"\n",
"You: 16\t Bot: 4\n"
]
}
],
"source": [
"from random import randrange\n",
"\n",
"coin_user, coin_bot = 10, 10 # 可以用一个赋值符号分别为多个变量赋值\n",
"rounds_of_game = 0\n",
"\n",
"def bet(dice, wager): # 接收两个参数,一个是骰子点数,另一个用户的输入\n",
" if dice == 7:\n",
" print(f'The dice is {dice};\\nDRAW!\\n') # \\n 是换行符号\n",
" return 0\n",
" elif dice < 7:\n",
" if wager == 's':\n",
" print(f'The dice is {dice};\\nYou WIN!\\n')\n",
" return 1\n",
" else:\n",
" print(f'The dice is {dice};\\nYou LOST!\\n')\n",
" return -1\n",
" elif dice > 7:\n",
" if wager == 's':\n",
" print(f'The dice is {dice};\\nYou LOST!\\n')\n",
" return -1\n",
" else:\n",
" print(f'The dice is {dice};\\nYou WIN!\\n')\n",
" return 1\n",
"\n",
"while True: # 除 for 之外的另外一个循环语句\n",
" print(f'You: {coin_user}\\t Bot: {coin_bot}')\n",
" dice = randrange(2, 13) # 生成一个 2 到 12 的随机数\n",
" wager = input(\"What's your bet? \")\n",
" if wager == 'q':\n",
" break \n",
" elif wager in 'bs': # 只有当用户输入的是 b 或者 s 得时候,才 “掷骰子”……\n",
" result = bet(dice, wager)\n",
" coin_user += result # coin_user += result 相当于 coin_user = coin_user + result\n",
" coin_bot -= result\n",
" rounds_of_game += 1\n",
" if coin_user == 0:\n",
" print(\"Woops, you've LOST ALL, and game over!\")\n",
" break\n",
" elif coin_bot == 0:\n",
" print(\"Woops, the robot's LOST ALL, and game over!\")\n",
" break\n",
" \n",
"print(f\"You've played {rounds_of_game} rounds.\\n\")\n",
"print(f\"You have {coin_user} coins now.\\nBye!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 总结"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"有控制流,才能算得上是程序。\n",
"\n",
"> * 只处理一种情况,用 `if ... `\n",
"> * 处理 `True`/`False` 两种情况,用 `if ... else ...`\n",
"> * 处理多种情况,用 `if ... elif ... elif ... else ...`\n",
"> * 迭代有序数据类型,用 `for ... in ...`,如果需要处理没有 `break` 发生的情况,用 `for ... else ...`\n",
"> * 其它循环,用 `while ...`\n",
"> * 与循环相关的语句还有 `continue`、`break`、`pass`\n",
"> * 函数从控制流角度去看其实就是子程序"
]
}
],
"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.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

473
递归.ipynb Normal file
View File

@ -0,0 +1,473 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 递归函数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 递归Recursion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在函数中有个理解门槛比较高的概念:**递归函数**Recursive Functions—— 那些**在自身内部调用自身的函数**。说起来都比较拗口。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在所有的项目中,都是非常常用的,这样子写,虽然运算复杂了、逻辑性要求更高,但是那是计算机考虑的事情,对人而言,写的代码少了,预计的结果出来了!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"先看一个例子,我们想要有个能够计算 `n` 的*阶乘*factorial`n!` 的函数,`f()`,规则如下:\n",
"\n",
"> - `n! = n × (n-1) × (n-2)... × 1`\n",
"> - 即,`n! = n × (n-1)!`\n",
"> - 且,`n >= 1`\n",
">\n",
"> **注意**:以上是数学表达,不是程序,所以,`=` 在这一小段中是 “*等于*” 的意思,**不是程序语言中的赋值符号**。\n",
"\n",
"于是,计算 `f(n)` 的 Python 程序如下:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"120\n"
]
}
],
"source": [
"def f(n):\n",
" if n == 1:\n",
" return 1\n",
" else:\n",
" return n * f(n-1)\n",
" \n",
"print(f(5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 递归函数的执行过程"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"以 factorial(5) 为例,让我们看看程序的流程:\n",
"\n",
"![](images/recursive-function-call.png)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"当 f(5) 被调用之后,函数开始运行……\n",
"* 因为 `5 > 1`,所以,在计算 `n * f(n-1)` 的时候要再次调用自己 `f(4)`;所以必须等待 `f(4)` 的值返回;\n",
"* 因为 `4 > 1`,所以,在计算 `n * f(n-1)` 的时候要再次调用自己 `f(3)`;所以必须等待 `f(3)` 的值返回;\n",
"* 因为 `3 > 1`,所以,在计算 `n * f(n-1)` 的时候要再次调用自己 `f(2)`;所以必须等待 `f(2)` 的值返回;\n",
"* 因为 `2 > 1`,所以,在计算 `n * f(n-1)` 的时候要再次调用自己 `f(1)`;所以必须等待 `f(1)` 的值返回;\n",
"* 因为 `1 == 1`,所以,这时候不会再次调用 `f()` 了,于是递归结束,开始返回,这次返回的是 `1`\n",
"* 下一步返回的是 `2 * 1`\n",
"* 下一步返回的是 `3 * 2`\n",
"* 下一步返回的是 `4 * 6`\n",
"* 下一步返回的是 `5 * 24` —— 至此,外部调用 `f(5)` 的最终返回值是 `120`……\n",
"\n",
"加上一些输出语句之后,能更清楚地看到大概的执行流程:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Call f(5)...\n",
"\tn = 5\n",
"\tn = 4\n",
"\tn = 3\n",
"\tn = 2\n",
"\tn = 1\n",
"Returning...\n",
"\tn = 1 return: 1\n",
"\tn = 2 return: 2\n",
"\tn = 3 return: 6\n",
"\tn = 4 return: 24\n",
"\tn = 5 return: 120\n",
"Get out of f(n), and f(5) = 120\n"
]
}
],
"source": [
"def f(n):\n",
" print('\\tn =', n)\n",
" if n == 1:\n",
" print('Returning...')\n",
" print('\\tn =', n, 'return:', 1)\n",
" return 1\n",
" else:\n",
" r = n * f(n-1)\n",
" print('\\tn =', n, 'return:', r)\n",
" return r\n",
" \n",
"print('Call f(5)...')\n",
"print('Get out of f(n), and f(5) =', f(5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 递归的终点"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"递归函数在内部必须有一个能够让自己停止调用自己的方式,否则永远循环下去了……\n",
"\n",
"其实,我们所有人很小就见过递归应用,只不过,那时候不知道那就是递归而已。听过那个无聊的故事罢?\n",
"\n",
"> 山上有座庙,庙里有个和尚,和尚讲故事,说……\n",
"> > 山上有座庙,庙里有个和尚,和尚讲故事,说……\n",
"> > > 山上有座庙,庙里有个和尚,和尚讲故事,说……\n",
"\n",
"写成 Python 程序大概是这样:\n",
"\n",
"```python\n",
"def a_monk_telling_story():\n",
" print('山上有座庙,庙里有个和尚,和尚讲故事,他说…… ')\n",
" return a_monk_telling_story()\n",
"\n",
"a_monk_telling_story()\n",
"```\n",
"\n",
"这是个*无限循环*的递归,因为这个函数里*没有设置中止自我调用的条件*。无限循环还有个不好听的名字,叫做 “死循环”。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在著名的电影**盗梦空间**_2010_从整体结构上来看“入梦” 也是个 “递归函数”。只不过,这个函数和 `a_monk_telling_story()` 不一样,它并不是死循环 —— 因为它设定了*中止自我调用的条件*\n",
"\n",
"> 在电影里,醒过来的条件有两个\n",
">> * 一个是在梦里死掉;\n",
">> * 一个是在梦里被 kicked 到……\n",
">\n",
"> 如果这两个条件一直不被满足,那就进入 limbo 状态 —— 其实就跟死循环一样,出不来了……\n",
"\n",
"为了演示,我把故事情节改变成这样:\n",
"> * 入梦,`in_dream()`,是个递归函数;\n",
"> * 入梦之后醒过来的条件有两个:\n",
">> * 一个是在梦里死掉,`dead is True`\n",
">> * 一个是在梦里被 kicked`kicked is True`……\n",
">>\n",
">> 以上两个条件中任意一个被满足,就苏醒……\n",
"\n",
"至于为什么会死掉,如何被 kick我偷懒了一下管它怎样管它如何反正每个条件被满足的概率是 1/10……也只有这样我才能写出一个简短的能够运行的 “*盗梦空间程序*”。)\n",
"\n",
"把这个很抽象的故事写成 Python 程序,看看一次入梦之后能睡多少天,大概是这样:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"dead: False kicked: False\n",
"dead: False kicked: False\n",
"dead: False kicked: False\n",
"dead: True kicked: False\n",
"I slept 4 days, and was dead to wake up...\n",
"The in_dream() function returns: 4\n"
]
}
],
"source": [
"import random\n",
"\n",
"def in_dream(day=0, dead=False, kicked=False):\n",
" dead = not random.randrange(0,10) # 1/10 probability to be dead\n",
" kicked = not random.randrange(0,10) # 1/10 probability to be kicked\n",
" day += 1\n",
" print('dead:', dead, 'kicked:', kicked)\n",
" \n",
" if dead:\n",
" print((f\"I slept {day} days, and was dead to wake up...\"))\n",
" return day\n",
" elif kicked:\n",
" print(f\"I slept {day} days, and was kicked to wake up...\")\n",
" return day\n",
" \n",
" return in_dream(day)\n",
" \n",
"print('The in_dream() function returns:', in_dream())"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"ename": "RecursionError",
"evalue": "maximum recursion depth exceeded",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[10], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mx\u001b[39m(n):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m x(n\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[0;32m----> 3\u001b[0m \u001b[43mx\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m5\u001b[39;49m\u001b[43m)\u001b[49m\n",
"Cell \u001b[0;32mIn[10], line 2\u001b[0m, in \u001b[0;36mx\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mx\u001b[39m(n):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m \u001b[43mx\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
"Cell \u001b[0;32mIn[10], line 2\u001b[0m, in \u001b[0;36mx\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mx\u001b[39m(n):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m \u001b[43mx\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
" \u001b[0;31m[... skipping similar frames: x at line 2 (2969 times)]\u001b[0m\n",
"Cell \u001b[0;32mIn[10], line 2\u001b[0m, in \u001b[0;36mx\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mx\u001b[39m(n):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m \u001b[43mx\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded"
]
}
],
"source": [
"def x(n):\n",
" return n * x(n-1)\n",
"x(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"不用深究上面盗梦空间这个程序的其它细节,不过,通过以上三个递归程序 —— 两个很扯淡的例子,一个正经例子 —— 你已经看到了递归函数的共同特征:\n",
"\n",
"> 1. 在 `return` 语句中返回的是*自身的调用*(或者是*含有自身的表达式*\n",
"> 2. 为了避免死循环,*一定要有至少一个条件*下返回的不再是自身调用……"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 变量的作用域"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"再回来看计算阶乘的程序 —— 这是正经程序。这次我们把程序名写完整,`factorial()`:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"120\n"
]
}
],
"source": [
"def factorial(n):\n",
" if n == 1:\n",
" return 1\n",
" else:\n",
" return n * factorial(n-1)\n",
" \n",
"print(factorial(5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"最初的时候,这个函数的执行流程之所以令人迷惑,是因为初学者对*变量*的**作用域**把握得不够充分。\n",
"\n",
"变量根据作用域可以分为两种全局变量Global Variables和局部变量Local Variables。\n",
"\n",
"可以这样简化理解:\n",
"\n",
"> * 在函数内部被赋值而后使用的,都是*局部变量*,它们的作用域是*局部*,无法被函数外的代码调用;\n",
"> * 在所有函数之外被赋值而后开始使用的,是*全局变量*,它们的作用域是*全局*,在函数内外都可以被调用。\n",
"\n",
"定义如此,但通常程序员们会严格地遵守一条原则:\n",
"\n",
"> 在函数内部绝对不调用全局变量。即便是必须改变全局变量,也只能通过函数的返回值在函数外改变全局变量。\n",
"\n",
"你也必须遵守同样的原则。而这个原则同样可以在日常的工作生活中 “调用”:\n",
"\n",
"> 做事的原则:自己的事自己做,别人的事,最多通过自己的产出让他们自己去搞……"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"再仔细观察一下以下代码。当一个变量被当做参数传递给一个函数的时候,这个变量本身并不会被函数所改变。比如,`a = 5`,而后,再把 `a` 当作参数传递给 `f(a)` 的时候,这个函数当然应该返回它内部任务完成之后应该传递回来的值,但 `a` 本身不会被改变。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5 120\n",
"120 120\n"
]
}
],
"source": [
"def factorial(n):\n",
" if n == 1:\n",
" return 1\n",
" else:\n",
" return n * factorial(n-1)\n",
" \n",
"a = 5\n",
"b = factorial(a) # a 并不会因此改变;\n",
"print(a, b)\n",
"a = factorial(a) # 这是你主动为 a 再一次赋值……\n",
"print(a, b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"理解了这一点之后,再看 `factorial()` 这个递归函数的递归执行过程,你就能明白这个事实:\n",
"\n",
"> 在每一次 factorial(n) 被调用的时候,它都会形成一个作用域,`n` 这个变量作为参数把它的值传递给了函数,*但是*`n` 这个变量本身并不会被改变。\n",
"\n",
"我们再修改一下上面的代码:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5 120\n"
]
}
],
"source": [
"def factorial(n):\n",
" if n == 1:\n",
" return 1\n",
" else:\n",
" return n * factorial(n-1)\n",
" \n",
"n = 5 # 这一次,这个变量名称是 n\n",
"m = factorial(n) # n 并不会因此改变;\n",
"print(n, m)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"在 `m = factorial(n)` 这一句中,`n` 被 `factorial()` 当做参数调用了,但无论函数内部如何操作,并不会改变变量 `n` 的值。\n",
"\n",
"关键的地方在这里:在函数内部出现的变量 `n`,和函数外部的变量 `n` 不是一回事 —— **它们只是名称恰好相同而已**,函数参数定义的时候,用别的名称也没什么区别:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5 120\n"
]
}
],
"source": [
"def factorial(x): # 在这个语句块中出现的变量,都是局部变量\n",
" if x == 1:\n",
" return 1\n",
" else:\n",
" return x * factorial(x-1)\n",
" \n",
"n = 5 # 这一次,这个变量名称是 n\n",
"m = factorial(n) # n 并不会因此改变;\n",
"print(n, m)\n",
"# 这个例子和之前再之前的示例代码有什么区别吗?\n",
"# 本质上没区别,就是变量名称换了而已……"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"函数开始执行的时候,`x` 的值,是由外部代码(即,函数被调用的那一句)传递进来的。即便函数内部的变量名称与外部的变量名称相同,它们也不是同一个变量。"
]
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}