Python小白到老司机,快跟我上车!基础篇(十三)
Posted coder-pig
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python小白到老司机,快跟我上车!基础篇(十三)相关的知识,希望对你有一定的参考价值。
函数(上)
1、函数定义
我们可以将一些实现特定功能,重复使用到的「代码片段」抽取出来,封装成一个函数。比如求两个数和的函数:
def plus(a, b):
"""
计算两个数的和
:param a: 第一个参数
:param b: 第二个参数
:return: 两个参数的和
"""
return a + b
if __name__ == '__main__':
print("1 + 2 = %d" % plus(1, 2))
运行结果如下:
1 + 2 = 3
从上面的求和函数可以初窥函数定义的一些端倪,接着具体说下规则:
- 函数定义格式:
def 函数名(传入参数)
- 使用
return
返回值,不写的话默认返回None
值。 - Python函数的
返回值可以有多个
,本质上利用的元组。 - Python传递的
参数有多个
的话,可以用**逗号隔开
**。 - 一个建议:函数定义时,可在函数的第一行语句中选择性地使用文档字符串编写函数说明,除了方便阅读,使用**
help(函数名)
**也可以拿到这个函数的说明信息。
2、形参与实参
定义函数时
,函数名后传入的参数叫「形参」,调用函数时
,函数名后传入的参数叫「实参」。这里还涉及到一个「传值与传址」的问题,传值就是传入一个参数的值,而传址则是传入一个参数的内存地址。两者的区别是:如果函数是传值类型的,在函数里修改了参数的值,外部的变量(实参)是不会改变的,比如下面这样一段代码:
def f(a):
a = 1
b = 0
f(b)
print(b)
运行结果如下:
0
尽管我们修改了传入参数的值,但是实参却依旧是0,没有改变,这就是传参,如果传递的是内存地址,修改的了话实参也会受影响。但是Python和其他编程语言有点不同(比如C语言里可以用&参数名传地址)。
Python不允许开发者选择采用传值还是传址,而是采用「传对象引用」的方式,如果传入的参数是一个不可变对象(数字,字符串和元组)的引用,就不能修改原始对象的值;如果传入的参数是一个可变对象(列表,字典)的引用,就能直接修改原始对象的值。
比如下面这样一串代码:
def f(a):
b[0] = 1
b = [0]
f(b)
print(b)
运行结果如下:
[1]
3、关键字参数与默认参数
「关键字参数
」:当函数需要传入的参数有多个的时候,怕参数混淆传错,可以在传入的时候指定形参的参数名,比如:plus(a = 1, b = 2)。
「默认参数
」:在 定义形参的时候赋予初始值,调用的时候就可以不带参数去调用函数,比如:
def plus(a=1, b = 2),调用的时候直接plus()或者只传入一个参数plus(3)都是可以的,还可以配合关键字参数指定传入的是哪个参数。另外,默认参数也称作「缺省参数」。
4、可变参数
有时传入函数中的 参数数目
可能是 不固定
的,比如,要你计算一堆数字的和,而具体有多少
个数字不知道,这个时候就可以使用可变参数了。只需要在函数定义时在参数前加上***
** 星号,
就代表这个参数是可变参数(其实是只是把数据打包成了一个元组)。
另外,如果除了可变参数外还有其他的参数,那么写在可变参数后的参数要用关键字参数指定,
否则会加入可变参数的范畴。还有一点要注意,如果传入的参数是列表或者元组,会被再次
打包成元组,如果想解包的话,需要在**实参前加*
**,代码示例如下:
def plus(*a):
result = 0
for b in a:
print(b, end='\\t')
if __name__ == '__main__':
a = [1, 2, 3, 4, 5]
plus(a)
print()
plus(*a)
运行结果如下:
[1, 2, 3, 4, 5]
1 2 3 4 5
另外,如果想把参数打包成字典的方式,可在函数形参前使用两个**
标识。
5、全局变量与局部变量
全局变量:定义在最外部,可在函数内部进行访问,但不能直接修改。
局部变量:定义在**函数内部
**,在函数外部无法访问的参数和变量。
局部变量无法在外部访问的原因:
Python在运行函数时,会利用**栈(Stack)**来存储数据,执行完函数后,所有数据会被自动删除。
函数中无法修改全局变量的原因:
试图在函数里修改全局变量的值时,Python会自动在函数内部新建一个名字一样的局部变量代替。如果硬是要修改,可以在函数内部使用**
global关键字
**修饰全局变量,但是不建议这样做,会使得程序维护成本的提高。
6、内部函数
所谓的内部函数其实就是「函数嵌套」,在一个函数中嵌套另一个函数,要注意:
内部函数的作用域,只在内部函数的「直接外部函数内」,外部是无法调用的没,外部调用内部函数会直接报:函数找不到的错误!
内部函数无法直接修改外部函数中的变量,否则会报**UnboundLocalError错误
!如果想在内部函数中直接修改,可以把直接外部函数中的变量通过容器类型来存放
**,或者使用Python提供的nonlocal关键字
修饰。代码示例如下:
def fun_x():
x = [10]
y = 10
def fun_y():
x[0] += x[0]
nonlocal y
y *= y
return x[0] * y
return fun_y()
if __name__ == '__main__':
print(fun_x())
运行结果如下:
2000
7、闭包
在函数内嵌套了另一个函数,如果「内部函数引用了外部函数的变量」,则可能产生闭包。
Python中形成闭包的三个条件:
- 函数嵌套
- 内部函数引用外部变量
- 外部函数返回内部函数
一个函数闭包的代码示例如下:
def outer(a):
b = 1
def inner():
print(a + b)
return inner
if __name__ == '__main__':
test_1 = outer(2)
test_1()
运行结果如下:
2
在上面的代码中,直接把内部函数当做返回值返回了,b是一个局部变量,按理来说,生命周期在调用完outer()函数后就完结了。但是载上面的代码中,调用test_1时,b变量的值却正常输出了,函数闭包使得函数的「局部变量信息」得以保存。
Python中通过 __closure__属性
保存闭包中的局部变量,把上面test_1函数里的东东
打印出来,代码如下:
print(test_1.__closure__)
print(test_1.__closure__[0].cell_contents)
print(test_1.__closure__[1].cell_contents)
运行结果如下:
(<cell at 0x000001D09ACF85E8: int object at 0x00000000667D6C30>, <cell at 0x000001D09ACF8648: int object at 0x00000000667D6C10>)
2
1
8、lambda表达式
在Python中可以使用**lambda关键字
来创建匿名函数**,直接返回一个函数对象,而不用去纠结给函数起什么名字,省去了定义函数的步骤,从而简化代码,一个对比大小简单的lambda表达式代码示例如下:
big = lambda x, y: x > y
print("第一个参数比第二个参数大:%s" % big(1, 2))
运行结果如下:
第一个参数比第二个参数大:False
9、递归
所谓的递归就是「函数调用自身」,最简单的递归求和代码示例如下:
def sum(n):
if n == 1:
return 1
else:
return n + sum(n - 1)
print("1到100的求和结果是: %d" % sum(100))
运行结果如下:
1到100的求和结果是: 5050
另外要注意两点:
- 递归要有结束条件,以避免递归的无休止调用!
- 递归可以简化程序,但不一定能提高程序的执行效率!
以上是关于Python小白到老司机,快跟我上车!基础篇(十三)的主要内容,如果未能解决你的问题,请参考以下文章