{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 实践3 函数与模块\n", "\n", "1. 理解函数的基本概念;\n", "2. 掌握Python内置函数的使用;\n", "3. 掌握Python标准模块的引入;\n", "4. 掌握math模块中常用数学函数的使用;\n", "5. 理解函数的定义和调用。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "程序里的函数概念完全来自数学里的函数。我们先来看看数学里的函数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 函数的基本概念" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在数学里,我们可以定义一个函数,函数有一些*自变量*,也就是一些**输入**,**函数定义**就是定义如何通过这些输入计算出*函数的值*,也就是**输出**。下面是个典型的函数定义的例子:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\n", "\\begin{align}\n", "f(a, b) = \\sqrt{a^2 + b^2}\n", "\\end{align}\n", "$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数定义好之后,随时可以用具体的值送进去算出一个具体的函数值,比如:\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\n", "\\begin{align}\n", "f(1, 2) = \\sqrt{5}\n", "\\end{align}\n", "$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\n", "\\begin{align}\n", "f(3, 4) = 5\n", "\\end{align}\n", "$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$\n", "\\begin{align}\n", "f(2, 6) = 2\\sqrt{10}\n", "\\end{align}\n", "$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "程序里的函数也是这样,我们先要定义函数,也就是定义它的名字(叫做 **函数名** *function name*)、若干输入变量(叫做 **参数** *argument*)以及如何通过输入变量算出函数的值(叫做 **返回值** *return value*);一旦定义好,就可以在程序的其他地方随时使用这个函数,给它具体的参数值,它就给出具体的返回值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 调用现成的函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们先看看怎么用函数,术语叫做函数**调用**(*call*),因为 Python 本身自带了很多已经定义好的函数,我们可以直接拿来用。其中有一些我们之前已经用过了:\n", "* `print(a, b,...)`:在命令行输出界面打印输入的参数(可以是各种类型的数据);\n", "* `type(x)`:返回参数 x 的数据类型;\n", "* `abs(x)`:返回参数 x(整数或者浮点数)的绝对值。\n", "\n", "这几个都是 Python 的内置函数,可以直接用。我们下面来看两个其他的例子。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "第一个例子是另一个 Python 内置函数,叫做 `isinstance(x, c)`,它有两个输入参数,第一个是一个数据,第二个是一个数据类型,如果第一个数据是第二个数据类型,`isinstance` 函数返回 `True`,否则就返回 `False`。所以 `isinstance` 可以用来判断某个值或者变量是不是某种类型,下面是例子:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(a, int)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(a, float)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(g, float)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(True, str)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(True, bool)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance('abracadabra', str)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(True, bool) and not isinstance('abracadabra', float)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.模块\n", "\n", "第二个例子是 Python 的平方根函数 `math.sqrt(x)`,这个函数前面有个 `math`,这表示函数 `sqrt(x)` 不在 Python 的内置环境中,而是放在一个叫 `math` 的函数包(学名叫 **模块** *module*)里,关于模块我们以后会专门介绍。要使用这种函数,第一种方法是:\n", "* 先 **引入**(*import*)对应的函数包;\n", "* 使用时函数名前面要带上模块的名字,中间加个 `.`。\n", "\n", "就像下面这样:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.0" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import math\n", "math.sqrt(16)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "另外一种方法是,从对应的模块里指明引入这一个函数,使用时就不用带上模块名了:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.324555320336759" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from math import sqrt\n", "sqrt(40)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以看到 `sqrt(x)` 函数可以接受整数或者浮点数输入,返回值是浮点数。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 定义函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "有了 `sqrt(x)` 这个函数,我们可以定义一个函数来实现前面举例的那个数学里的函数 `f(a, b)` 了。在 Python 里函数的定义以关键字 `def` 开始,后面依次是函数名、小括号括起来的参数列表和一个冒号,之后的代码就是函数的算法定义,直到关键字 `return` 开始的 **返回语句**,这个返回语句会终止函数的运行,并把 `return` 后面的值作为函数的返回值,回到调用函数的地方。看起来有点抽象,我们看看例子: " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5.0\n" ] } ], "source": [ "from math import sqrt\n", "\n", "def f(a, b):\n", " return sqrt(a**2 + b**2)\n", "\n", "result = f(3, 4)\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上面的代码很短,但是很重要,我们务必要搞懂这里的每一个细节。\n", "\n", "首先第一行是引入 `sqrt(x)` 函数,因为我们下面定义的函数要用到它。\n", "\n", "然后是函数定义 `def f(a, b):`,关键字 `def` 打头,后面是函数名 `f`、参数列表 `(a, b)` 和一个冒号,这表示函数 `f` 接受两个参数,一个叫 `a` 一个叫 `b`。下面一行就是**函数体**(*function body*),里面可以写很多东西,只要最后有一个 `return` 指明函数返回值就行,注意 Python 的函数可以没有返回值(`return` 后面什么都不写就行了),也可以返回多个值(`return` 后面写多个值,用逗号隔开)。\n", "\n", "我们这个例子因为很简单,就一句话就能搞定:函数返回值等于 a 平方加 b 平方再开方。注意函数体的代码缩进了一些,和其他的行都是顶着行首书写不一样,这是 Python 的缩进规则要求的,下面的插播会做出解释。\n", "\n", "然后是调用函数的例子,它是这么进行的:\n", "1. 首先用 3 和 4 作为参数来调用 `f()` 函数;\n", "2. 函数 `f()` 开始执行,把值 3 赋给参数变量 a,4 赋给 b,然后执行函数体;\n", "3. 函数体计算出返回值 5.0 并返回;\n", "4. 用返回值 5.0 替换掉 `f()` 的位置,相当于执行赋值 `result = 5.0`\n", "5. 用 5.0 作为参数值调用内置的 `print()` 函数,打印出 5.0(`print()` 没有返回值)。\n", "\n", "顺便说一句,熟练之后我们可以把后两行简化成一句 `print(f(3, 4))`,效果是完全一样的。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 插播:代码缩进" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "所谓代码缩进就是一行代码不顶格写,而是在行首空若干格再写。缩进可以很多层,比如 Python 中第一层缩进空四格,第二层缩进空八格,第三层缩进空十二格,依此类推。程序员都喜欢用“**等宽字体**(*fix-width font*)”,就是因为等宽字体在缩进时非常整齐美观。\n", "\n", "缩进是为了让代码层次更加鲜明地呈现出来。比如上面函数定义的代码,函数体的部分是“隶属于”函数定义 `def` 语句的,是它的组成部分,所以缩进一层表示这个从属关系;而后面的函数调用则不是,所以最后两行不缩进,和 `def` 语句平齐,代表是同一层级的代码。\n", "\n", "在 Python 中缩进是强制的,如果不进行正确的缩进,比如如果函数体没有正确的缩进,解释器会因为找不到必须的部分而报错。\n", "\n", "目前主流的程序编辑器都提供了自动缩进、自动排版的功能,在 VSCode 和 Jupyter Notebook 中书写代码时大部分时候都会自动正确的缩进,所以很方便。\n", "\n", "对于初学者来说,知道有这么回事,然后多看,慢慢就会习惯成自然,和多听多看学语言是差不多的道理。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4.函数的调用 \n", "函数定义后,未经调用,是不会执行的。需要时使用函数调用语句。 \n", "在函数调用的时候要求传递对应的值给形参,这个传递的值称为实际参数,简称实参。参数是可选的,也就是说函数可以不包括参数,例如:random.random() 就不包含参数。 \n", "根据函数是否有返回值,函数的调用也是不同的。如果函数有返回值,函数调用的时候需要安排接受返回值。 \n", "\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.324555320336759" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 可以是赋值语句,将返回值赋值给一个变量\n", "z = f(2,6)\n", "z" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "50.0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 可以是表达式语句,将返回值作为表达式的一个数值,继续参加运算\n", "z = f(3,4)*10\n", "z\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "11.180339887498949" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 可以作为函数调用的实参\n", "z=f(f(3,4),10)\n", "z\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 函数的意义" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "函数是编程中最重要的概念之一。实现它有助于我们实现程序的“**模块化**(*modulization*)”,我们可以把特定的工作用一个函数来实现,然后在需要的地方反复地调用它。这是一种代码重用的方法,一个函数就好像乐高积木里的一个组件,我们可以通过组合各种不同功能的函数来实现复杂的目标。\n", "\n", "函数还是协作编程的基础。如果我们实现了一个很有用的函数,我们可以写一个文档说明函数的输入参数和返回值是什么,这样的文档叫做**调用接口**(*interface*),别人都可以照此使用我们写的函数,甚至不需要了解它是怎么实现的(比如我们就直接拿了 `math.sqrt()` 来用而不需要自己写一个)。函数天生就把自己的接口和实现分离开,让协作和复用成果更容易。\n", "\n", "**模块化** 和 **接口独立** 是编程时最重要的两个思维模型(没有之一),我们在后面还会深入介绍。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 小试身手\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### (1)使用math模块的数学函数。 \n", "导入数学库math。然后输入以下表达式理解math中函数的使用,注意结果值的数据类型。 \n", "math.sqrt(2*2+3*3)、 math.log10(100)、 math.exp(2)、math.fmod(4,3)、math.sin(2*math.pi)、\n", "math.gcd(12,9)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[数学库math](https://docs.python.org/zh-cn/3/library/math.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### (2)自定义求矩形面积的函数" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#定义一个函数getArea(length,width),计算长为length,宽为width的矩形面积。 \n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#试着用不同的方式调用getArea函数,验证函数的正确性\n", "\n" ] } ], "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.5" } }, "nbformat": 4, "nbformat_minor": 4 }