forked from jackfrued/Python-Core-50-Courses
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
726 additions
and
2 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
## 第008课:函数和模块 | ||
|
||
在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。 | ||
|
||
![](res/equation.png) | ||
|
||
事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案,所以答案应该是$C_7^3=35$。组合数的计算公式如下所示。 | ||
|
||
![](res/combination.png) | ||
|
||
根据我们前面学习的知识,可以用循环做累乘来计算阶乘,那么通过下面的Python代码我们就可以计算出组合数`C(M,N)`的值,代码如下所示。 | ||
|
||
```Python | ||
""" | ||
输入M和N计算C(M,N) | ||
Version: 0.1 | ||
Author: 骆昊 | ||
""" | ||
m = int(input('m = ')) | ||
n = int(input('n = ')) | ||
# 计算m的阶乘 | ||
fm = 1 | ||
for num in range(1, m + 1): | ||
fm *= num | ||
# 计算n的阶乘 | ||
fn = 1 | ||
for num in range(1, n + 1): | ||
fn *= num | ||
# 计算m-n的阶乘 | ||
fm_n = 1 | ||
for num in range(1, m - n + 1): | ||
fm_n *= num | ||
# 计算C(M,N)的值 | ||
print(fm // fn // fm_n) | ||
``` | ||
|
||
### 函数的作用 | ||
|
||
不知道大家是否注意到,上面的代码中我们做了三次求阶乘,虽然`m`、`n`、`m - n`的值各不相同,但是三段代码并没有实质性的区别,属于重复代码。世界级的编程大师*Martin Fowler*先生曾经说过:“**代码有很多种坏味道,重复是最坏的一种!**”。要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。 | ||
|
||
### 定义函数 | ||
|
||
数学上的函数通常形如`y = f(x)`或者`z = g(x, y)`这样的形式,在`y = f(x)`中,`f`是函数的名字,`x`是函数的自变量,`y`是函数的因变量;而`z = g(x, y)`中,`g`是函数名,`x`和`y`是函数的自变量,`z`是函数的因变量。Python中的函数跟这个结构是一致的,每个函数都有自己的名字、自变量和因变量。我们通常把Python中函数的自变量称为函数的参数,而因变量称为函数的返回值。 | ||
|
||
在Python中可以使用`def`关键字来定义函数,和变量一样每个函数也应该有一个漂亮的名字,命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,就是我们刚才说到的函数的自变量,而函数执行完成后我们会通过`return`关键字来返回函数的执行结果,就是我们刚才说的函数的因变量。 | ||
|
||
我们可以用函数的知识对上面的代码进行重构(不影响代码执行结果的前提下对代码的结构进行调整),重构之后的代码如下所示。 | ||
|
||
```Python | ||
""" | ||
输入M和N计算C(M,N) | ||
Version: 0.1 | ||
Author: 骆昊 | ||
""" | ||
|
||
|
||
# 定义函数:def是定义函数的关键字、fac是函数名,num是参数(自变量) | ||
def fac(num): | ||
"""求阶乘""" | ||
result = 1 | ||
for n in range(1, num + 1): | ||
result *= n | ||
# 返回num的阶乘(因变量) | ||
return result | ||
|
||
|
||
m = int(input('m = ')) | ||
n = int(input('n = ')) | ||
# 当需要计算阶乘的时候不用再写重复代码而是直接调用函数fac | ||
# 调用函数的语法是在函数名后面跟上圆括号并传入参数 | ||
print(fac(m) // fac(n) // fac(m - n)) | ||
``` | ||
|
||
### 函数的参数 | ||
|
||
#### 参数的默认值 | ||
|
||
在Python语言中,如果函数中没有`return`语句,那么函数默认返回代表空值的`None`。另外,在定义函数时,函数也可以没有自变量,但是函数名后面的圆括号是必须有的。Python中还允许函数的参数拥有默认值,例如我们把上一课中摇色子获得点数的功能定义到一个函数中,我们可以写出如下所示的代码。 | ||
|
||
```Python | ||
""" | ||
参数的默认值 | ||
Version: 0.1 | ||
Author: 骆昊 | ||
""" | ||
from random import randint | ||
|
||
|
||
# 定义摇色子的函数,n表示色子的个数,默认值为2 | ||
def roll_dice(n=2): | ||
"""摇色子返回总的点数""" | ||
total = 0 | ||
for _ in range(n): | ||
total += randint(1, 6) | ||
return total | ||
|
||
|
||
# 如果没有指定参数,那么n使用默认值2,表示摇两颗色子 | ||
print(roll_dice()) | ||
# 传入参数3,变量n被赋值为3,表示摇三颗色子获得点数 | ||
print(roll_dice(3)) | ||
``` | ||
|
||
我们再来看一个更为简单的例子。 | ||
|
||
```Python | ||
def add(a=0, b=0, c=0): | ||
"""三个数相加求和""" | ||
return a + b + c | ||
|
||
|
||
# 调用add函数,没有传入参数,那么a、b、c都使用默认值0 | ||
print(add()) # 0 | ||
# 调用add函数,传入一个参数,那么该参数赋值给变量a, 变量b和c使用默认值0 | ||
print(add(1)) # 1 | ||
# 调用add函数,传入两个参数,1和2分别赋值给变量a和b,变量c使用默认值0 | ||
print(add(1, 2)) # 3 | ||
# 调用add函数,传入三个参数,分别赋值给a、b、c三个变量 | ||
print(add(1, 2, 3)) # 6 | ||
# 传递参数时可以不按照设定的顺序进行传递 | ||
print(add(c=50, a=100, b=200)) | ||
``` | ||
|
||
#### 可变参数 | ||
|
||
接下来,我们还可以实现一个对任意多个数求和的`add`函数,因为Python语言中的函数支持可变参数,所谓可变参数指的是在调用函数时,可以向函数传入0个或任意多个参数。将来我们以团队协作开发的模式做商业项目时,可能需要去设计一个函数给其他人使用,但我们又不知道函数的调用者会向该函数传入多少个参数,这个时候可变参数就可以派上用场。下面的代码演示了用可变参数实现对任意多个数求和的`add`函数。 | ||
|
||
```Python | ||
""" | ||
可变参数 | ||
Version: 0.1 | ||
Author: 骆昊 | ||
""" | ||
|
||
|
||
# 在参数名前面的*表示args是一个可变参数 | ||
def add(*args): | ||
total = 0 | ||
# 可变参数可以放在for循环中取出每个参数的值 | ||
for val in args: | ||
total += val | ||
return total | ||
|
||
|
||
# 在调用add函数时可以传入0个或任意多个参数 | ||
print(add()) | ||
print(add(1)) | ||
print(add(1, 2)) | ||
print(add(1, 2, 3)) | ||
print(add(1, 3, 5, 7, 9)) | ||
``` | ||
|
||
### 用模块管理函数 | ||
|
||
不管用什么样的编程语言来写代码,给变量、函数起名字都是一个让人头疼的问题,因为我们会遇到**命名冲突**这种尴尬的情况。最简单的场景就是在同一个`.py`文件中定义了两个同名的函数,如下所示。 | ||
|
||
```Python | ||
def foo(): | ||
print('hello, world!') | ||
|
||
|
||
def foo(): | ||
print('goodbye, world!') | ||
|
||
|
||
foo() # 大家猜猜调用foo函数会输出什么 | ||
``` | ||
|
||
当然上面的这种情况我们很容易就能避免,但是如果项目是团队协作多人开发的时候,团队中可能有多个程序员都定义了名为`foo`的函数,这种情况下怎么解决命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过`import`关键字导入指定的模块再使用**完全限定名**的调用方式就可以区分到底要使用的是哪个模块中的`foo`函数,代码如下所示。 | ||
|
||
`module1.py` | ||
|
||
```Python | ||
def foo(): | ||
print('hello, world!') | ||
``` | ||
|
||
`module2.py` | ||
|
||
```Python | ||
def foo(): | ||
print('goodbye, world!') | ||
``` | ||
|
||
`test.py` | ||
|
||
```Python | ||
import module1 | ||
import module2 | ||
|
||
# 用“模块名.函数名”的方式(完全限定名)调用函数, | ||
module1.foo() # hello, world! | ||
module2.foo() # goodbye, world! | ||
``` | ||
|
||
在导入模块时,还可以使用`as`关键字对模块进行别名,这样我们可以使用更为简短的完全限定名。 | ||
|
||
`test.py` | ||
|
||
```Python | ||
import module1 as m1 | ||
import module2 as m2 | ||
|
||
m1.foo() # hello, world! | ||
m2.foo() # goodbye, world! | ||
``` | ||
|
||
上面的代码我们导入了定义函数的模块,我们也可以使用`from...import...`语法从模块中直接导入需要使用的函数,代码如下所示。 | ||
|
||
`test.py` | ||
|
||
```Python | ||
from module1 import foo | ||
|
||
foo() # hello, world! | ||
|
||
from module2 import foo | ||
|
||
foo() # goodbye, world! | ||
``` | ||
|
||
但是,如果我们如果从两个不同的模块中导入了同名的函数,后导入的函数会覆盖掉先前的导入,就像下面的代码中,调用`foo`会输出`hello, world!`,因为我们先导入了`module2`的`foo`,后导入了`module1`的`foo` 。如果两个`from...import...`反过来写,就是另外一番光景了。 | ||
|
||
`test.py` | ||
|
||
```Python | ||
from module2 import foo | ||
from module1 import foo | ||
|
||
foo() # hello, world! | ||
``` | ||
|
||
如果想在上面的代码中同时使用来自两个模块中的`foo`函数也是有办法的,大家可能已经猜到了,还是用`as`关键字对导入的函数进行别名,代码如下所示。 | ||
|
||
`test.py` | ||
|
||
```Python | ||
from module1 import foo as f1 | ||
from module2 import foo as f2 | ||
|
||
f1() # hello, world! | ||
f2() # goodbye, world! | ||
``` | ||
|
||
### 标准库中的模块和函数 | ||
|
||
Python标准库中提供了大量的模块和函数来简化我们的开发工作,我们之前用过的`random`模块就为我们提供了生成随机数和进行随机抽样的函数;而`time`模块则提供了和时间操作相关的函数。随着我们进一步的学习Python编程知识,我们还会用到更多的模块和函数。Python标准库中还有一类函数是不需要`import`就能够直接使用的,我们将其称之为内置函数,这些内置函数都是很有用也是最常用的,下面的表格列出了一部分的内置函数。 | ||
|
||
| 函数 | 说明 | | ||
| ------- | ------------------------------------------------------------ | | ||
| `abs` | 返回一个数的绝对值,例如:`abs(-1.3)`会返回`1.3`。 | | ||
| `bin` | 把一个整数转换成以`'0b'`开头的二进制字符串,例如:`bin(123)`会返回`'0b1111011'`。 | | ||
| `chr` | 将Unicode编码转换成对应的字符,例如:`chr(8364)`会返回`'€'`。 | | ||
| `hex` | 将一个整数转换成以`'0x'`开头的十六进制字符串,例如:`hex(123)`会返回`'0x7b'`。 | | ||
| `input` | 从输入中读取一行,返回读到的字符串。 | | ||
| `len` | 获取字符串、列表等的长度。 | | ||
| `max` | 返回多个参数或一个可迭代对象(后面会讲)中的最大值,例如:`max(12, 95, 37)`会返回`95`。 | | ||
| `min` | 返回多个参数或一个可迭代对象(后面会讲)中的最小值,例如:`min(12, 95, 37)`会返回`12`。 | | ||
| `oct` | 把一个整数转换成以`'0o'`开头的八进制字符串,例如:`oct(123)`会返回`'0o173'`。 | | ||
| `open` | 打开一个文件并返回文件对象(后面会讲)。 | | ||
| `ord` | 将字符转换成对应的Unicode编码,例如:`ord('€')`会返回`8364`。 | | ||
| `pow` | 求幂运算,例如:`pow(2, 3)`会返回`8`;`pow(2, 0.5)`会返回`1.4142135623730951`。 | | ||
| `print` | 打印输出。 | | ||
| `range` | 构造一个范围序列,例如:`range(100)`会产生`0`到`99`的整数序列。 | | ||
| `round` | 按照指定的精度对数值进行四舍五入,例如:`round(1.23456, 4)`会返回`1.2346`。 | | ||
| `sum` | 对一个序列中的项从左到右进行求和运算,例如:`sum(range(1, 101))`会返回`5050`。 | | ||
| `type` | 返回对象的类型,例如:`type(10)`会返回`int`;而` type('hello')`会返回`str`。 | | ||
|
||
### 简单的总结 | ||
|
||
**函数是功能相对独立且会重复使用的代码的封装**。学会使用定义和使用函数,就能够写出更为优质的代码。当然,Python语言的标准库中已经为我们提供了大量的模块和常用的函数,用好这些模块和函数就能够用更少的代码做更多的事情。 | ||
|
||
> **温馨提示**:学习中如果遇到困难,可以加**QQ交流群**询问,群号:**789050736**,当然也可以看看我们为大家录制的入门视频,视频的链接地址:<https://pan.baidu.com/s/10y7sGM016YBM7gDdauGqLw>,密码:4s6r。 |
Oops, something went wrong.