函数
函数也被称为方法,函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
定义一个函数
1)无参函数
1 def say_hello(): 2 """输出hello,并返回""" 3 print('hello') 4 return 'hello'
-
函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
-
任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
-
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
-
函数内容以冒号起始,并且缩进。
-
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
2)有参函数
1 def max(x,y): 2 if x > y: 3 print(x) 4 else: 5 print(y) 6 7 max(2, 3) # 3 这里是函数调用
3)空函数
def func(x, y): pass # pass 代表一个占位符,啥事也不干,就是让程序完整,运行不报错的,后续要添加功能时删除
函数的返回值
1)获取函数的返回值
def max(x, y): if x > y: return x else: return y res = max(2, 3) # 可以定义一个变量去接收函数的返回值 print(res)
2)利用函数返回值,反复利用。类型与while循环的方式
def max(x, y): if x > y: return x else: return y def max3(x, y, z): res1 = max(x,y) # 函数里调用别的函数,并接收返回值 res2 = max(res1, z) return res2 # 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) return b print(func(3)) # [3] print(func(4, [1,])) # [1, 4] print(func(3)) # [3, 3]
最后一个不是也应该输出[3]的吗?原因解释如下:
Python函数在定义的时候,默认参数b
的值就被计算出来了,即[]
,因为默认参数b
也是一个变量,它指向对象[]
,每次调用该函数,如果改变了b
的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]
了。
所以定义默认参数要牢记一点:默认参数必须指向不变对象!上述列子可以这样更改:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def func(a, b = None): 2 if not b: 3 b = [] 4 b.append(a) 5 return b 6 7 print(func(3)) # [3] 8 print(func(4, [1,])) # [1, 4] 9 print(func(3)) # [3]
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 def fo(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} <class 'dict'>
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 <class 'int'> 8 # (888, 677) <class 'tuple'> 9 # {'k4': 888} <class 'dict'>
5.元组的动态参数的标准定义,*args
1 def foo(*args): 2 print(args,type(args)) 3 li = [11,22,33] 4 foo(li) #返回结果 ([11, 22, 33],) <class 'tuple'>, 把li当一个整体传入 5 foo(*li) #返回结果 (11, 22, 33) <class 'tuple'>, 把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、求连续数字的积
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def sum(*args): 2 res = 0 3 for i in args: 4 res += i 5 return res 6 7 print(sum(1, 2, 3)) 8 print(sum(1, 2, 3, 5))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def fat(n): 2 res = 1 3 for i in range(1, n+1): 4 res = res*i 5 return res 6 print(fat(5))
7.形参,实参,带* 的综合利用。重点
1 def index(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 名称空间Namespaces
存放名字与值绑定关系的地方
2 名称空间的分类
内置名称空间:
存放python解释器自带名字,比如内置的函数名:len,max,sum
创建:随着python解释器启动而创建
销毁:随着python解释器关闭而销毁
全局名称空间:
存放文件级别的名字,比如x,f1,z
创建:文件(模块)开始执行时则立即创建
销毁:文件(模块)开始执行完毕时则销毁,通常保留到python解释器退出
局部名称空间:
存放函数内的名字,强调函数的参数也属于局部的
创建:函数执行时才临时创建
销毁:函数执行完毕则立即销毁
3 名称空间的加载顺序
内置名称空间---》全局名称空间-----》局部名称空间
加载的目的是为了吧名字存起来,然而存起的目的就是为取
那么但凡查找一个名字一定会从三种名称空间之一找到
4、名称空间的查找名字顺序
局部名称空间====》全局名称空间====》内置名称空间
名字的查找关系是在函数定义阶段就已经固定死的,与调用位置无关
5、作用域:域=范围
全局范围:内置名称空间中的名字,全局名称空间中的名字
特点:全局有效,全局存活
局部范围:局部名称空间中的名字
特点:局部有效,临时存活
定义在全局作用域的名字称为全局变量
定义在局部作用域的名字称为局部变量
命名空间示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def f1(): 2 print(len) 3 f1() # <built-in function len> 这里是从内置函数中找到的 4 5 len = 99 6 def f2(): 7 print(len) 8 9 def f3(): 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
作用域示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 x = 100 2 def ff(): 3 print(x) 4 5 def hh(): 6 x = 11111 7 ff() 8 ff() # 100 9 x = 1000 10 hh() # 1000
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 x = 100 2 def ff(x): 3 print(x) 4 5 def hh(x): 6 x = 11111 7 ff(x) 8 ff(x) # 100 9 x = 1000 10 hh(x) # 11111 x当做参数传进去的 调用hh时会优先把自己作用域的x传给ff
查看全局作用域的名字:
1 x = 1111111 2 print(globals()) # 查看当前全局变量 返回字典格式
globals和nonlocal
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 # global 在函数里把一个变量声明为全局的 2 x=99 3 def foo(): 4 global x # 强行修改全局变量 5 x = 100 6 7 foo() 8 print(x) # 100 9 10 # nonlocal 11 def f1(): 12 # x=10 13 def f2(): 14 def f3(): 15 nonlocal x # nonlocal会从当前外一层开始查找一直找到最外层的函数,如果还没有则报错 16 x = 11 17 f3() 18 f2() 19 # print(x) 20 21 f1()
函数对象的使用,函数可以当做变量去使用1)函数可以被赋值
1 def foo(): 2 print('from foo') 3 4 f = foo 5 # print(f) 6 f() # ===>>> foo()
2)可以当做参数传给一个函数
1 def foo(): 2 print('from foo') 3 4 def bar(func): #func = foo 5 # print(func) 6 func() # func() == foo() 7 8 bar(foo)
3)可以当做函数的返回值
1 def foo(): 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 def foo(): 2 print('from foo') 3 4 l = [foo] 5 print(l) 6 l[0]() 7 d = {'foo': foo} 8 9 d['foo']() # 理解:就是一切皆对象,详情参考面向对象
示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def get(): 2 print('from get') 3 4 def put(): 5 print('from put') 6 7 def ls(): 8 print('from ls') 9 10 def login(): 11 print('from login') 12 13 def cd(): 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 def run(): 26 while True: 27 for k in func_dic: 28 print(k, func_dic[k][1]) 29 choice = input('>>>: ').strip() 30 if choice == 'q': 31 break 32 if choice in func_dic: 33 func_dic[choice][0]() 34 35 run()
闭包函数
当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,那这个内部的函数称为闭包函数
闭包函数就是:函数嵌套+名称空间与作用域+函数对象 什么是闭包函数 1、定义在函数内部的函数 2、该函数体代码包含对该函数外层作用域中变量的引用, 强调:函数外层指的不是全局作用域 满足上述两个条件,那么该内部函数就称之为闭包函数
示例:
1 def outter(): 2 x = 1 3 def inner(): 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()
应用示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import requests 2 def get(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 def outter(url): 12 # url='https://www.baidu.com' 13 def get(): 14 response=requests.get(url) 15 if response.status_code == 200: 16 print(response.text) 17 return get 18 baidu = outter('https://www.baidu.com') 19 python = outter('https://www.python.org') 20 baidu() 21 python()
装饰器
装饰器本质上还是一个函数,目的是为了装饰其他函数(为其他函数添加功能),故而称作装饰器。
装饰器原则:(1)不能修改被装饰函数的源代码(2)不能修改被装饰函数的调用方式
就是有没有装饰器原函数都能顺利执行
示例:为函数增加功能,计算函数运行时间
1)初步实现装饰器功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import time 2 def index(): 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))
2)普通函数版修改
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import time 2 def index(): 3 time.sleep(1) 4 print('welome to index') 5 6 # 传参,传要修饰的函数 7 def wrapper(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)
3)闭包函数修改版
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import time 2 def index(): 3 time.sleep(1) 4 print('welome to index') 5 return 123 6 7 def outter(func): 8 def wrapper(): 9 start_time = time.time() 10 res = func() 11 stop_time = time.time() 12 print('run time is %s'%(stop_time - start_time)) 13 return res 14 return wrapper 15 16 index = outter(index) 17 res = index() # index() ===>>> wrapper() 18 print(res)
4)修正被装饰函数的传参问题,并加上装饰器特殊语法 @
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import time 2 def time_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 return res 9 return inner 10 11 @time_count 12 def home(name): 13 time.sleep(1) 14 print('welome %s to home' %name) 15 return 123 16 17 home('user')
示例:用户认证
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 status = {'is_login': False} 2 def auth(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'] = True 10 return func(*args, **kwargs) 11 else: 12 return '用户名或密码错误' 13 return inner 14 @auth 15 def index(): 16 print('welcome to index') 17 @auth 18 def home(name): 19 print('welcome %s to home page' % name) 20 21 index() 22 home('user')
示例:用户认证,可选择认证方式(带参数的装饰器)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import time 2 current_user = {'login':False} 3 4 def auth(engine): 5 def outter(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'] = True 16 return func(*args,**kwargs) 17 elif engine == 'mysql': 18 print('基于mysql数据库的认证') 19 elif engine == 'ldap': 20 print('基于ldap的认证方式') 21 return inner 22 return outter 23 24 @auth('mysql') 25 def index(): 26 time.sleep(1) 27 print('welcome to index') 28 return 1234 29 30 @auth('ldap') 31 def home(name): 32 time.sleep(2) 33 print('welcome %s to home page' %name) 34 35 index() 36 home('user')
递归函数
1、定义:
函数的内部调用自己,这个函数就称为递归函数
2、递归应该分为两个明确的阶段:回溯,递推
回溯,一层一层地调用下去
回溯阶段一定要有一个明确的结束条件,并且每一次回溯问题的规模都应该减少(否则就变成了单纯的重复,没有任何意义)
递推,结束当前层的调用,进而往前一层一层地结束
强调:递归的本质就是一个重复的过程,但是每进入下一次递归调用,问题的规模都应该有所减少
3、递归vs循环
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归是一个重复的过程,比起来循环的实现方式,递归的优点是只需要把控住回溯结束的条件或者是进入下一次递归调用的条件,具体循环多少次则无需考虑。
4、示例
1)直接调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 import sys 2 # print(sys.getrecursionlimit()) 3 sys.setrecursionlimit(500) # 限制最大递归次数 4 5 # 直接调用 6 def f1(n): 7 print('from f1',n) 8 f1(n+1) 9 10 f1(1)
2)间接调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def f1(): 2 print('from f1') 3 f2() 4 5 def f2(): 6 print('from f2') 7 f1() 8 9 f1()
3)递归调用简单算法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def f1(n): 2 if n == 5: 3 return 4 print('from f1',n) 5 f1(n+1) 6 f1(1) 7 8 def age(n): 9 if n == 1: 10 return 18 11 return age(n-1)+10 12 13 print(age(4))
4)循环打印列表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 l = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10, [11, [12,]]]]]]]]]]]] 2 print(isinstance(l, list)) 3 def tell(l): 4 for i in l: 5 # if isinstance(i, list): 6 if type(i) is list: 7 tell(i) 8 else: 9 print(i) 10 11 tell(l)
5)递归实现二分查找
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 l = [1, 2, 5, 9, 11, 23, 43, 57, 63, 83, 97, 101, 118, 211, 399] 2 3 def search(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< l[mid]: 10 high = mid - 1 11 else: 12 return mid 13 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('找到!')
6)递归实现斐波那契数列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def fib(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个数的值