python学习(二):函数基础
python学习(二):函数基础
本篇博客主要介绍了
- 函数的基本介绍和简单例子
- 使用模块对函数进行管理
- 部分标准库中的函数和模块
- 函数作用域问题
内容整理和修改自骆昊老师的github_python100天教学。
一、函数简介和简单例子
数学上的函数通常形如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
表示其返回值,函数的目的往往是为了减少重复的代码,增加代码的复用性,使代码可以被高效地使用。
1 |
|
以上就是一个函数定义和使用的基本例子,计算的是一个整数的阶乘
二、用模块管理函数
使用模块(module)管理函数主要是为了提高团队协作开发时的效率,减少冲突,同时也可以高效地使用别人提供的模块。
在函数命名时如果在单一文件中命名了两个同名函数,则后定义的函数会把之前的函数覆盖,而如果没有模块管理,引用别人的文件时产生函数同名的问题将会很难解决,因此使用模块之后就有特定的用法来完成模块的引用和函数的调用。
2.1 引用模块
在使用函数的时候我们通过import
关键字导入指定的模块再使用完全限定名的调用方式就可以区分到底要使用的是哪个模块中的foo
函数,代码如下所示。
module1.py
1 |
|
module2.py
1 |
|
test.py
1 |
|
在导入模块时,还可以使用as
关键字对模块进行别名,这样我们可以使用更为简短的完全限定名。
test.py
1 |
|
2.2 导入函数
上面的代码我们导入了定义函数的模块,我们也可以使用from...import...
语法从模块中直接导入需要使用的函数,代码如下所示。
test.py
1 |
|
如上的写法中就可以直接使用该函数,但是直接使用函数也就会出现函数覆盖的问题,所以在实际编写的时候需要留意。
2.3 main函数使用
需要说明的是,如果我们导入的模块除了定义函数之外还有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此,因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"__main__"。
module3.py
1 |
|
test.py
1 |
|
三、标准库中的模块和函数
Python标准库中提供了大量的模块和函数来简化我们的开发工作,我们之前用过的random
模块就为我们提供了生成随机数和进行随机抽样的函数;而time
模块则提供了和时间操作相关的函数;上面求阶乘的函数在Python标准库中的math
模块中已经有了,实际开发中并不需要我们自己编写,而math
模块中还包括了计算正弦、余弦、指数、对数等一系列的数学函数。随着我们进一步的学习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中有关变量作用域的问题。
1 |
|
上面的代码能够顺利的执行并且打印出100、hello和True,但我们注意到了,在bar
函数的内部并没有定义a
和b
两个变量,那么a
和b
是从哪里来的。我们在上面代码的if
分支中定义了一个变量a
,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。在上面的foo
函数中我们定义了变量b
,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在foo
函数的外部并不能访问到它;但对于foo
函数内部的bar
函数来说,变量b
属于嵌套作用域,在bar
函数中我们是可以访问到它的。bar
函数中的变量c
属于局部作用域,在bar
函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索,前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些标识符,我们之前用过的input
、print
、int
等都属于内置作用域。
再看看下面这段代码,我们希望通过函数调用修改全局变量a
的值,但实际上下面的代码是做不到的。
1 |
|
在调用foo
函数后,我们发现a
的值仍然是100,这是因为当我们在函数foo
中写a = 200
的时候,是重新定义了一个名字为a
的局部变量,它跟全局作用域的a
并不是同一个变量,因为局部作用域中有了自己的变量a
,因此foo
函数不再搜索全局作用域中的a
。如果我们希望在foo
函数中修改全局作用域中的a
,代码如下所示。
1 |
|
我们可以使用global
关键字来指示foo
函数中的变量a
来自于全局作用域,如果全局作用域中没有a
,那么下面一行的代码就会定义变量a
并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用nonlocal
关键字来指示变量来自于嵌套作用域,请大家自行试验。
在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被垃圾回收。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对迪米特法则的践行。
说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!