函数
函数也被称为方法,函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
定义一个函数
1)无参函数
1 defsay_hello():2 """输出hello,并返回"""
3 print('hello')4 return 'hello'
函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
函数内容以冒号起始,并且缩进。
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
2)有参函数
1 defmax(x,y):2 if x >y:3 print(x)4 else:5 print(y)6
7 max(2, 3) #3 这里是函数调用
3)空函数
deffunc(x, y):pass #pass 代表一个占位符,啥事也不干,就是让程序完整,运行不报错的,后续要添加功能时删除
函数的返回值
1)获取函数的返回值
defmax(x, y):if x >y:returnxelse:returny
res= max(2, 3) #可以定义一个变量去接收函数的返回值
print(res)
2)利用函数返回值,反复利用。类型与while循环的方式
defmax(x, y):if x >y:returnxelse:returnydefmax3(x, y, z):
res1= max(x,y) #函数里调用别的函数,并接收返回值
res2 =max(res1, z)returnres2
# max3 ====》》》max(max(x,y),z)print(max3(4, 2, 9))
3)函数返回值补充
1、返回值没有类型限制2、返回值没有个数限制,可以用逗号分开多个值,以元组的形式一起返回3、可以不写return ,默认返回None
函数的参数的传递,调用
1)形参,实参,默认参数,位置参数
def foo(x,y,z = 3): #这里的x,y,z是形参,其中z是默认参数,默认就被赋值3
print(x,y,z)
foo(1,2) #函数调用传的1,2是实参,分别对应x,y,1,2也是必须参数,调用时必须要有,默认参数可以没有
foo(1,2,5) #不传就使用默认的3,传的话就使用传入的值5
foo(1,y= 8,z=10) #这里的y=8,z=10是关键字参数,就是把形参跟实参对应上
foo(1,z = 3,y= 8) #关键字参数位置随意,但是必须在实参1的后面#foo(1,3,y= 8) # 这样也不行,因为3的值已经给y了,后面又加了y=8,重复赋值,报错#注意事项#1、默认参数必须放在形参参数后面#2、形参赋值一定要放在关键字参数前面,比如 1,z = 3,y= 8#3、同一个形参,只能被赋值一次
一个关于默认参数的坑
def func(a, b =[]):
b.append(a)returnbprint(func(3)) #[3]
print(func(4, [1,])) #[1, 4]
print(func(3)) #[3, 3]
最后一个不是也应该输出[3]的吗?原因解释如下:
Python函数在定义的时候,默认参数b的值就被计算出来了,即[],因为默认参数b也是一个变量,它指向对象[],每次调用该函数,如果改变了b的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
所以定义默认参数要牢记一点:默认参数必须指向不变对象!上述列子可以这样更改:
1 def func(a, b =None):2 if notb:3 b =[]4 b.append(a)5 returnb6
7 print(func(3)) #[3]
8 print(func(4, [1,])) #[1, 4]
9 print(func(3)) #[3]
View Code
2)动态参数
1.动态参数一个*,转换成元组
def foo(x,y,*z):print(x,y)print(z)
foo(1,2,3,4,5,6)#1 2#(3, 4, 5, 6)
2.实参中*的用法
1 #实参使用* 相当于打打散了
2 def foo(x, y, *z):3 print(x, y)4 print(z)5 foo(1, *(2, 3, 4, 5, 6)) #==>>foo(1,2,3,4,5,6) ==>>foo(1, 2, *(3,4,5,6))
6
7 deffo(x, y, z):8 print(x, y)9 print(z)10 fo(1, *(2, 3))
3.动态参数二,二个*,转为字典
1 def k1(**b):2 #a = (123, 456)
3 print(b, type(b))4 #k1(123, "aegag", 456, 34, [11,53,55]) # 会报错
5 k1(k1=123,k2=456) #===>>>k1(**{'k1': 123, 'k2': 456})
6 #返回结果 {'k1': 123, 'k2': 456}
4.动态参数组合使用
1 def m1(p,*a,**aa):2 print(p,type(p))3 print(a,type(a))4 print(aa,type(aa))5 m1(445,888,677,k4=888)6 #返回结果
7 #445
8 #(888, 677)
9 #{'k4': 888}
5.元组的动态参数的标准定义,*args
1 def foo(*args):2 print(args,type(args))3 li = [11,22,33]4 foo(li) #返回结果 ([11, 22, 33],) , 把li当一个整体传入
5 foo(*li) #返回结果 (11, 22, 33) , 把li里的元素分别传入
6.字典的动态参数的标准定义,**kwargs
#形参使用**,实参也可以使用**
def foo(x,y,**kwargs):print(x)print(y)print(kwargs)
foo(1,a=2,y=3,f=6)
foo(1,**{'a':2,'y':'3','f':6})
foo(**{'a':2,'y':'3','f':6,'x': 1})
示例
1、求任意的个数的数字的和
2、求连续数字的积
1 def sum(*args):2 res =03 for i inargs:4 res +=i5 returnres6
7 print(sum(1, 2, 3))8 print(sum(1, 2, 3, 5))
View Code
1 deffat(n):2 res = 1
3 for i in range(1, n+1):4 res = res*i5 returnres6 print(fat(5))
View Code
7.形参,实参,带* 的综合利用。重点
1 defindex(name, gender):2 print('welcome %s gender is %s' %(name,gender))3
4 def wrapper(*args,**kwargs):5 index(*args,**kwargs) #参数传递,wrapper(*args,**kwargs) 接收到的参数全部给 index(*args,**kwargs)
6 #参数传递过程 wrapper(*args,**kwargs) == index(*args,**kwargs) >= index(name,gender)
7 #参数传递根据最小化接收参数给参数 index(name,gender)
8 wrapper('user', 'male')
命名空间与作用域
命名空间示例:
1 deff1():2 print(len)3 f1() # 这里是从内置函数中找到的
4
5 len = 99
6 deff2():7 print(len)8
9 deff3():10 len = 1
11 print(len)12
13 len = 10
14 f1() #10 这是从全局变量里找到的 len最后的值为10
15 f2() #10 这是从全局变量里找到的 len最后的值为10
16 f3() #1 这是从局部变量里找到的 len的值为1
View Code
作用域示例:
1 x = 100
2 defff():3 print(x)4
5 defhh():6 x = 11111
7 ff()8 ff() #100
9 x = 1000
10 hh() #1000
View Code
1 x = 100
2 defff(x):3 print(x)4
5 defhh(x):6 x = 11111
7 ff(x)8 ff(x) #100
9 x = 1000
10 hh(x) #11111 x当做参数传进去的 调用hh时会优先把自己作用域的x传给ff
View Code
查看全局作用域的名字:
1 x = 1111111
2 print(globals()) #查看当前全局变量 返回字典格式
globals和nonlocal
1 #global 在函数里把一个变量声明为全局的
2 x=99
3 deffoo():4 global x #强行修改全局变量
5 x = 100
6
7 foo()8 print(x) #100
9
10 #nonlocal
11 deff1():12 #x=10
13 deff2():14 deff3():15 nonlocal x #nonlocal会从当前外一层开始查找一直找到最外层的函数,如果还没有则报错
16 x = 11
17 f3()18 f2()19 #print(x)
20
21 f1()
View Code
函数对象的使用,函数可以当做变量去使用1)函数可以被赋值
1 deffoo():2 print('from foo')3
4 f =foo5 #print(f)
6 f() #===>>> foo()
2)可以当做参数传给一个函数
1 deffoo():2 print('from foo')3
4 def bar(func): #func = foo
5 #print(func)
6 func() #func() == foo()
7
8 bar(foo)
3)可以当做函数的返回值
1 deffoo():2 print('from foo')3
4 def bar(func): #func = foo
5 return func #return foo
6
7 f =bar(foo)8 print(f) #f ===>>>foo
9 f()
4)可以当做容器类型元素
1 deffoo():2 print('from foo')3
4 l =[foo]5 print(l)6 l[0]()7 d = {'foo': foo}8
9 d['foo']() #理解:就是一切皆对象,详情参考面向对象
示例:
1 defget():2 print('from get')3
4 defput():5 print('from put')6
7 defls():8 print('from ls')9
10 deflogin():11 print('from login')12
13 defcd():14 print('from cd')15
16 func_dic ={17 "1": [get, '下载'],18 "2":[put, '上传'],19 "3": [ls, '遍历'],20 "4": [login, '登录'],21 "5": [cd, '切换目录']22
23 }24
25 defrun():26 whileTrue:27 for k infunc_dic:28 print(k, func_dic[k][1])29 choice = input('>>>:').strip()30 if choice == 'q':31 break
32 if choice infunc_dic:33 func_dic[choice][0]()34
35 run()
View Code
闭包函数
当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,那这个内部的函数称为闭包函数
闭包函数就是:函数嵌套+名称空间与作用域+函数对象
什么是闭包函数1、定义在函数内部的函数2、该函数体代码包含对该函数外层作用域中变量的引用,
强调:函数外层指的不是全局作用域
满足上述两个条件,那么该内部函数就称之为闭包函数
示例:
1 defoutter():2 x = 1
3 definner():4 print(x) #内部函数调用外部函数作用域的变量
5 return inner #返回的是内部函数
6 f = outter() #f = inner
7 f() #inner()
8
9 #def foo():
10 #print('from foo')
11 #f()
12 #foo()
应用示例:
1 importrequests2 defget(url):3 response =requests.get(url)4 if response.status_code == 200:5 print(response.text)6
7 get('https://www.baidu.com')8 get('https://www.python.org')9
10 #使用闭包函数
11 defoutter(url):12 #url='https://www.baidu.com'
13 defget():14 response=requests.get(url)15 if response.status_code == 200:16 print(response.text)17 returnget18 baidu = outter('https://www.baidu.com')19 python = outter('https://www.python.org')20 baidu()21 python()
View Code
装饰器
装饰器本质上还是一个函数,目的是为了装饰其他函数(为其他函数添加功能),故而称作装饰器。
装饰器原则:(1)不能修改被装饰函数的源代码(2)不能修改被装饰函数的调用方式
就是有没有装饰器原函数都能顺利执行
示例:为函数增加功能,计算函数运行时间
1)初步实现装饰器功能
1 importtime2 defindex():3 time.sleep(1)4 print('welome to index')5 start_time =time.time()6 index()7 stop_time =time.time()8 print('run time is %s' % (stop_time - start_time))
View Code
2)普通函数版修改
1 importtime2 defindex():3 time.sleep(1)4 print('welome to index')5
6 #传参,传要修饰的函数
7 defwrapper(func):8 start_time =time.time()9 func()10 stop_time =time.time()11 print('run time is %s'%(stop_time -start_time))12 wrapper(index)
View Code
3)闭包函数修改版
1 importtime2 defindex():3 time.sleep(1)4 print('welome to index')5 return 123
6
7 defoutter(func):8 defwrapper():9 start_time =time.time()10 res =func()11 stop_time =time.time()12 print('run time is %s'%(stop_time -start_time))13 returnres14 returnwrapper15
16 index =outter(index)17 res = index() #index() ===>>> wrapper()
18 print(res)
View Code
4)修正被装饰函数的传参问题,并加上装饰器特殊语法 @
1 importtime2 deftime_count(func):3 def inner(*args, **kwargs):4 start_time =time.time()5 res = func(*args, **kwargs)6 stop_time=time.time()7 print('run time is %s' % (stop_time -start_time))8 returnres9 returninner10
11 @time_count12 defhome(name):13 time.sleep(1)14 print('welome %s to home' %name)15 return 123
16
17 home('user')
View Code
示例:用户认证
1 status = {'is_login': False}2 defauth(func):3 def inner(*args, **kwargs):4 if status['is_login']:5 return func(*args, **kwargs)6 username = input('请输入用户名:').strip()7 password = input('请输入密码:').strip()8 if username == 'user' and password == 'pwd':9 status['is_login'] =True10 return func(*args, **kwargs)11 else:12 return '用户名或密码错误'
13 returninner14 @auth15 defindex():16 print('welcome to index')17 @auth18 defhome(name):19 print('welcome %s to home page' %name)20
21 index()22 home('user')
View Code
示例:用户认证,可选择认证方式(带参数的装饰器)
1 importtime2 current_user = {'login':False}3
4 defauth(engine):5 defoutter(func):6 def inner(*args,**kwargs):7 if current_user['login']:8 return func(*args, **kwargs)9 user = input("username>>>:").strip()10 pwd = input("password>>>:").strip()11
12 if engine == 'file':13 print('基于文件的认证')14 if user == 'user' and pwd == '123':15 current_user['login'] =True16 return func(*args,**kwargs)17 elif engine == 'mysql':18 print('基于mysql数据库的认证')19 elif engine == 'ldap':20 print('基于ldap的认证方式')21 returninner22 returnoutter23
24 @auth('mysql')25 defindex():26 time.sleep(1)27 print('welcome to index')28 return 1234
29
30 @auth('ldap')31 defhome(name):32 time.sleep(2)33 print('welcome %s to home page' %name)34
35 index()36 home('user')
View Code
递归函数
1、定义:
函数的内部调用自己,这个函数就称为递归函数
2、递归应该分为两个明确的阶段:回溯,递推
回溯,一层一层地调用下去
回溯阶段一定要有一个明确的结束条件,并且每一次回溯问题的规模都应该减少(否则就变成了单纯的重复,没有任何意义)
递推,结束当前层的调用,进而往前一层一层地结束
强调:递归的本质就是一个重复的过程,但是每进入下一次递归调用,问题的规模都应该有所减少
3、递归vs循环
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归是一个重复的过程,比起来循环的实现方式,递归的优点是只需要把控住回溯结束的条件或者是进入下一次递归调用的条件,具体循环多少次则无需考虑。
4、示例
1)直接调用
1 importsys2 #print(sys.getrecursionlimit())
3 sys.setrecursionlimit(500) #限制最大递归次数
4
5 #直接调用
6 deff1(n):7 print('from f1',n)8 f1(n+1)9
10 f1(1)
View Code
2)间接调用
1 deff1():2 print('from f1')3 f2()4
5 deff2():6 print('from f2')7 f1()8
9 f1()
View Code
3)递归调用简单算法
1 deff1(n):2 if n == 5:3 return
4 print('from f1',n)5 f1(n+1)6 f1(1)7
8 defage(n):9 if n == 1:10 return 18
11 return age(n-1)+10
12
13 print(age(4))
View Code
4)循环打印列表
1 l = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, [11, [12,]]]]]]]]]]]]2 print(isinstance(l, list))3 deftell(l):4 for i inl:5 #if isinstance(i, list):
6 if type(i) islist:7 tell(i)8 else:9 print(i)10
11 tell(l)
View Code
5)递归实现二分查找
1 l = [1, 2, 5, 9, 11, 23, 43, 57, 63, 83, 97, 101, 118, 211, 399]2
3 defsearch(l, num, low, high):4 if low >high:5 return -1
6 mid = (low + high)//2
7 if num >l[mid]:8 low = mid + 1
9 elif num
11 else:12 returnmid13 return search(l, num, low, high) #这里之所以会有return是因为必须要接收值,不然返回None
14 #回溯到最后一层的时候,如果没有return,那么将会返回None
15
16 while 1:17 num = int(input('输入要查找的数:'))18 res = search(l, num, 0, len(l)-1)19 print(res)20 if res == -1:21 print('未找到!')22 else:23 print('找到!')
View Code
6)递归实现斐波那契数列
1 deffib(n):2 if n == 1 or n == 2:3 return 1
4 return fib(n-2) + fib(n-1)5
6 print(fib(10)) #打印第10个数的值
View Code