#Python函数
一、函数介绍
在我们以往的学习编程的过程当中,碰到的最多的两张编程方式或者说编程方法:面向过程和面向对象。其实不管是哪一种,其实都是编程的方法论而已。但是现在有一种更古老的编程方式:函数式编程,以它的不保存的状态,不修改变量等特性,重新进入我们的视野。
- 面向对象 --->类 ---->class
- 面向过程 --->过程--->def
- 函数式编程-->函数--->def
二、函数定义
我们上初中那会也学过函数,即:y=2x,说白了,这里面总共就两个变量x和y,x是自变量,y是因变量(因为x变而变),y是x的函数。自变量的取值范围,叫做这个函数的定义域。
说了这么多,我们还是来讲讲,编程中的函数定义吧!!!
1、函数定义:
def test():
"the funcation details"
print("in the test funcation")
return 0
def #定义函数的关键字
test #函数名
() #定义形参,我这边没有定义。如果定义的话,可以写成:def test(x): 其中x就是形式参数
"the funcation details" # 文档描述(非必要,但是强烈建议你为你的函数添加详细信息,方便别人阅读)
print #泛指代码块或者程序逻辑处理
return #定义返回值
2、过程定义:
#定义过程
def test_2():
"the funcation details"
print("in the test funcation")
注:从上面两个例子看出来,函数和过程,无非就是函数比过程多了一个return的返回值,其他的也没什么不一样。
3、两者比较:
#定义函数
def test_1():
"the funcation details"
print("in the test funcation")
return 0
#定义过程
def test_2():
"the funcation details"
print("in the test funcation")
a = test_1()
b = test_2()
print(a)
print(b)
#输出
in the test funcation
in the test funcation
#输出返回值
0
#没有return ,返回为空
None
小结:不难看出,函数和过程其实在python中没有过多的界限,当有return时,则输出返回值,当没有return,则返回None
三、使用函数原因
至此、我们已经了解了函数,但是我们为啥要用函数啊,我觉的我们以前的那种写法挺好的呀!其实不然,我给使用函数总结 了两点好处:
- 代码重复利用
- 可扩展性
- 保持一致性
1、代码重复利用
我们平时写代码的时候,最讨厌的就是写重复代码,这个无疑是增加我们的工作量,所以代码重用性是非常有必要的。下面我们就举个简单的例子吧,使用函数和不使用函数的区别。
①优化前
#假设我们编写好了一个逻辑(功能),用来打印一个字符串
print("高高最帅")
#现在下面有2个函数,每个函数处理完了,都需要使用上面的逻辑,那么唯一的方法就是拷贝2次这样的逻辑
def test_1():
"the funcation details"
print("in the test1")
print("高高最帅")
def test_2():
"the funcation details"
print("in the test2")
print("高高最帅")
那么假设有n个函数,我们是不是也要拷贝n次呐?于是,我们就诞生了下面的方法
②优化后
def test():
print("高高最帅")
def test_1():
"the funcation details"
print("in the test1")
test()
def test_2():
"the funcation details"
print("in the test2")
test()
2、可扩展,代码保持一致性
#代码逻辑变了
def test():
print("高高一直都很帅")
def test_1():
"the funcation details"
print("in the test1")
test()
def test_2():
"the funcation details"
print("in the test2")
test()
注:如果遇到代码逻辑变了,用以前拷贝n次的方法,那什么时候拷贝完啊,而且中间有遗漏怎么办,如果用了函数,我们只需要改这个函数的逻辑即可,不用改其他任何地方。
四、返回值
之前在day3-函数介绍中提到了return关键字,但是那个只是提到,并没有详细的介绍的return关键字的用法,下面我们就来详细的阐述一下。
1、return作用
return其实有两个作用:
- 需要用一个变量来接受程序结束后返回的结果
- 它是作为一个结束符,终止程序运行
def test():
print("in the test_1")
return 0
print("the end") #结果中没有打印
x = test()
print(x)
#输出
in the test_1 #第一次打印
0 #结果返回值
注:从上面的代码可以看出,return 0后面的代码就不执行了,只执行return前面的代码;变量x接受了test()函数结束后的返回结果
2、返回多个值
上面我们试了返回一个常量值,或者说一个对象值,下面我们来试试,不返回值,或者说返回多个值得情况
# __auther__ == zhangqigao
def test_1():
print("in the test_1")
def test_2():
print("in the test_2")
return 0
def test_3():
print("in the test_3")
return 1,"hello",["qigao","shuaigao"],{"name":"qigao"}
x = test_1()
y = test_2()
z =test_3()
print(x)
print(y)
print(z)
#输出
in the test_1
in the test_2
in the test_3
None #x的值
0 #y的值
(1, 'hello', ['qigao', 'shuaigao'], {'name': 'qigao'}) #z的值
从上面的例子可以看出:
- 没有定义,返回值数=0,返回:None
- 只定义了1个返回值,返回值=1 ,返回:定义的那个值,或者说定义的那个object
- 定义了2个以上,返回值 > 1,返回:1个元组(tuple)
提问:这边我们不经意的要问,为什么要有返回值?
因为我们想要想要这个函数的执行结果,这个执行结果会在后面的程序运行过程中需要用到。
五、有参数函数调用
在此之前,我们演示的函数都是没有带参数的,下面我们就来说说带参数的函数。在讲之前,我们先来说一下,什么是形参,什么是实参吧!
- 形参:指的是形式参数,是虚拟的,不占用内存空间,形参单元只有被调用的时才分配内存单元
- 实参:指的是实际参数,是一个变量,占用内存空间,数据传递单向,实参传给形参,形参不能传给实参
代码如下:
def test(x,y): #x,y是形参
print(x)
print(y)
test(1,2) #1和2是实参
#输出
1
2
1、位置参数
从上面的例子可以看出,实际参数和形式参数是一一对应的,如果调换位置,x和y被调用的时,位置也会互换,代码如下:
def test(x,y):
print(x)
print(y)
print("--------互换前-----")
test(1,2)
print("--------互换后-----")
test(2,1)
#输出
--------互换前-----
1
2
--------互换后-----
2
1
那有些同学会说,那我多一个或者少一个参数,总归没事了吧!那我看看行不行呢?
①多一个参数
def test(x,y):
print(x)
print(y)
print("--------多一个参数----")
test(1,2,3)
#输出
--------多一个参数----
Traceback (most recent call last):
File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8, in <module>
test(1,2,3)
TypeError: test() takes 2 positional arguments but 3 were given #test()函数需要传两个实参,你传了三个实参
②少一个参数
def test(x,y):
print(x)
print(y)
print("--------少一个参数----")
test(1)
#输出
--------少一个参数----
Traceback (most recent call last):
File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8, in <module>
test(1)
TypeError: test() missing 1 required positional argument: 'y' #没有给y参数传实参
事实证明:多传一个参数或者少传一个参数都是不行的。
2、关键字参数
到这边有些小伙伴不经意的要说,像这种位置参数,有点死,我不想这么干,万一我传错了咋办,对吧!OK,没有问题,下面我们就来讲讲关键字传参。
关键字传参不需要一一对应,只需要你指定你的哪个形参调用哪一个实参即可,代码如下:
def test(x,y):
print(x)
print(y)
print("--------互换前------")
test(x=1,y=2)
print("--------互换后------")
test(y=2,x=1)
#输出
--------互换前------
1
2
--------互换后------
1
2
但是,这是又有小伙伴要问了,那我可不可以位置参数和关键字参数结合起来用呐?接下来,我们就来探讨一下。
①位置参数在前,关键字参数在后
def test(x,y):
print(x)
print(y)
test(1,y=2)
#输出
1
2
我擦这样是可以的,那我试试这种情况,我把后面关键字不传给y,我传给x,代码如下:
def test(x,y):
print(x)
print(y)
test(1,x=2)
#输出
Traceback (most recent call last):
File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8, in <module>
test(1,x=2)
TypeError: test() got multiple values for argument 'x' #给x形参传的值过多
报错的意思是:给形参x传的值过多。这种报错的原因是:实参1已经传给了形参x,后面的x=2又传了一次,所以报错。
②关键字在前,位置参数在后
def test(x,y):
print(x)
print(y)
test(y=2,1)
#输出
File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8
test(y=2,1)
^
SyntaxError: positional argument follows keyword argument # 关键字参数在位置参数的前面
我去,看来这样不行。那我位置参数放前面,中间放关键字参数总行了吧!代码如下:
def test(x,y,z):
print(x)
print(y)
test(1,y=2,3)
#输出
File "D:/PycharmProjects/pyhomework/day3/函数_带参数.py", line 8
test(1,y=2,3)
^
SyntaxError: positional argument follows keyword argument
还是一样的错误,我去,那我只能把关键字参数放最后试试!
def test(x,y,z):
print(x)
print(y)
print(z)
test(1,2,z=3)
#输出
1
2
3
那我最后两个用关键字参数呐?
def test(x,y,z):
print(x)
print(y)
print(z)
test(1,z=2,y=3)
#输出
1
3
2
这样也是可以的,所以,得出一个结论:关键字参数是不能写在位置参数前面的。
总结:
- 既有关键字,又有位置参数时,是按位置参数的顺序来
- 关键字参数是不能写在位置参数的前面的
六、默认参数
默认参数指的是,我们在传参之前,先给参数制定一个默认的值。当我们调用函数时,默认参数是非必须传递的。
def test(x,y=2):
print(x)
print(y)
print("-----data1----")
test(1) #没有给默认参数传值
print("-----data2----")
test(1,3) #给默认参数传位置参数
print("-----data3----")
test(1,y=3) #给默认参数传关键字参数
#输出
-----data1----
1
2
-----data2----
1
3
-----data3----
1
3
默认参数用途:
- 安装默认软件(def test(x,soft=True))
- 传递一下默认的值(定义mysql的默认端口号:def count(host,port=3306))
七、参数组(非固定参数)
之前我们传参数,都是传一个固定参数,不能多也不能少,但是如果说我们需要非固定参数怎么办呢?好吧,于是就衍生出了,以下两种传参方式:
- 非固定位置参数传参(*args)
- 非固定关键字传参(**kwargs)
下面我们就来说说这两种方式传参:
1、非固定位置参数传参
①功能:接收N个位置参数,转换成元组的形式。
②定义,代码如下:
def test(*args): #形参必须以*开头,args参数名随便定义,但是最好按规范来,定义成args
print(args)
test(1,2,3,4,5) #输入多个位置参数
#输出
(1, 2, 3, 4, 5) #多个参数转换成元组
这边不禁的有个疑问,你这是传入的都是N个位置参数,那我要传入一整个列表咋办,我要完全的获取这个列表的值。
③传入列表
def test(*args):
print(args)
print("-------data1-----")
test() #如果什么都不传入的话,则输出空元组
print("-------data2-----")
test(*[1,2,3,4,5]) #如果在传入的列表的前面加*,输出的args = tuple([1,2,3,4,5])
print("-------data3-----")
test([1,2,3,4,5]) #如果再传入的列表前不加*,则列表被当做单个位置参数,所以输出的结果是元组中的一个元素
#输出
-------data1-----
()
-------data2-----
(1, 2, 3, 4, 5)
-------data3-----
([1, 2, 3, 4, 5],)
④位置参数和非固定位置参数
def test(x,*args):
print(x) #位置参数
print(args) #非固定参数
test(1,2,3,4,5,6)
#输出
1
(2, 3, 4, 5, 6)
从上面看出,第1个参数,被当做位置参数,剩下的被当做非固定位置参数。
⑤关键字和非固定位置参数
def test(x,*args):
print(x)
print(args)
test(x=1,2,3,4,5,6)
#输出
File "D:/PycharmProjects/pyhomework/day3/非固定参数/非关键字参数.py", line 21
test(x=1,2,3,4,5,6)
^
SyntaxError: positional argument follows keyword argument #位置参数在关键字参数后面
很显然报错了,因为x=1是关键字参数,*args是位置参数,而关键字参数不能再位置参数前面的,所以报错。
2、非固定关键字传参
①功能:把N个关键字参数,转换成字典形式
②定义,代码如下:
def test(**kwargs): #形参必须以**开头,kwargs参数名随便定义,但是最好按规范来,定义成kwargs
print(kwargs)
test(name="qigao",age=18) #传入多个关键字参数
#输出
{'name': 'qigao', 'age': 18} #多个关键字参数转换成字典
③传入字典
def test(**kwargs):
print(kwargs)
test(**{"name":"qigao","age":18}) #传入字典时,一定要在字典前面加**,否则就会报错
#输出
{'name': 'qigao', 'age': 18}
然而,有些小伙伴说,我就不信,难道不加**,就会报错,那为啥非固定位置参数不加*,为啥就不报错呐?下面我们就用事实说话,代码如下:
def test(**kwargs):
print(kwargs)
test({"name":"qigao","age":18})
#输出
Traceback (most recent call last):
File "D:/PycharmProjects/pyhomework/day3/非固定参数/非固定关键字参数.py", line 9, in <module>
test({"name":"qigao","age":18})
TypeError: test() takes 0 positional arguments but 1 was given #报类型错误,传入的是位置参数
因为传入的字典被当做位置参数,所以被报类型错误,所以小伙伴们千万要记住:传字典,加**
④配合位置参数使用
def test(name,**kwargs):
print(name)
print(kwargs)
print("------data1-----")
test("qigao") #1个位置参数
print("------data2------")
test("qigao",age=18,sex="M") #1个位置参数,两个关键字参数
print("------data3------")
test(name="qigao",age=18,sex="M") #3个关键字参数
#输出
------data1-----
qigao #输出1个位置参数
{} #没有输入关键字参数,所以输出空字典
------data2------
qigao #第1个位置参数
{'age': 18, 'sex': 'M'} #剩下关键字参数,转换成1个字典
------data3------
qigao #第1个关键字参数
{'age': 18, 'sex': 'M'} #剩下的关键字参数,转换成1个字典
⑤位置参数、关键字和非固定关键字参数
提示:参数组一定要往最后放
def test(name,age=18,**kwargs):
print(name)
print(age)
print(kwargs)
print("----------data1--------")
test("qigao",sex='M',hobby='tesl')
print("----------data2--------")
test("qigao",34,sex='M',hobby='tesl')
print("----------data3--------")
test("qigao",sex='M',hobby='tesl',age=34) #age关键字参数放到最后,也可以的
#输出
----------data1--------
qigao
18 #不传,显示默认参数
{'sex': 'M', 'hobby': 'tesl'}
----------data2--------
qigao
34 #传位置参数
{'sex': 'M', 'hobby': 'tesl'}
----------data3--------
qigao
34 #关键字参数,放在前后并没有影响
{'sex': 'M', 'hobby': 'tesl'}
注:就是说,如果遇到一个关键字传参和非固定关键字传参,前后放的位置是不影响传参的,但是我们一般还是按顺序来。
⑥位置参数、关键字参数、非固定位置参数和非固定关键字参数
def test(name,age=18,*args,**kwargs):
print(name)
print(age)
print(args)
print(kwargs)
print("-------第1种传参--------")
test("qigao",19,1,2,3,4,sex="m",hobby="tesla")
print("-------第2种传参--------")
test("qigao",19,*[1,2,3,4],**{'sex':"m",'hobby':"tesla"})
#输出
-------第1种传参--------
qigao #传name位置参数
19 #给age传位置参数
(1, 2, 3, 4) #非固定位置参数,以转换成元组
{'sex': 'm', 'hobby': 'tesla'} # 非固定关键字参数,转换成字典
-------第2种传参---------
qigao
19
(1, 2, 3, 4) #以列表的形式传入,在列表前加*
{'sex': 'm', 'hobby': 'tesla'} #以字典的形式传入,在字典前加**
那么问题来了,上面的age传参传的是位置参数,那我能不能传关键字参数呐?现在我们就来看看,代码如下:
def test(name,age=18,*args,**kwargs):
print(name)
print(age)
print(args)
print(kwargs)
test("qigao",age=19,1,2,3,4,sex="m",hobby="tesla")
#输出
File "D:/PycharmProjects/pyhomework/day3/非固定参数/非固定关键字参数.py", line 55
test("qigao",age=19,1,2,3,4,sex="m",hobby="tesla")
^
SyntaxError: positional argument follows keyword argument #语法错误,位置参数在关键字参数前面
看来是不可以的,为什么?因为age=19是关键字参数,而后面的*args是非固定位置参数,说白了不管*args传入几个字,它的本质都是位置参数,上面我们提到关键字参数是不能再位置参数的前面,所以报错了。
看来上面的情况是不可以的,那能不能非固定关键字参数在非固定位置参数前面呢?来,我们带着疑问一起来试一下。代码如下:
def test(name,age=18,*args,**kwargs):
print(name)
print(age)
print(args)
print(kwargs)
test("qigao",19,sex="m",hobby="tesla",1,2,3,4,5)
#输出
File "D:/PycharmProjects/pyhomework/day3/非固定参数/非固定关键字参数.py", line 57
test("qigao",19,sex="m",hobby="tesla",1,2,3,4,5)
^
SyntaxError: positional argument follows keyword argument #语法错误,关键字参数在位置参数前面
我擦咧,也是不可以的,经我仔细研究发现,非固定关键字参数,本质也是关键字参数,是不能放在非固定位置参数的前面的。
总结
- 参数分为位置参数、关键字参数、默认参数、非固定位置参数和非固定关键字参数
- 位置参数之前传参,位置是不能调换的,多一个或者少一个参数都是不可以的。
- 关键字参数是不能放在位置参数前面的。
- 函数传参的位置一次是,位置参数,默认参数、非固定位置参数、非固定关键字参数(def test(name,age=18,*args,**kwargs))
- 关键字传参,可以不用考虑位置的前后问题
八、作用域、局部变量、全局变量
我们之前写代码,都需要声明变量,但是我们思考过变量的作用范围吗?今天我们就来讲讲变量的作用范围,这个作用范围又叫作用域。首先我们根据变量的作用范围把变量分为:局部变量和全局变量,即:
- 局部变量
- 全局变量
我们先做一个小实验:一个函数体内部调用另外一个函数,代码如下:
def test(name,age=18,**kwargs):
print(name)
print(age)
print(kwargs)
logger("test") #调用logger函数
def logger(sounce):
print("from %s"%sounce)
①logger函数之后执行
def test(name,age=18,**kwargs):
print(name)
print(age)
print(kwargs)
logger("test")
def logger(sounce):
print("from %s"%sounce)
test("qigao",age=23,sex="m",hobby="tesla") #在logger函数之后调用
#输出
qigao
23
{'hobby': 'tesla', 'sex': 'm'}
from test
ok,很完美,一点问题都没有,那么还有一种情况,就是在logger函数之前调用呐?
②logger函数之前执行
def test(name,age=18,**kwargs):
print(name)
print(age)
print(kwargs)
logger("test")
test("qigao",age=23,sex="m",hobby="tesla") #在logger函数之前调用
def logger(sounce):
print("from %s"%sounce)
#输出
qigao
23
{'hobby': 'tesla', 'sex': 'm'}
Traceback (most recent call last):
File "D:/PycharmProjects/pyhomework/day3/局部变量和全局变量/test.py", line 12, in <module>
test("qigao",age=23,sex="m",hobby="tesla")
File "D:/PycharmProjects/pyhomework/day3/局部变量和全局变量/test.py", line 10, in test
logger("test")
NameError: name 'logger' is not defined #命名错误:logger没有被定义
很显然是出错的,为什么呢?我不是定义了logger函数了吗?喔。。。。。。原来在logger函数之前执行,logger函数还没有被读到内存中,所以报错。
1、局部变量
局部变量:顾名思义,指在局部生效,定义在函数体内的变量只能在函数里面生效,出个这个函数体,就不能找到它,这个函数就是这个变量的作用域。
下面我们就用事实说话吧,请看如下代码:
def test(name):
print("before change:",name)
name = "qigao" #局部变量name,只能在这个函数内生效,这个函数就是这个变量的作用域
print("after change:",name)
name = "alex"
print("-----调用test-----")
test(name)
print("------打印name----")
print(name)
#输出
-----调用test-----
before change: alex
after change: qigao #局部变量生效
------打印name----
alex
2、全局变量
有了局部变量,那就肯定有全局变量,那什么是全局变量呐?全局变量又改怎么定义呢?
全局变量:指的是在整个程序中都生效的变量,在整个代码的顶层声明。
代码如下:
school = "leidu edu" #定义全局变量
def test_1():
print("school:",school)
def test_2():
school = "qigao linux "
print("school:",school)
print("------test_1----")
test_1()
print("------test_2----")
test_2()
print("----打印school--")
print("school:",school)
#输出
------test_1----
school: leidu edu #打印的是全局变量
------test_2----
school: qigao linux #打印局部变量
----打印school--
school: leidu edu #打印全局变量
从上面的例子可看出全局变量的优先级是低于局部变量的,当函数体内没有局部变量,才会去找全局变量。但是有的同学会问了,那我需要在函数体内修改全局变量咋办呢?
①函数里面修改全局变量
- 改前用global先声明一下全局变量
- 将全局变量重新赋值
school = "leidu edu" #定义全局变量
def test():
global school #用global关键字声明全局变量
school = "qigao linux " #将全局变量重新赋值
print("school:",school)
print("------test_2----")
test()
print("----打印school--")
print("school:",school)
#输出
------test_2----
school: qigao linux
----打印school--
school: qigao linux #全局变量被改掉
②只在函数里面声明全局变量
def test():
global school #只在里面定义全局变量
school = "qigao linux"
print("school:",school)
print("--------test-------")
test()
print("-----打印school----")
print("school:",school)
#输出
--------test-------
school: qigao linux
-----打印school----
school: qigao linux
也是可以的,但是我们最好不要用以上2种情况,也就是说最好不要用global这个关键字,因为你用了,其他人调你的函数时,就乱套了,而且还不好调试,说不定还冒着被开除的危险,所以请大家忘记它吧,只是知道有这么一个东西就行了。
③修改列表
names = ['alex',"qigao"] #定义一个列表
def test():
names[0] = "金角大王"
print(names)
print("--------test-----")
test()
print("------打印names--")
print(names)
#输出
--------test-----
['金角大王', 'qigao'] #函数内的names输出
------打印names--
['金角大王', 'qigao'] #函数外的names输出
从上面的例子可以看出,列表names被当做在函数中全局变量,重新赋值了,是可以被修改的。
小结:1、只有字符串和整数是不可以被修改的,如果修改,需要在函数里面声明global。2、但是复杂的数据类型,像列表(list)、字典(dict)、集合(set),包括我们后面即将要学的类(class)都是可以修改的。
总结
- 在子程序(函数)中定义的变量称为局部变量,在程序一开始定义的变量称为全局变量。
- 全局变量的作用域是整个程序,局部变量的作用域是定义该变量的子程序(函数)。
- 当全局变量和局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其他地方,全局变量起作用。
九、匿名函数、eval()函数
1.匿名函数
也叫 lambda 表达式
a.匿名函数的核心:一些简单的需要用函数去解决的问题,匿名函数的函数体只有一行
b.参数可以有多个,用逗号隔开
c.返回值和正常的函数一样可以是任意的数据类型
d.匿名函数不支持复杂的逻辑判断
e.一般跟其他函数搭配使用,如map()、filter()
#这段代码
def calc(x,y):
return x**y
print(calc(2,5))
#换成匿名函数
calc = lambda x,y:x**y
print(calc(2,5))
#输出
32
你也许会说,用上这个东西没感觉有毛方便呀, 。。。。呵呵,如果是这么用,确实没毛线改进,不过匿名函数主要是和其它函数搭配使用的呢,如下
res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
print(i)
#输出
1
25
49
16
64
2.eval()函数
在我们使用一些类似于字典的字符串时,虽然它看起来很像字典,但是在它的最外层多了引号,说明它是字符串,但是我们如何把它转换成字典呐,这就用到了eval()函数,看看eval()函数是如何把字符串转换成字典的,下面就来看看见证奇迹的时刻:
#定义一个类似于字典的字符串,把值赋给arg
>>> arg = '''{
'backend': 'www.oldboy.org',
'record':{
'server': '100.1.7.9',
'weight': 20,
'maxconn': 30
}
}'''
#这边根据键取值报错,说明它是一个字符串,不是字典
>>> arg["backend"]
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: string indices must be integers
#通过eval()函数把字符串转成字典
>>> arg = eval(arg)
#显示的类型是字典
>>> type(arg)
<class 'dict'>
>>> arg
{'record': {'server': '100.1.7.9', 'weight': 20, 'maxconn': 30}, 'backend': 'www.oldboy.org'}
#通过键能获取对应的值,说明字符串成功转成字典
>>> arg["backend"]
'www.oldboy.org'
十、递归
定义:递归调用是函数嵌套调用的一种特殊形式,如果一个函数在内部直接或间接调用自身,这个函数就是递归函数。
场景:用10不断除以2,直到除不尽为止,打印每次结果。
思路:
# 10/2=5 # 5/2=2.5 # 2.5/2=1.25 # ...
用while循环实现:
n = 10
while True:
n = int(n/2)
print(n)
if n == 0:
break
#输出
5
2
1
0
非递归方式实现:
def calc(n):
n = int(n/2)
return n
r1 = calc(10)
r2 = calc(r1)
r3 = calc(r2)
r4 = calc(r3)
print(r1)
print(r2)
print(r3)
print(r4)
#输出
5
2
1
0
用递归实现:
def calc(n):
print(n)
if int(n/2) == 0: #结束符
return n
return calc(int(n/2)) #调用函数自身
m = calc(10)
print('----->',m)
#输出
10
5
2
1
-----> 1 #最后返回的值
来看实现过程,我改了下代码
def calc(n):
v = int(n/2)
print(v)
if v > 0:
calc(v)
print(n)
calc(10)
#输出
5
2
1
0
1
2
5
10
为什么输出结果是这样?

递归层数:
import sys print(sys.getrecursionlimit()) # 默认递归限制层数 sys.setrecursionlimit(1500) # 修改递归层数
尾递归优化:
调用下一层时候退出。(解决栈溢出的问题,每次只保留当前递归的数据),在C语音里有用,Python中没用。
def recursion(n):
print(n)
return recursion(n+1)
recursion(1)
递归特性:
(1)递归就是在过程或者函数里调用自身
(2)在使用递归策略时,必须有一个明确的结束条件,称为递归出口
(3)每次进入更深一层递归时,问题规模相比上次递归都应有所减少(问题规模:比如你第1次传进的是10,第2次递归应该是9...依次越来越少,不能越来越多)。
(4)递归算法解题通常显得很简洁,但递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出),所以一般不倡导使用递归算法设计程序。
堆栈扫盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html
递归有什么用呢?下面讲几个实例
1.用递归实现1+2+3...+100
def calc(n):
if n > 0:
return n + calc(n-1)
if n == 0:
return 0
print(calc(100))
2.用递归实现阶乘(就是大于等于1的自然数相乘,1*2*3*4*5*...)
我们知道n! = n *(n-1)! 那么我们可以用递归来实现阶乘,下面计算一下5的阶乘
def factorial(n):
if n == 1:
return 1
return n * factorial(n-1)
print(factorial(5))
#输出
120
3.二分查找
#!/usr/bin/env python # -*- coding:utf-8 -*- """ @Version: v1.0 @Author: wushuaishuai @Contact: 434631143@qq.com @File: 2018-06-20.py @Time: 2018/06/20 15:06 """ """ 1.二分查找练习,所用知识点,函数、递归、高亮显示文字 (1).首先判断列表长度是否>1,>1的情况下再去设置一个变量mid为列表总长度的1/2长度,mid=int(len(dataset)/2) (2).设置完mid后,比较列表的中间的数字(dataset[mid])和要查找的数字(find_num)的关系 (3).data[mid] == find_num时,返回查找的数字 data[mid] > find_num时,返回列表在data[mid]的左边,并return binary_search(dataset[0:mid],find_num) data[mid] < find_num时,返回列表在data[mid]的右边,并return binary_search(dataset[mid+1:],find_num) (4).dataset列表经过上面的不断递归,列表越来越短,最终列表长度变成1,设置会变成空列表,此时先判断dataset是否为空 为空时返回查找的数字不在列表中,再判断dataset[0] == find_num, 当相等时,返回查找的数字,否则返回查找的数字不在列表中。 """ data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] def binary_search(dataset, find_num): """ :param dataset: 检索的数据集合,eg.列表 :param find_num: 查找的数字 eg.32 :return: 数字查找过程以及最终数字 """ if len(dataset) > 1: mid = int(len(dataset) / 2) # 注意浮点数要转为int # print("mid",mid) # print("len(dataset)",len(dataset)) # print("dataset",dataset) if dataset[mid] == find_num: print("你要查找的数字是%s" % (find_num)) elif dataset[mid] > find_num: print("\033[31;1m你要查找的数字在%s左边\033[0m" % (dataset[mid])) # 31是红色 return binary_search(dataset[0:mid], find_num) # 递归查找,顾头不顾尾,此处mid不用加1 else: print("\033[32;1m你要查找的数字在%s右边\033[0m" % (dataset[mid])) # 32是绿色 return binary_search(dataset[mid + 1:], find_num) # 顾头不顾尾,mid要加1,不包含mid本身 else: # dataset列表通过上面的不断递归,列表越来越短,最终列表长度会变成1,甚至变为空列表 if not dataset: # dataset为空 print("没的分了,你要的数字%s不在这个列表中" % (find_num)) elif dataset[0] == find_num: print("你要查找的数字是%s" % (find_num)) else: print("没的分了,你要的数字%s不在这个列表中" % (find_num)) num = int(input("请输入你要查找的数字>>>")) # input接收的都是字符串,要int一下 binary_search(data, num)
递归算法一般用于解决三类问题:
(1)数据的定义是按递归定义的。(比如Fibonacci函数)
(2)问题解法按递归算法实现。(回溯)
(3)数据的结构形式是按递归定义的。(比如树的遍历,图的搜索,二分法查找等)
十一、内置函数
Python的len为什么你可以直接用?肯定是解释器启动时就定义好了

内置参数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii
几个刁钻古怪的内置方法用法提醒
#compile
f = open("函数递归.py")
data =compile(f.read(),'','exec')
exec(data)
#print
msg = "又回到最初的起点"
f = open("tofile","w")
print(msg,"记忆中你青涩的脸",sep="|",end="",file=f)
# #slice
# a = range(20)
# pattern = slice(3,8,2)
# for i in a[pattern]: #等于a[3:8:2]
# print(i)
#
#
#memoryview
#usage:
#>>> memoryview(b'abcd')
#<memory at 0x104069648>
#在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存,
import time
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = data
while b:
b = b[1:]
print('bytes', n, time.time()-start)
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = memoryview(data)
while b:
b = b[1:]
print('memoryview', n, time.time()-start)


浙公网安备 33010602011771号