python函数基础用法
一、函数的定义,调用和返回值
1.1 语法
def 函数(参数一,参数二...): ''' 文档注释 ''' 代码逻辑一 代码逻辑二 .... return 返回值
1.2 定义函数的三种形式
说明:定义阶段,只检测语法,不执行代码。
# 定义函数方式一:无参函数 def foo(): print('from foo') #定义函数方式二:有参函数 def max(a,b): if a>b: print(a) else: print(b) #定义函数方式三:空函数 def get(): pass def put(): pass def cat(): pass
1.3 函数调用
#调用形式一:语句形式 def fun(): print('from fun') res = fun()
print(res)
#调用形式二:表达式形式 def max(x,y): return x if x>y else y #python 三元运算 salary = max(500,600) m_salary = salary * 12 print(m_salary) #上面的写法可以直接写成下面这样 print(max(500,600) * 12) # 调用形式三:将结果作为参数传递给另一个函数 # 比如比较 2,3,4 的之间的大小关系 res = max(max(2,3),4) # 此时就是将2,3比较的结果与4比较 print(res)
1.4 函数的返回值
1.4.1 return
特点:retrun 是函数结束标志,函数内可以有多个return ,但只要执行一个,函数就立即结束了,并且把return 后的值当成本次的结果返回
def foo(): print('first') return 1 print('second') retrun 2 print('third') return 3 foo()
1.4.2 返回值注意事项
#注意一:返回值没有类型限制 #注意二:返回值没有个数的限制,可以用逗号分隔开多个值 一次返回 #注意三:可以没有return,默认返回None def first_num(): return 1 def bar1(): return {'x':1} def bar2(): return 1,'jjj',{'a':'b'} def bar3(): return (1,'jjj',{'a':'b'}) def bar4(): pass print(first_num()) print(bar1()) print(bar2()) print(bar3()) print(bar4())
1.5 练习题
输入两个数字,比较两者的大小,并乘以10,输出结果,尽量使用函数
def compare(x, y): return x if x > y else y while True: num1 = int(input('first num:')) num2 = int(input('second num:')) print(compare(num1, num2) * 10)
二、函数参数
2.1 函数参数分类
#分类一:形参:指的是在定义函数是括号定义的参数,形参即变量。 def foo(x,y,z): #函数定义时,x,y,z 表示为三个形参 print(x,y,z) #分类二:实参:指的是定义函数时括号传入的值。实参即变量值。 foo(1,'a',['中国','巴基斯坦']) # 函数调用是传入实参,实参的值会传给形参,这就是一个赋值操作。
2.2 函数参数
2.2.1 位置参数
#1、函数定义阶段,按照从左往右的顺序依次定义的参数,称只为位置形参 #特性:位置形参必须被传值,多一个少一个都不行 def foo(x,y,z): print(x,y,z) foo(1,2,3) foo(5,6) # 少一个形参位置,会报错 foo(7,8,9,10) # 多一个形参位置,也会报错 #2、调用函数时,按照从左往右的顺序依次传入的值,称为位置实参 #特性,实参与形参位置一一对应 foo(2,3,4) foo(5,6,7)
2.2.2 默认参数
#默认参数:在定义阶段即已赋值的参数,被称之为 默认参数 #注意: # 1、定义阶段被赋值,以为着在调用阶段,可以不用赋值 # 2、默认参数必须写在关键字参数的后面 # 3、在定义阶段被赋值一次就固定死了,定义之后改变没有影响 m =10 def func(x,y=m): #==func(x,y=10) print(x,y) m=1111 #函数定义阶段y就已被锁定,之后如何变量都没有影响 func(2,3) # 2,3 func(5) # 5,10
def foo1(name,hobby,l=[]):
l.append(hobby)
print(name,l)
foo1('aa','read') # aa 爱好 读书 # aa ['read']
foo1('bb','play') # bb 爱好 玩 # bb ['read', 'play']
# 上面的结果明显出现了bb 的 爱好出现了 读书。明显是不可以,我们如何保证 只出现自己的爱好,看下面
def foo1(name,hobby,l=None):
if l is None:
l = []
l.append(hobby)
print(name,l)
foo1('aa','read')
foo1('bb','play')
# 这样就可以很好的解决上面的问题了,总结下来就是 默认参数最好是不可变类型
2.2.3 关键字参数
# 在默认形参可以理解为关键字参数 # 在调用函数是,按照key=value的形式定义的实参,称之为关键词实参 # 特点,可以完全打乱函数调用时实参一一对应的情况,但任然需要指名道姓的为指定参数传值。 def foo(x,y,z): print(x,y,z) foo(1,2,3) #位置实参调用方式 foo(z=3,y=4,x=9) #完全关键字实参调用方式 foo(1,z=5,y=6) # 混合使用方式 # 位置实参与关键字混合使用: # 1、位置参数一定要在关键字参数的前面 # 2、同一个形参只能赋值一次 foo(1,2,y=4) # y 形参被赋值了两次就会报错 #默认参数 与 关键字参数一起使用 def func(a,b,c,d=4): # 默认参数 print(a,b,c,d) func(1,2,c=3) # 位置参数与关键字参数 func('9',d='3',b='8',c='7') # 位置参数与关键字参数
2.2.4 可变长参数
# 可变长参数指的是在调用函数时:传入的实参个数可以不固定 # 实参无非两种形式:1,位置实参,2关键字实参 # 所以对应的 形参必须有两种对应解决接受溢出位置实参和关键字实参的解决方法 # 溢出位置实参解决方法 # 流程:*接受溢出的位置实参,存成元组形式,然后赋值给* 后面的变量(即形参) # 形参中使用* def foo(x,y,*z): print(x,y) print(z) foo(1,2,3,4,5) #实参中使用* foo(1,2,*[3,4,5]) #foo(1,2,3,4,5) foo(1,[2,3,4,5]) # [2,3,4,5] 是作为一个整体的实参,有没有用*转化成个体 foo(1,2,*'abc') #foo(1,2,'a','b,'c') # 溢出关键字实参解决方法, # ** :接收溢出关键字实参,存成字典形式,然后赋值给**后面的那个变量 # 在形参中用** def func(x,y,**z): print(x,y) print(z) func(1,2,a=3,b=4) #z = {'a':3,'b':4} func(z='a',k='k',x=1,y=2) # z ={'z':'a','k':'k'} # 在实参中使用** func(1,2,**{'a':3,'b':4}) func(1,2,**{'k':3,'z':4}) # 约定俗成的定义方式 # *arg 接受所有的溢出位置参数 ## **kwargs 接受所有的溢出关键字参数 def foo(*arg,**kwargs): print(arg) print(kwargs) foo(1,3,4,*(4,5,6),y='a',z='b',**{'f':'f','k':'k'})
2.3 练习题
1、不可知数字个数的前提下,求和
2、比较两个数大小,默认取小
# 练习一 def sum(*args): total = 0 for arg in args: total += arg return total print(sum(1,23,4,5)) print(sum(2,3,4,4)) # 练习二 def compare(x,y,z='small'): ''' 取两个数的最大或最小值 :param x: :param y: :param z: small or big :return: ''' if z == 'small': return x if x<y else y elif z == 'big': return x if x > y else y else: pass print(compare(1,3)) print(compare(1,3,'big'))
三、函数嵌套
3.1 函数嵌套调用
# 函数中调用另一个函数 def max2(x,y): return x if x >y else y def max4(x,y,m,n): res1 = max2(x,y) res2 = max2(m,n) return max2(res1,res2) print(max4(1,2,3,4))
3.2 函数定义嵌套
# 函数中 定义了另一个函数 def f1(): print('from f1') def f2(): print('from f2') def f3(): print('from f3') f3() f2() f1()
四、命名空间与作用域
4.1、命名空间 Namespaces
1、存放名字与值绑定关系的地方
2、名称空间的分类
# 内置名称空间 # 存在在python自带的名字,比如内置的函数名:len,max,sum # 创建:随着python解释器启动而创建 # 销毁:随着python 解释器的关闭而销毁 # 全局名称空间 # 存在在文件级别的名字,比如下面的x,foo,z # 创建:文件开始执行时则创建 # 销毁:文件执行完毕则销毁 x= 1 def foo(): print('from foo') if x ==1: z=3 # 局部命名空间 # 存放函数内的名字,强调函数的参数也属于局部的,比如下面的x,y,z # 创建:函数执行时才领会创建 # 销毁:函数执行完毕则立即销毁 def func(): x=1 y=2 z=3 func()
3、命名空间的加载顺序
# 内置名称空间==>全局名称空间===>局部命名空间
# 按照 python 解释器执行文件的过程即可得出上面的结论
# 1、python 解释器启动====》内置命名空间
# 2、开始执行文件=======》全局命名空间
# 3、函数执行时========》局部命名空间
4、命名空间的查找名字的顺序
# 先局部后整体 # 局部命名空间==>全局命名空间==>内置命名空间 # 重点:名字的查找关系在定义阶段就已经固定死了,与调用位置无关 len =10 def f1(): len =100 def f2(): print(len) f2() len=11111 f1()
x =10
def f1():
print(x)
def f2():
x=1111
print(x)
f1()
f2()
4.2 作用域
# 全局范围:内置名称空间中的名字,全局名称空间中的名字
#特点:全局有效,全局存活 # 局部范围:局部名称空间中的名字 #特点:局部有效,临死存活 #定义在全局作用域的名字称为全局变量 #定义在局部作用域的名字称为局部变量 x=1 y=2 z=3 # 上面的x,y,z 都是全局 def foo1(): x = 1111 # 在函数内部定义的变量,此时x是局部变量,临时存活 def foo2(): y=2222 # 这里的y也是局部变量 print(x) print(y) print(z) # 在函数定义,查找关系已经确定,先foo2内,然后foo1然后全局,在该函数调用之前,已创建了一个临时变量z=1000, # 所以打印1000 z=1000 foo2() print(y) # 在foo1 这一层函数内并没有定义临时的局部变量y,所以使用全局y foo1()
4.3 global与 nonlocal
# global 使用全局变量 # nonlocal 使用上层直至全局变量的局部变量 # globals() 查看所有的 全局变量 # locals() 查看当前层的局部变量,在全局作用域内,则查看全局变量 x = 1 def foo(): x = 2 def foo2(): def foo3(): def foo4(): nonlocal x # 使用自己上层作用域内的直至全局作用域内的局部变量 print(x) # 2 foo4() foo3() foo2() print(locals()) # 查看该层的所有的局部变量 foo() print(globals()) # 查看所有的全局变量 def f1(): global x # 使用全局的x print(x) # 1 f1()
五、函数对象
# 函数对象:函数可以当成变量处理 # 1、被赋值 def foo(): print('from foo') f =foo f() # 2、当成参数使用 def bar(func): print(func) bar(foo) # 3、当成返回值调用 def bar1(func): return func bar1(foo)() # 4、当成容器类型元素 def pull(): print('from pull') def push(): print('from push') def cat(): print('from cat') func_dict=[['pull',pull],['push',push],['cat',cat]] while True: count = 1 for value in func_dict: print(count,value[0]) count+=1 print('q','退出') chooise = input('chooise>>').strip() if chooise =='q': break elif int(chooise) <= len(func_dict): func_dict[int(chooise)-1][1]()
六、闭包函数
6.1 闭包函数: 函数嵌套 + 名称空间与作用域 + 函数对象
# 什么是闭包函数: # 1、定义在函数内的函数 # 2、该函数体代码包含对该函数外层作用域中的名字的引用, # 强调:函数外层指的不是全局作用域 # 满足上面两个条件,那么该内部函数就称之为闭包函数。 x=9999 def outter(): x= 1 def input(): print(x) return input f=outter() # f = input f() # x=1 名字的查找关系在定义阶段就已经确定了。print(x) 现在input 中寻找,然后再在outter中寻找,最后在全局寻找, # 全局查找在 调用之前寻找 def foo(): x=222222 print('from foo') f() foo() # x=1
6.2 函数体传值的两种方式
# 函数体传值的两种方式 # # 1、参数的形式为函数体传值 # 2、以变量的形式传值 def echo(x): print(x) echo('jmz') y = 990 def get_y(): print(y) get_y()
6.3 闭包:两种传值的结合+函数嵌套+命名空间作用域+函数对象
def echo(): print('from echo') def outter(x): def echo(): print(x) return echo echo = outter(333) # 将outter 内置函数echo 赋值给 echo,此时的echo 就是 就是内置函数echo,非全局函数echo echo()
6.4 闭包函数运用
def outter(type): def get(num1,num2): if type=='+': print(num1+num2) elif type == '*': print(num1*num2) else: print('不合法') return get jiafa = outter('+') jiafa(1,2) jiafa(3,4) chengfa=outter('*') chengfa(2,3) chengfa(4,5) # 如果后续还需要使用加法运算 你任然可以使用 jiafa 如果想要使用乘法 你也可以使用chengfa # 这样区分不会乱,又可随时调用