{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## 定义函数" ] }, { "cell_type": "markdown", "metadata": { "jp-MarkdownHeadingCollapsed": true, "tags": [] }, "source": [ "**什么是函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**定义**:为实现一个操作或特定功能而集合在一起的语句集。\n", "\n", "**好处**:避免代码复制带来的错误或漏洞,不仅可以实现代码的复用,还可以保证代码的一致性。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**如何定义函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "定义函数:函数头,函数体\n", "\n", "- 函数头为`def max(a, b)`\n", "\n", "- 函数体为:\n", "\n", "```python\n", "if a > b:\n", " return a\n", "else:\n", " retur\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数头:函数名,参数列表\n", "\n", "- 函数名为`max`\n", "\n", "- 参数列表为`(a, b)`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数体:缩进语句块,返回值(return)\n", "\n", "- 缩进语句块,缩进正确才能对齐\n", "\n", "- 返回值为`a`或`b`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "注意:\n", "\n", "- 即使函数没有参数,也要保留括号;\n", "\n", "- 没有`return`语句,则默认返回空值`None`;\n", "\n", "- 注意**冒号**,**缩进**,**对齐**三原则" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**调用函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对于一个函数,可以通过`函数名(参数列表)`这样的语法来调用函数;\n", "\n", "如果函数有返回值,则可以在函数调用的同时将返回值传递出来,此时这个函数调用可以当作一个值来处理;\n", "\n", "函数也可以通过一条语句调用而不接收任何值,这种情况一般应用于无返回值的函数,实际上,如果函数有返回值,也可以当作语句被调用,此时函数返回值会被忽略;\n", "\n", "`main`函数:一般作为程序的入口函数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "拓展学习:\n", "\n", "- 使用模块\n", "\n", "- python全局变量引用与修改" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# 定义一个求矩形面积的函数\n", "\n", "def compute_area(width, height):\n", " area = width * height\n", " return area" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# 定义main函数\n", "\n", "def main():\n", " width = 3\n", " height = 9\n", " # 调用compute_area函数\n", " # compute_area函数有返回值\n", " # 调用该函数时,会将其返回值传递出来\n", " # 此处我们把其返回值赋值给变量area\n", " area = compute_area(3, 9)\n", " print(\"The area of the square is {}\".format(area))" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The area of the square is 27\n" ] } ], "source": [ "# 调用main函数\n", "\n", "# 函数也可以通过一条语句调用而不接收任何值\n", "# 通常函数没有返回值的时候\n", "# 我们会像下面一样调用函数\n", "# 即使有返回值(就是函数里面有return语句)\n", "# 如果你接下来不用它的返回值\n", "# 你也可以这样调用哦\n", "main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 变量的作用域" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**局部变量**:在函数内部定义的变量称为局部变量,局部变量只能在函数内部被访问,其作用域从**创建变量的地方开始**,到包含该局部变量的**函数结束为止**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数`compute_area`和函数`main`中的变量`area`就是局部变量,除了其作用域,该变量便不可被调用。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# 在局部变量的作用域外调用,会出错哦\n", "# print(area)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**全局变量**:在所有函数之外创建的变量被称为全局变量,可以被所有函数访问,全局变量可以在程序的任意位置访问,而如果试图在作用域外访问局部变量就会造成错误。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# 此处定义的a就是一个全局变量\n", "a = 0" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# 定义一个函数cool\n", "def cool():\n", " global a # “呼叫”全局变量,其实正规翻译是“声明全局变量”\n", " a += 1\n", " return a" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n", "10\n" ] } ], "source": [ "for i in range(10):\n", " print(cool())" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# 定义一个函数not_cool\n", "def not_cool():\n", " # 此处要修改全局变量a,但是没有“呼叫”,调用该函数会出错哦\n", " # 因为此时会把a作为一个局部变量处理\n", " # 那么a作为一个局部变量,被创建了吗,没有吧\n", " # 所以会报错local variable 'a' referenced before assignment\n", " a += 1\n", " return a" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# 函数内修改全局变量,但是没有“呼叫”,所以会出错哦\n", "# for i in range(10):\n", "# print(not_cool())" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# 定义一个函数another_cool\n", "def another_cool():\n", " # 此处没“呼叫”全局变量,那么默认会把a作为一个局部变量对待\n", " # 所以此处创建了一个局部变量a\n", " a = 100\n", " a += 1\n", " return a" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "101\n", "101\n", "101\n", "101\n", "101\n", "101\n", "101\n", "101\n", "101\n", "101\n" ] } ], "source": [ "# 局部变量的作用域是 创建处-至-函数结束处\n", "# 所以调用10次,每次都是打印101\n", "for i in range(10):\n", " print(another_cool())" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# 调用一下全局变量看看\n", "# 上面我们呼叫过一次全局变量a\n", "# 然后对它进行了修改\n", "# 你觉得此处的a的值是什么\n", "\n", "# a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通过上面的小栗子,可以看出局部变量和全局变量是可以重名的,但是全局变量和局部变量尽量,不要,不要,不要,重名" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:如何在函数内部定义一个全局变量" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**答案**:函数内部只能定义局部变量,不能定义全局变量;但可以声明全局变量(也就是用 global),`global`的语法是`global var_name`,不能在声明的时候赋值。" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# 定义一个全局变量global_list\n", "global_list = []" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# 定义一个函数very_cool\n", "def very_cool(item):\n", " global_list.append(item) # del global_list 就是报错了哦\n", " return global_list" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0]\n", "[0, 1]\n", "[0, 1, 2]\n", "[0, 1, 2, 3]\n", "[0, 1, 2, 3, 4]\n", "[0, 1, 2, 3, 4, 5]\n", "[0, 1, 2, 3, 4, 5, 6]\n", "[0, 1, 2, 3, 4, 5, 6, 7]\n", "[0, 1, 2, 3, 4, 5, 6, 7, 8]\n", "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] } ], "source": [ "for i in range(10):\n", " print(very_cool(i))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "注意:引用全局变量,不需要`golbal`声明,修改全局变量,需要使用`global`声明,特别地,**列表、字典**等如果只是**修改其中元素的值**,可以直接使用全局变量,不需要`global`声明。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "再强调一遍,平时在jupyter-notebook里写的代码,函数外面的一般都是全局变量,所以,为了避免不必要的错误,请你:\n", "\n", "- **局部变量和全局变量尽可能不要重名**\n", "\n", "- **不要在函数里修改全局变量**\n", "\n", "- **尽量不要在函数里访问全局变量**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 函数的参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**形式参数(形参)**:在`def`语句中,位于函数名后面的变量通常称为形参。\n", "\n", "**实际参数(实参)**:调用函数时提供的**值**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "def change(my_obj):\n", " my_obj *= 2\n", " return my_obj\n", "\n", "\n", "result = change(2)\n", "\n", "a = 10\n", "\n", "result = change(a)\n", "\n", "result = change([1, 2])\n", "\n", "b = [1, 2]\n", "\n", "result = change(b)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面代码中`my_obj`是形参,`2`和`[1, 2]`是实参。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 基本的参数传递机制" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 值传递:在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。\n", "\n", "- 引用传递:在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Python的参数传递机制:传对象引用" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "需要对Python的对象按照内容是否可变划分为可变对象和不可变对象。所谓可变对象指的是对象的内容是可变的,而不可变对象指的是对象内容不可变。\n", "\n", "- **传不可变对象**,如数字(int、float),布尔值(boolean),元组(tuple),字符串(string)\n", "\n", "- **传可变对象**,如列表(list),字典(dict),集合(set)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def increament_int(x):\n", " x += 1" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "def increament_list(x):\n", " x += [1]" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "[2, 3, 4, 1]\n" ] } ], "source": [ "a = 3\n", "b = [2, 3, 4]\n", "increament_int(a)\n", "print(a) # a的内容为int,a是不可变对象,原实参内容不变\n", "increament_list(b)\n", "print(b) # b的内容为list,b是可变对象,原实参内容会发生变化" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:下面代码运行后,变量`c`的值将会如何变化\n", "\n", "```\n", "def new_change(my_obj):\n", " print(my_obj)\n", " return 666\n", "\n", "c = {1:'a', 2:'b'}\n", "\n", "new_change(c)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:下面代码运行后,变量`c`的值将会如何变化\n", "\n", "```\n", "def new_change(my_obj):\n", " print(my_obj)\n", " return 666\n", "\n", "\n", "c = {1:'a', 2:'b'}\n", "\n", "c = new_change(c)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:下面代码运行后,变量`c`的值将会如何变化\n", "\n", "```\n", "def new_change(my_obj):\n", " my_obj.clear()\n", " return 666\n", "\n", "\n", "c = {1:'a', 2:'b'}\n", "\n", "new_change(c)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:下面代码运行后,变量`a`的值和变量`b`的值将会如何变化\n", "\n", "```\n", "def new_change(my_obj):\n", " my_obj *= 2\n", " return my_obj\n", "\n", "\n", "a = '000'\n", "b = [1,2]\n", "\n", "new_change(a)\n", "new_change(b)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "↓↓↓↓↓↓↓↓↓↓下面内容,课下练习,加深理解↓↓↓↓↓↓↓↓↓↓" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "# def change(my_obj):\n", "# my_obj *= 2\n", "# return my_obj\n", "\n", " \n", "# a = 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`my_obj`是形式参数,变量`a`此时指向不可变对象(因为`type(a) == int`),所以函数调用语句`change(a)`是在进行值传递,`change(a)`的运行结果与`change(1)`的运行结果相同,这里的相同包含两层含义:\n", "- 两条函数调用语句执行后,变量`a`的值都不发生变化,依然是整数1;\n", "- 两条函数调用语句的返回值一样。" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# print('执行语句change(1)之前a的值',a)\n", "# print('change(1)的返回值是 ',change(1))\n", "# print('执行语句change(1)之后a的值',a)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "# print('执行语句change(a)之前a的值',a)\n", "# print('change(a)的返回值是 ',change(a))\n", "# print('执行语句change(a)之后a的值',a)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "# 因为变量a指向的是一个不可变对象(此处为int)\n", "# 所以change(a)是值传递,不能改变变量a的值,变量a的值依然是1(表达更严谨一点,应该是“变量a仍然指向整数1”)\n", "\n", "# a" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# b = [1, 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`my_obj`是形式参数,变量`b`此时指向不可变对象(因为`type(b) == list`),所以函数调用语句`change(b)`是在进行引用传递,`change(b)`的运行结果与`change([1,2])`的运行结果不同,这里的不同是指:\n", "\n", "- `change(b)`执行后,变量`b`的值会发生变化,因为函数内部对变量`b`指向的列表进行了修改;\n", "- `change([1,2])`执行后,不会改变变量`b`的值。\n", "\n", "但是,两者的返回值是一样的。" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "# print('执行语句change([1,2])之前b的值',b)\n", "# print('change([1,2])的返回值是 ',change([1,2]))\n", "# print('执行语句change([1,2])之后b的值',b)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "# print('执行语句change(b)之前b的值',b)\n", "# print('change(b)的返回值是 ',change(b))\n", "# print('执行语句change(b)之后b的值',b)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "# 因为变量b指向的是一个可变对象(此处为list)\n", "# 所以change(b)能改变变量b的值,因为change函数内部对参数my_obj进行了修改操作\n", "# 所以变量b的值发生了变化(表达更严谨一点,应该是“变量b指向的列表发生了变化”)\n", "# b" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "def change(my_obj):\n", " print(id(my_obj))\n", " my_obj *= 2\n", " print(id(my_obj))\n", " return my_obj" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "a = '000'" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685964464\n" ] } ], "source": [ "print(id(a))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685964464\n", "140341685982896\n" ] }, { "data": { "text/plain": [ "'000000'" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "change('000')" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685964464\n" ] } ], "source": [ "print(id(a))" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685964464\n", "140341685647792\n" ] }, { "data": { "text/plain": [ "'000000'" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "change(a)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685964464\n" ] } ], "source": [ "print(id(a))" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "b = [1,2]" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685925056\n" ] } ], "source": [ "print(id(b))" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685649216\n", "140341685649216\n" ] }, { "data": { "text/plain": [ "[1, 2, 1, 2]" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "change([1,2])" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685925056\n" ] } ], "source": [ "print(id(b))" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685925056\n", "140341685925056\n" ] }, { "data": { "text/plain": [ "[1, 2, 1, 2]" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "change(b)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "140341685925056\n" ] } ], "source": [ "print(id(b))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "↑↑↑↑↑↑↑↑↑↑上面内容,课下练习,加深理解↑↑↑↑↑↑↑↑↑↑" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 默认参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python中的函数允许定义**默认参数**,如果函数调用时**没有传入某些参数的值**,那么**参数的默认值**就会被传递给实参。" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "# 此处参数b的默认值为42,如果调用my_func函数时\n", "# 不指定参数b的值,则b取默认值42\n", "def my_func(a, b = 42):\n", " print(a, b)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7 42\n" ] } ], "source": [ "my_func(a = 7)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "# 此处index参数的默认值为2,\n", "# 调用power函数时,如果不指定index参数的值\n", "# 则index默认取值为2,即求平方\n", "def power(base, index=2):\n", " return base ** index" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "power(2)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "power(2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`power`函数中`base`是非默认参数,在调用`power`函数时,必须,必须,必须,指定参数`base`的值,否则会出错。参数`index`是默认值参数,调用`power`函数时,可以指定其取值,也可以不指定,不指定就取默认值,`index=2`。" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "# 下面函数调用代码会出错\n", "# power() # 未指定参数base的值" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "# 下面函数调用代码会出错\n", "# power(index = 3) # 未指定参数base的值" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "# 此处,a,b,c三个参数均指定了默认值\n", "def my_sum(a = 1, b = 2, c = 3):\n", " return a**0 + b**1 + c**2" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "power(2, 5) # 两个参数base和index均传入了非默认实参," ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "power(base = 2, index = 5)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "power(10) # base参数指定为10,index参数使用默认值2" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "12" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum() # a,b,c三个参数均使用默认值,即a = 1, b = 2, c = 3" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "12" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(5) # 参数a取值为5,b,c取默认值" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "16" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(5, 6) # 参数a取值为5,参数b取值为6,参数c取默认值" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "56" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(5, 6, 7) # 参数a,b,c均传入非默认实参,分别为5,6,7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数如果**混用**默认值参数或非默认值参数,则**非默认值参数**必须**定义在**默认值参数之**前**。\n", "\n", "下面这种定义函数的方式会出错,因为默认值参数`base`在非默认值参数`index`之前了。" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [], "source": [ "# def power(base = 10, index):\n", "# return base ** index" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "↓↓↓↓↓↓↓↓↓↓函数重载的定义了解即可↓↓↓↓↓↓↓↓↓↓" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**函数重载**(方法重载):是某些编程语言(如 C++、C#、Java、Swift、Kotlin 等)具有的一项特性,该特性允许创建**多个具有不同实现的同名函数**。对重载函数的调用会运行其**适用于调用上下文的具体实现**,即允许一个函数调用根据上下文执行不同的任务。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "重载函数实际上只是一组具有相同名称的不同函数,具体调用使用哪个函数是在编译期决定的。\n", "例如,重载函数 `Print(text_object T)`和`Print(image_object P)`分别用于打印文本和图像,在程序中打印某种类型的对象时,函数调用语句始终是`Print(something)`,在编译时,编译器会根据传入对象`something`的类型,选择使用重载函数`Print(text_object T)`还是\n", "`Print(image_object P)`。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "↑↑↑↑↑↑↑↑↑↑函数重载的定义了解即可↑↑↑↑↑↑↑↑↑↑" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Python不支持函数重载**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 位置参数和关键字参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用位置参数要求参数按照函数定义时的顺序进行传递。" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "56" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# my_sum定义时,参数的前后位置是a,b,c\n", "# 所以调用时,参数值5,6,7分别对应参数a, b, c\n", "my_sum(5, 6, 7) # 5**0 + 6**1 + 7**2" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "56" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5**0 + 6**1 + 7**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用关键字参数调用函数,可以通过类似`name=value`的格式传递每个参数,**全部使用关键词参数时**,参数位置对应关系不再起作用,看看下面例子:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "56" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(b = 6, a = 5, c = 7)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "56" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(a = 5, c = 7, b = 6)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "56" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(c = 7, a = 5, b = 6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当函数的参数有默认值时,使用关键字参数能够选择其中某些参数传入,其他参数传递其默认值,看下面例子:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "52" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(c = 7) # 1**0 + 2**1 +7**2" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "52" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1**0 + 2**1 +7**2" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "53" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sum(c = 7, b = 3) # 1**0 + 3**1 +7**2" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "53" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1**0 + 3**1 +7**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "位置参数和关键字参数可以**混合使用**。" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "53" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 位置参数和关键词参数混合使用\n", "my_sum(7, c = 7, b = 3) # 7**0 + 3**1 +7**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**位置参数** **不能** 出现在任何关键字参数之**后**,你看下面例子不就错了:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "# 关键字参数在前,位置参数在后,不行,报错了\n", "# my_sum(a = 7, b = 3, 9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "位置参数和关键字参数**不能传给一个形参**。" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [], "source": [ "# 位置参数和关键词参数混用\n", "# 位置参数在前,关键词参数在后,没毛病\n", "# 通过位置参数给参数a赋值7,又通过关键字参数给参数a赋值3\n", "# 不行啊,多重赋值,出错了呀\n", "# my_sum(7, a = 3, c = 9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 可变长度参数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**可变长度参数**:指的是在函数定义时可以使用**个数不确定的参数**,同一函数可以使用不同个数的参数调用。直接看下面例子:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`*args`:可变长度参数元组。Python使用类似`*parameter`的语法来表示可变长度参数,在函数体内可以使用for循环来访问可变长度参数中的内容。" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "def hello_args(para1, *args):\n", " print(\"para1 :\", para1)\n", " \n", " print(\"type(args):\", type(args))\n", " print(\"args :\", args)\n", " for idx,arg in enumerate(args):\n", " print(\"args{}:\".format(idx+1), arg)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "para1 : hello\n", "type(args): \n", "args : ()\n" ] } ], "source": [ "# para1这个参数必须指定哦\n", "# 此处采用的是位置参数哦\n", "hello_args('hello')" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [], "source": [ "# para1这个参数必须指定哦,不信你试试下面代码,会出错\n", "# hello_args()" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "para1 : hello\n", "type(args): \n", "args : ('this', 'is', 'mc.zhang')\n", "args1: this\n", "args2: is\n", "args3: mc.zhang\n" ] } ], "source": [ "# 除了para1参数指定为hello外,我们又指定了三个参数值\n", "hello_args('hello', 'this', 'is', 'mc.zhang')" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "para1 : hello\n", "type(args): \n", "args : ('python', 'course', 'is', 'useful', 'and', 'interesting')\n", "args1: python\n", "args2: course\n", "args3: is\n", "args4: useful\n", "args5: and\n", "args6: interesting\n" ] } ], "source": [ "# 除了para1参数指定为hello外,我们又指定了六个参数值\n", "hello_args('hello', 'python', 'course', 'is', 'useful', 'and', 'interesting')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在上面例子中,`para1 = hello`之后的多个参数会被当做参数元组`args`中的元素,`args`是可变长度的,其中可以包含0个,1个,或者多个元素。再通过下面例子深入理解一下,可变长度元组参数。" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "def hello_variable_args(*paras):\n", " print(\"type(args):\", type(paras))\n", " print(\"args :\", paras)\n", " for idx,arg in enumerate(paras):\n", " print(\"args{}:\".format(idx+1), arg)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "type(args): \n", "args : ()\n" ] } ], "source": [ "hello_variable_args()" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "type(args): \n", "args : ('hello', 'this', 'is', 'mc.zhang')\n", "args1: hello\n", "args2: this\n", "args3: is\n", "args4: mc.zhang\n" ] } ], "source": [ "hello_variable_args('hello', 'this', 'is', 'mc.zhang')" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "type(args): \n", "args : ('hello', 'python', 'course', 'is', 'useful', 'and', 'interesting')\n", "args1: hello\n", "args2: python\n", "args3: course\n", "args4: is\n", "args5: useful\n", "args6: and\n", "args7: interesting\n" ] } ], "source": [ "hello_variable_args('hello', 'python', 'course', 'is', 'useful', 'and', 'interesting')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`**kwargs`:可变长度参数字典,Python还支持另一种形式的可变长度参数的用法,即,使用类似`**parameter`的语法来表示参数,在调用时使用**类似关键字参数**的格式进行传递,或者,使用**字典**传递参数。看下面例子:" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "def hello_kwargs(para1, **kwargs):\n", " print(\"para1 :\", para1)\n", " print(\"type(kwargs):\", type(kwargs))\n", " print(\"kwargs:\", kwargs)\n", " for key, value in kwargs.items():\n", " print(\"{0} = {1}\".format(key, value))" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "para1 : hello\n", "type(kwargs): \n", "kwargs: {'kwarg_1': 'this', 'kwarg_2': 'is', 'kwarg_3': 'mc.zhang'}\n", "kwarg_1 = this\n", "kwarg_2 = is\n", "kwarg_3 = mc.zhang\n" ] } ], "source": [ "hello_kwargs(\"hello\", kwarg_1='this', kwarg_2='is', kwarg_3='mc.zhang')" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "para1 : hello\n", "type(kwargs): \n", "kwargs: {'kwarg_1': 'this', 'kwarg_2': 'is', 'kwarg_3': 'mc.zhang'}\n", "kwarg_1 = this\n", "kwarg_2 = is\n", "kwarg_3 = mc.zhang\n" ] } ], "source": [ "kwargs_dict = {'kwarg_1': 'this', 'kwarg_2': 'is', 'kwarg_3': 'mc.zhang'}\n", "hello_kwargs(\"hello\", **kwargs_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在函数定义时,一般使用`*args`表示可变长度参数元组,`**kwargs`表示可变长度参数字典,当然你可以可以把`args`和`kwargs`替换为任何你喜欢的字符串,比如`*parameter_tuple`,`**parameter_dict`,但是我十分不建议你这么做。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 可变长度参数实例" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [], "source": [ "# 打印参数元组中的每一个参数元素\n", "def my_print(*args):\n", " for item in args:\n", " print(item)" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['a', 'b', 'c']\n", "123\n", "abc\n" ] } ], "source": [ "list_1 = [\"a\",\"b\",\"c\"]\n", "\n", "# 此处,list_1,123,\"abc\"均为为参数元组中的元素\n", "my_print(list_1,123,\"abc\")" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "list_2=[\"a\",\"b\",\"c\"]" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['a', 'b', 'c']\n" ] } ], "source": [ "# 此处,list_2是参数元组中的一个元素,参数元组中只包含一个元素\n", "my_print(list_2)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a\n", "b\n", "c\n" ] } ], "source": [ "# 星号表示解包,此处,将list_2解包后,把list_2中的元素作为参数传入\n", "# 所以,list_2中的每个元素就变成了参数元组中的元素\n", "my_print(*list_2)" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a\n", "b\n", "c\n", "abc\n" ] } ], "source": [ "my_print(*list_2, 'abc')" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a\n", "b\n", "c\n", "abc\n" ] } ], "source": [ "my_print(\"a\",\"b\",\"c\", 'abc')" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "abc\n", "a\n", "b\n", "c\n" ] } ], "source": [ "my_print('abc',*list_2)" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "abc\n", "a\n", "b\n", "c\n" ] } ], "source": [ "my_print('abc', \"a\",\"b\",\"c\")" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "# 此处定义的函数有固定个数的参数\n", "# 在调用时,必须传入两个参数值才行哦\n", "def print_fixed_para(arg1, arg2):\n", " print(\"{}-----{}\".format(arg1, arg2))" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a-----b\n" ] } ], "source": [ "print_fixed_para('a', 'b')" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [], "source": [ "list_3 = ['c', 'd']" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "# 此处,按照位置传参,只传了一个参数,显然错误\n", "# print_fixed_para(list_3)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['c', 'd']-----['c', 'd']\n" ] } ], "source": [ "# 此处按照位置传参,传了两个参数\n", "# 没问题,可以正常调用函数\n", "print_fixed_para(list_3, list_3)" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "c-----d\n" ] } ], "source": [ "# 此处采用列表解包的形式,按位置传递了两个参数值\n", "# 当然可以正常调用函数\n", "print_fixed_para(*list_3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "从上面的例子可以得出:在调用函数的时候,可以使用`*`对列表(或者元组)进行解包,解包的结果可以用于按位置传参。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面的例子,都是解包列表,解包元组同样可以,简单用下面小栗子演示一下:" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "good\n", "boy\n" ] } ], "source": [ "arg_tuple = ('good', 'boy')\n", "my_print(*arg_tuple)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "good\n", "boy\n", "nice\n", "girl\n" ] } ], "source": [ "my_print(*arg_tuple, 'nice', 'girl')# 'nice', 'girl'" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "nice\n", "girl\n", "good\n", "boy\n" ] } ], "source": [ "my_print('nice', 'girl', *arg_tuple)" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "good-----boy\n" ] } ], "source": [ "print_fixed_para(*arg_tuple)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:下面代码的运行结果是什么\n", "\n", "`arg_set = {'good', 'boy'}`\n", "\n", "`print_fixed_para(*arg_set)`\n", "\n", "`my_print(*arg_set)`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "字典传参与列表(元组)传参非常类似,下面,直接看小栗子:" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [], "source": [ "# 定义了一个具有固定个数参数的函数,用来求分数平均值\n", "def avg_grade(chinese = 80, math = 85):\n", " return (chinese + math)/2" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "93.5" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "grade_dict = {'chinese':88,\n", " 'math':99}\n", "\n", "# 对字典进行解包后,按关键字进行参数传递,字典无序哦\n", "avg_grade(**grade_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面的例子对字典解包后,按关键字传参,调用函数`avg_grade`,所以,grade_dict中的key必须,必须,必须,与函数定义中的参数关键字同名,一个字母也不能差。不信,你试试下面例子:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [], "source": [ "# 想一下,为什么下面代码运行出错\n", "# grade_dict = {' chinese':88,\n", "# 'math':99}\n", "\n", "# avg_grade(**grade_dict)" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [], "source": [ "# 定义了一个具有可变个数参数的函数,用来求分数平均值\n", "def avg_grade_mul(chinese = 80, math = 85, **kwargs):\n", " return (chinese + math + sum(kwargs.values()))/(2+len(kwargs))" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "85.75" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "grade_dict = {'english':88,'history':90}\n", "\n", "\n", "# 对字典解包后,按关键字参数调用函数\n", "# 此时的参数有四个,求四科成绩的平均值\n", "avg_grade_mul(**grade_dict)" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "93.5" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "grade_dict = {'chinese':88,\n", " 'math':99}\n", "\n", "# 对字典解包后,按关键字参数调用函数\n", "# 此时的参数有两个\n", "# 字典中的keys与函数的有默认值参数重名\n", "# 用解包后的结果,对默认参数值进行更新\n", "avg_grade_mul(**grade_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`*`是解包元组,`**`是解包字典。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "无论是列表(元组)传参还是字典传参,都可用于调用**有固定个数参数的函数**和**有可变个数参数的函数**,字典传参的时候,keys要与函数的参数名一样哦。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 不同类型参数定义顺序" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "位置参数,默认值参数,可变参数,命名参数,可变命名参数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "先看例子:" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [], "source": [ "def my_func(a, b, c = 0, *args, d, **kwargs):\n", " print('a: {}'.format(a),\n", " 'b: {}'.format(b),\n", " 'c: {}'.format(c),\n", " 'args: {}'.format(args),\n", " 'd: {}'.format(d),\n", " 'kwargs: {}'.format(kwargs),\n", " sep='\\n')" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a: a\n", "b: b\n", "c: 88\n", "args: (-1, -2, -3)\n", "d: d\n", "kwargs: {'name': 'Tom', 'age': '18'}\n" ] } ], "source": [ "my_func('a', 'b', 88, *[-1, -2, -3], d ='d', **{'name':'Tom','age':'18'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`my_func(a, b, c = 0, *args, d, **kwargs)`中:\n", "\n", "- `a`,`b`是位置参数,位置参数一定是在最前面;\n", "\n", "- `c=0`是默认值参数,它有默认值,在调用时不是必须传参数值的;\n", "\n", "- `*args`是可变参数,可以通过解包元组(或列表)来按照位置传参;\n", "\n", "- `d`是命名参数,传参数时,必须带上参数名,你看上面的参数调用语句中`d = 'd'`;\n", "\n", "- `**kwargs`是可变命名参数,可以通过解包字典来按照关键字传参。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "自己运行下面的函数`my_func_1`至函数`my_func_5`的定义,根据运行结果加深对上述**参数定义顺序**的理解:" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [], "source": [ "# def my_func_1(a, b, c = 0, d, *args,**kwargs):\n", "# print('a: {}'.format(a),\n", "# 'b: {}'.format(b),\n", "# 'c: {}'.format(c),\n", "# 'args: {}'.format(args),\n", "# 'd: {}'.format(d),\n", "# 'kwargs: {}'.format(kwargs),\n", "# sep='\\n')" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [], "source": [ "# def my_func_2(a, b, c = 0, *args, **kwargs, d):\n", "# print('a: {}'.format(a),\n", "# 'b: {}'.format(b),\n", "# 'c: {}'.format(c),\n", "# 'args: {}'.format(args),\n", "# 'd: {}'.format(d),\n", "# 'kwargs: {}'.format(kwargs),\n", "# sep='\\n')" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [], "source": [ "# def my_func_3(**kwargs, a, b, c = 0, *args, d):\n", "# print('a: {}'.format(a),\n", "# 'b: {}'.format(b),\n", "# 'c: {}'.format(c),\n", "# 'args: {}'.format(args),\n", "# 'd: {}'.format(d),\n", "# 'kwargs: {}'.format(kwargs),\n", "# sep='\\n')" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [], "source": [ "# def my_func_4(a, b, c = 0, d, **kwargs, *args):\n", "# print('a: {}'.format(a),\n", "# 'b: {}'.format(b),\n", "# 'c: {}'.format(c),\n", "# 'args: {}'.format(args),\n", "# 'd: {}'.format(d),\n", "# 'kwargs: {}'.format(kwargs),\n", "# sep='\\n')" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [], "source": [ "# def my_func_5(d, a, b, c = 0, *args, **kwargs):\n", "# print('a: {}'.format(a),\n", "# 'b: {}'.format(b),\n", "# 'c: {}'.format(c),\n", "# 'args: {}'.format(args),\n", "# 'd: {}'.format(d),\n", "# 'kwargs: {}'.format(kwargs),\n", "# sep='\\n')" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [], "source": [ "# def my_func_6(*args, a, b, c = 0, d, **kwargs):\n", "# print('a: {}'.format(a),\n", "# 'b: {}'.format(b),\n", "# 'c: {}'.format(c),\n", "# 'args: {}'.format(args),\n", "# 'd: {}'.format(d),\n", "# 'kwargs: {}'.format(kwargs),\n", "# sep='\\n')" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## 函数实例" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段。函数通过对常用功能进行封装,能提高程序的模块性和代码的重复利用率。直接看例子吧:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**第一个例子是**:\n", "\n", "检查用户键盘输入的年月日是否合法。" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [], "source": [ "# 定义一个检测年份是否为闰年的函数\n", "def is_leap_year(year):\n", " if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):\n", " return True\n", " else:\n", " return False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "目前使用的格里高利历闰年规则如下:\n", "\n", "- 公元年份为4的倍数但非100的倍数,为闰年;\n", "- 公元年份为400的倍数,为闰年。" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [], "source": [ "# 根据年份和月份,返回该月份的天数\n", "def get_days_in_month(month, year):\n", " if month in [1, 3, 5, 7, 8, 10, 12]:\n", " return 31\n", " elif month in [4, 6, 9, 11]:\n", " return 30\n", " elif is_leap_year(year):\n", " return 29\n", " else:\n", " return 28" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [], "source": [ "def validate_year(year):\n", " return year > 0" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [], "source": [ "def validate_month(month):\n", " if 0< month < 13:\n", " return True\n", " else:\n", " return False" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [], "source": [ "def validate_day(year, month, day):\n", " return 0 <= day <= get_days_in_month(month, year) " ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [], "source": [ "def validate_date(year, month, day):\n", " return all([validate_year(year),\n", " validate_month(month), \n", " validate_day(year, month, day)])" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [], "source": [ "def main():\n", " year = int(input('Year: '))\n", " month = int(input('Month: '))\n", " day = int(input('Day: '))\n", " if is_date_validate(year, month, day):\n", " print('Valid')\n", " else:\n", " print('Invalid')" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [], "source": [ "# main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**第二个例子是**:\n", "\n", "课件身份证号校验例题:给定一个身份证号,验证其是否合法:(1) 总体校验;(2) 校验地区;(3) 校验日期。\n", "\n", "https://www.dute.org/fake-id-card-number" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [], "source": [ "def verify_char_length(ids):\n", " if len(ids) != 18:\n", " return False\n", " if not ids[:-1].isdigit():\n", " return False\n", " if ids[-1] not in '0123456789X':\n", " return False\n", " return True" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [], "source": [ "def verify_last_num(ids):\n", " ids_17 = ids[:-1]\n", " weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]\n", " S = sum([int(num)*weight for num,weight in zip(list(ids_17),weights)])\n", " T = S % 11\n", " R = (12 - T) % 11\n", " if R == 10:\n", " last_num = 'X'\n", " else:\n", " last_num = R\n", " if ids[-1] == str(last_num):\n", " return True\n", " else:\n", " return False" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [], "source": [ "def verify_area(ids):\n", " import _locale\n", " _locale._getdefaultlocale = (lambda *args: ['zh_CN', 'utf8'])\n", " with open('./area_dict.json') as f:\n", " area_dict = eval(f.read())\n", " if ids[:6] not in area_dict.keys():\n", " return False\n", " else:\n", " return True" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [], "source": [ "def is_leap_year(year):\n", " if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):\n", " return True\n", " else:\n", " return False" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [], "source": [ "def verify_date(ids):\n", " year = int(ids[6:10])\n", " if year > 2022 or year < 1900:\n", " return False\n", " month = int(ids[10:12])\n", " if month > 12 or month < 1:\n", " return False\n", " day = int(ids[12:14])\n", " if month in [1,3,5,7,8,10,12]:\n", " if day > 31 or day < 1:\n", " return False\n", " elif month in [4,6,9,11]:\n", " if day > 30 or day < 1:\n", " return False\n", " else:\n", " if is_leap_year(year):\n", " if day > 29 or day < 1:\n", " return False\n", " else:\n", " if day > 28 or day < 1:\n", " return False\n", " return True" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [], "source": [ "def verify_id(ids):\n", " if verify_char_length(ids):\n", " if all([verify_last_num(ids),verify_area(ids),verify_date(ids)]):\n", " print(\"VALID\")\n", " return True\n", " else:\n", " print(\"INVALID\")\n", " return False\n", " else:\n", " print(\"INVALID\")\n", " return False" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "VALID\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "verify_id(ids = \"110113198702121018\")" ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INVALID\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "verify_id(ids = \"110113198703121018\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 匿名函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "lambda表达式用于创建一个**匿名**函数,即**没有与标识符绑定**的函数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "写法:`lambda 参数列表:表达式`; \n", "\n", "- lambda表达式以`lambda`关键字开始,参数列表与一般函数的参数列表的语法格式相同,参数间用逗号隔开,允许参数有默认值;\n", "\n", "- 表达式为匿名函数的返回值,但**只能由一个表达式组成**,不能有其他的复杂结构;\n", "\n", "- 通常而言,可**将lambda表达式赋值给一个变量**,这个变量就可以作为函数使用,此时就把lambda表达式和变量绑定在一起了;\n", "- 调用lambda表达式的语法**与调用函数**完全**相同**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "下面看几个小栗子来理解上面的理论内容:" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [], "source": [ "my_sum = lambda x, y:x+y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面定义了一个两个数求和的匿名函数赋值给了变量`my_sum`;\n", "\n", "来来来,看一下匿名函数的三个部分啊,\n", "\n", "- 第一部分是关键字`lambda`;\n", "\n", "- 第二部分是参数列表,可以是多个,此处为`x, y`;\n", "\n", "- 第三部分是表达式,只能有一个,一个,一个,哦,此处为`x+y`;\n", "\n", "- 变量和表达式之间要有冒号哦,类似于定义函数时,参数列表后有冒号。" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [], "source": [ "my_sub = lambda y, x=10: x- y" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_sub(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面定义了一个求两个数减法的匿名函数,被减数默认值为10。同时,把定义的匿名函数赋值给了变量`my_sub`,之后就像函数调用那样去调用了定义的匿名函数,`my_sub(1)`。" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [], "source": [ "my_list = list(range(10, 24))" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_list" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [], "source": [ "import random" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [], "source": [ "random.shuffle(my_list)" ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[13, 22, 20, 12, 11, 14, 16, 15, 19, 17, 23, 18, 10, 21]" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_list" ] }, { "cell_type": "code", "execution_count": 137, "metadata": {}, "outputs": [], "source": [ "my_sort_key = lambda item:item" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10]" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sorted(my_list, key = my_sort_key, reverse=True)" ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[13, 22, 20, 12, 11, 14, 16, 15, 19, 17, 23, 18, 10, 21]" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "再来看一下待条件表达式的lambda函数:" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [], "source": [ "my_max = lambda x, y:x if x>y else y" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [], "source": [ "my_min = lambda x, y:x if x思考题:\n", "\n", "写一个列表推导式,将`my_list`中奇数元素对应的位置为1,偶数对应的位置为0,将列表推导式生成的新列表赋值给变量`my_new_list`。" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 直接输出匿名函数的结果\n", "(lambda x, y:x**y)(2,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## map、reduce、filter 函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**map函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`map`函数**遍历** **序列** **每个值**进行操作,返回一个**`map`对象**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "语法:`map(function, sequence, ....)`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- `function`:函数\n", "- `sequence`:序列" ] }, { "cell_type": "code", "execution_count": 145, "metadata": {}, "outputs": [], "source": [ "def my_square(x):\n", " return x**2" ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [], "source": [ "list_6 = [1, 2, 3]\n", "list_7 = [4, 5, 6, 7]\n", "list_8 = [8, 9, 10, 11, 12]" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [], "source": [ "result = map(my_square, list_6)" ] }, { "cell_type": "code", "execution_count": 148, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "map" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(result)" ] }, { "cell_type": "code", "execution_count": 149, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 149, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(result)" ] }, { "cell_type": "code", "execution_count": 150, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(result)" ] }, { "cell_type": "code", "execution_count": 151, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面对一个序列进行map操作,并使用`next`方法逐个访问返回的`map`对象。下面使用`for`循环访问map结果。" ] }, { "cell_type": "code", "execution_count": 152, "metadata": {}, "outputs": [], "source": [ "result = map(my_square, list_6)" ] }, { "cell_type": "code", "execution_count": 153, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "4\n", "9\n" ] } ], "source": [ "for item in result:\n", " print(item)" ] }, { "cell_type": "code", "execution_count": 154, "metadata": {}, "outputs": [], "source": [ "result = map(lambda x,y:x*y, list_6, list_7)" ] }, { "cell_type": "code", "execution_count": 155, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "map" ] }, "execution_count": 155, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(result)" ] }, { "cell_type": "code", "execution_count": 156, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[4, 10, 18]" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`len(list_6) < len(list_7)`,所以最终结果的长度是`min(len(list_6), len(list_7))`" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[4, 10, 18]" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x,y,z:x*y, list_6, list_7, list_8))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "注意:`function`的参数个数,与后面的序列个数要一致哦。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**filter函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`filter`函数,过滤出序列中的元素。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 语法:`filter(function, iterable)`;\n", "\n", "- `function`,判断函数;\n", "\n", "- `iterable`,可迭代对象;\n", "\n", "- 返回filter对象。" ] }, { "cell_type": "code", "execution_count": 158, "metadata": {}, "outputs": [], "source": [ "str_1 = 'aHbDkLuY'" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [], "source": [ "import string" ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [], "source": [ "# 过滤出字符串中的大写字母\n", "result = filter(lambda char:char in string.ascii_uppercase, str_1)" ] }, { "cell_type": "code", "execution_count": 161, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "filter" ] }, "execution_count": 161, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(result)" ] }, { "cell_type": "code", "execution_count": 162, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['H', 'D', 'L', 'Y']" ] }, "execution_count": 162, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使得`function`返回True(或等价于True)的元素被取出来,看下面例子:" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 163, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda char:None, str_1))" ] }, { "cell_type": "code", "execution_count": 164, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['a', 'H', 'b', 'D', 'k', 'L', 'u', 'Y']" ] }, "execution_count": 164, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda char:char, str_1))" ] }, { "cell_type": "code", "execution_count": 165, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 165, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda char:'', str_1))" ] }, { "cell_type": "code", "execution_count": 166, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 166, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda char:[], str_1))" ] }, { "cell_type": "code", "execution_count": 167, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['a', 'H', 'b', 'D', 'k', 'L', 'u', 'Y']" ] }, "execution_count": 167, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda char:char.lower(), str_1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`function`最好是显示地定义返回布尔值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**reduce函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`reduce`函数会对参数序列中元素进行**累积**,返回一个值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数将一个数据对象(列表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "语法:`reduce(function, iterable[, initializer])`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "参数:\n", "\n", "- function,函数,**有两个参数**;\n", "\n", "- iterable,可迭代对象;\n", "\n", "- initializer,可选,初始参数。" ] }, { "cell_type": "code", "execution_count": 168, "metadata": {}, "outputs": [], "source": [ "from functools import reduce" ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 169, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(lambda x, y: x + y, [1,2,3,4,5])" ] }, { "cell_type": "code", "execution_count": 170, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "115" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(lambda x, y: x + y, [1,2,3,4,5], 100)" ] }, { "cell_type": "code", "execution_count": 171, "metadata": {}, "outputs": [], "source": [ "kua_list = ['眉清目秀', '高大威猛', '英俊潇洒', '风流倜傥', '人见人爱', '花见花开', '车见车栽', '才高八斗', '学富五车']" ] }, { "cell_type": "code", "execution_count": 172, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['眉清目秀', '高大威猛', '英俊潇洒', '风流倜傥', '人见人爱', '花见花开', '车见车栽', '才高八斗', '学富五车']" ] }, "execution_count": 172, "metadata": {}, "output_type": "execute_result" } ], "source": [ "kua_list" ] }, { "cell_type": "code", "execution_count": 173, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'你真是、眉清目秀、高大威猛、英俊潇洒、风流倜傥、人见人爱、花见花开、车见车栽、才高八斗、学富五车'" ] }, "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(lambda x, y: x + '、'+ y, kua_list, '你真是')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 递归函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "一个函数在内部也可以**调用自身**。我们将直接或间接调用自身的函数称为递归函数,将使用递归函数来解决问题的编程技巧称为递归。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "递归能够将一个大型的、复杂的问题层层转换为一个与原问题相似的小规模问题来求解,给出一个自然、直观、简单的解决方案。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "先看例子,在数学上,斐波那契数是以递归的方法来定义:\n", "\n", "$F_0 = 0$\n", "\n", "$F_1 = 1$\n", "\n", "$F_{n} = F_{n-1} + F_{n-2}$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "用文字来说,就是斐波那契数列由0和1开始,之后的斐波那契数就是由之前的两数相加而得出。前几个斐波那契数是:\n", "\n", "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377 ,610, 987\n", "\n", "**特别指出**:0不是第一项,而是第零项。\n", "\n", "定义一个函数,用于输出斐波那契数列的第n项,从第0项开始计数。" ] }, { "cell_type": "code", "execution_count": 174, "metadata": {}, "outputs": [], "source": [ "def fibo(n):\n", " if n == 0:\n", " return 0\n", " elif n == 1:\n", " return 1\n", " else:\n", " return fibo(n-1) + fibo(n-2)" ] }, { "cell_type": "code", "execution_count": 175, "metadata": {}, "outputs": [], "source": [ "l = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377 ,610, 987]" ] }, { "cell_type": "code", "execution_count": 176, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 0\n", "1 1\n", "2 1\n", "3 2\n", "4 3\n", "5 5\n", "6 8\n", "7 13\n", "8 21\n", "9 34\n", "10 55\n", "11 89\n", "12 144\n", "13 233\n", "14 377\n", "15 610\n", "16 987\n" ] } ], "source": [ "# 打印斐波那契数列的前几项\n", "for idx, item in enumerate(l):\n", " print(idx, item)" ] }, { "cell_type": "code", "execution_count": 177, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibo(17) == 610 + 987" ] }, { "cell_type": "code", "execution_count": 178, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "610" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def my_fibo(n):\n", " a = 0\n", " b = 1\n", " result = 0\n", " for i in range(n-1): \n", " result = a + b\n", " a = b\n", " b = result\n", " return result\n", "\n", "my_fibo(15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "思考题:定义函数`my_fact(n)`,求n的阶乘,$n >= 0$,为正整数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**递归函数的特点**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 函数会使用**选择结构**将问题分成不同的情况;\n", "- 函数中会有**一个或多个基础情况**用来结束递归;\n", "- 非基础情况的分支会递归调用自身,递归会将原始问题简化为一个或多个子问题,这些子问题与原问题**性质一样但规模更小**;\n", "- 每次递归调用会**不断接近基础情况**,直到变成基础情况,终止递归。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "递归函数的优点是定义简单、逻辑清晰,但是一般的递归函数会**占用大量的程序栈**,尤其是当递归深度特别大的时候,有可能导致**堆栈溢出**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在编写递归函数时,需要仔细考虑**边界情况(基础情况)**,当递归**不能**使全部的问题**简化收敛到边界情况时**,程序就会**无限运行**下去,并且在程序**堆栈溢出**时导致运行时的**错误**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "↓↓↓↓↓↓↓↓↓↓堆栈溢出的定义了解即可↓↓↓↓↓↓↓↓↓↓" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**堆栈溢出**(stack overflow)在计算机科学中是指使用过多的存储器时导致调用堆栈产生的溢出,也是缓冲区溢出中的一种。堆栈溢出的产生是由于过多的函数调用,导致使用的调用堆栈大小**超过事先规划的大小**,覆盖其他存储器内的资料,**一般在递归中产生**。堆栈溢出很可能由无限递归(infinite recursion)产生,但也可能仅仅是过多的堆栈层级。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "↑↑↑↑↑↑↑↑↑↑堆栈溢出的定义了解即可↑↑↑↑↑↑↑↑↑↑" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 生成器" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "生成器是创建**迭代器对象**的一种简单而强大的工具,生成器的**语法**就像正常的**函数**,只是**返回数据**时需要**使用`yield`**语句而非return语句。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "与一般函数不同的是,一般函数在**执行到return**语句时,会**结束函数**的执行;而生成器在**执行到yield**语句时,并不会终止执行,而是**继续**向后执行,直至函数结束。\n", "\n", "如果生成器中执行了**多个yield**语句,那么生成器将会把这些yield语句中**所有要返回的值**组成一个**生成器对象**并返回。 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在生成器外部,可以通过**`next`函数** **依次** 获得**每一个值**,也可以将其**转换**为某一类型的**可迭代对象**(如列表、元组等)。" ] }, { "cell_type": "code", "execution_count": 179, "metadata": {}, "outputs": [], "source": [ "def fibo_generator(n):\n", " if n == 1:\n", " yield 0\n", " elif n == 2:\n", " yield 0\n", " yield 1\n", " else:\n", " yield 0\n", " yield 1\n", " a = 0\n", " b = 1\n", " for _ in range(n-2):\n", " c = a + b\n", " yield c\n", " a,b = b,c" ] }, { "cell_type": "code", "execution_count": 180, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "1\n" ] } ], "source": [ "for item in fibo_generator(3):\n", " print(item)\n", "fibo_list = list(fibo_generator(3))" ] }, { "cell_type": "code", "execution_count": 181, "metadata": {}, "outputs": [], "source": [ "my_gene = fibo_generator(5) # 0, 1, 1, 2, 3, " ] }, { "cell_type": "code", "execution_count": 182, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "generator" ] }, "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(my_gene)" ] }, { "cell_type": "code", "execution_count": 183, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 183, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(my_gene)" ] }, { "cell_type": "code", "execution_count": 184, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(my_gene)" ] }, { "cell_type": "code", "execution_count": 185, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(my_gene)" ] }, { "cell_type": "code", "execution_count": 186, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(my_gene)" ] }, { "cell_type": "code", "execution_count": 187, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(my_gene)" ] }, { "cell_type": "code", "execution_count": 188, "metadata": {}, "outputs": [], "source": [ "# 迭代完之后,再迭代就会出错哦\n", "# next(my_gene)" ] }, { "cell_type": "code", "execution_count": 189, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 1, 2, 3]" ] }, "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 使用list函数将可迭代对象变为列表\n", "list(fibo_generator(5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "回忆一下我们之前讲推导式时,顺带讲过的一种生成器创建方法,那就是,用括号把推导式包起来,如下例子:" ] }, { "cell_type": "code", "execution_count": 190, "metadata": {}, "outputs": [], "source": [ "gene_1 = (i**2 for i in range(10))" ] }, { "cell_type": "code", "execution_count": 191, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "generator" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(gene_1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "有列表推导式,中括号包起来推导式,有集合推导式,花括号抱起来推导式,有字典推导式,花括号包起来推导式,但是它的起始位置是`键:值`哦;\n", "\n", "没有元组推导式,用圆括号包起来的推导式,是生成器推导式哦;\n", "\n", "关于推导式的内容,再去看看`lecture_4.ipynb`." ] }, { "cell_type": "code", "execution_count": 192, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 192, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(gene_1)" ] }, { "cell_type": "code", "execution_count": 193, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 193, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(gene_1)" ] }, { "cell_type": "code", "execution_count": 194, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n", "9\n", "16\n", "25\n", "36\n", "49\n", "64\n", "81\n" ] } ], "source": [ "# 也可以使用for循环访问生成器的内容哦\n", "# 在迭代完之后就会自动结束for循环\n", "# 不会像next一样,迭代完,再迭代就会报错stop iteration error\n", "for item in gene_1:\n", " print(item)" ] }, { "cell_type": "code", "execution_count": 195, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "1\n", "2\n", "3\n" ] } ], "source": [ "for item in fibo_generator(5):\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "生成器对象一旦迭代完毕,再迭代就不会输出值,除非重新创建一次迭代器对象。你试试下面代码,看看有米有输出:" ] }, { "cell_type": "code", "execution_count": 196, "metadata": {}, "outputs": [], "source": [ "for item in gene_1:\n", " print(item)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "那你思考一下,为什么下面代码依旧能输出:" ] }, { "cell_type": "code", "execution_count": 199, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "1\n", "2\n", "3\n", "5\n", "8\n", "13\n", "21\n" ] } ], "source": [ "for item in fibo_generator(9):\n", " print(item)" ] }, { "cell_type": "code", "execution_count": 198, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "END\n" ] } ], "source": [ "print('END')" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.7" } }, "nbformat": 4, "nbformat_minor": 4 }