一、认识函数
1 什么是函数
函数就是具备某一功能的工具
2 为什么要用函数
1 程序的组织结构不清晰,可读性差
2 代码冗余
3 可扩展性差
3 如何用函数
函数的使用必须遵循的原则:先定义,后调用
修理工事先准备工具的过程即函数的定义
修理工遇到应用场景拿来工具就用即函数的调用
语法:
def 函数名(参数1,参数2,...):
"""
文档注释
"""
code1
code2
code3
......
return 返回值
def:定义函数的关键字
函数名: 就相当于一个变量名,指向函数的内存地址,
注意:函数的内存地址()就可以出发函数体代码的执行
参数: 参数是函数的调用者为函数体代码传值的媒介,在python中函数的参数无需声明类型
"""文档注释""" : 推荐写上
代码块:就是函数体功能的具体实现
return 返回值 :函数体代码块运行的成果
函数的使用分为两个阶段:
定义阶段: 只检测语法,不执行代码
调用阶段:运行函数体代码
def foo():
xxx
print(
# 先定义
def foo(): print("from foo")
# 后调用
# 定义时无参,意味着调用时也无需传入参数
print(foo) foo()
# 先定义
def bar(x,y): print(x) print(y)
# 后调用
# 定义时无参,意味着调用时也必须传入参数
bar('a',2)
# 定义无参函数:当函数体的代码逻辑不依赖任何传入的值就能执行,就不需要定义参数
def print_msg(): print('='*50) print('welecome.....'.center(50,' ')) print('='*50) print_msg() print_msg() ----------------------------------------------------------------- ================================================== welecome..... ================================================== ================================================== welecome..... ==================================================
#定义有参函数:当函数体的代码逻辑依赖于外部调用者传入的值才能执行,必须定义参数用来接收外部传入的值
def max2(x,y): # x=1 # y=3 if x > y: print(x) else: print(y) max2(1,4) --------------------------------- 4
# 定义阶段
def foo(): print('from foo') bar() def bar(): print('from bar')
# 调用阶段
foo()
# 定义阶段
def foo(): print('from foo') bar()
# 调用阶段
foo()
def bar(): print('from bar')
二、定义函数的三种形式
1 无参函数
def foo(): print('from foo') foo()
2 有参函数
def bar(x,y): print(x,y) bar(1,2)
3 空函数
def func(): pass
三、调用函数的三种形式
1 #语句形式
def foo(): print('from foo') foo()
2 表达式形式
def foo(x,y): res = x + y return res res=foo(1,2) #表达式形式 res1=foo(1,2)*100 print(res1)
3 可以当作参数传给另外一个函数
def max2(x,y): if x > y: return x else: return y res=max2(max2(1,2),3) print(res)
四、函数的返回值
#一 函数的返回值需要注意:
#1 返回值没有类型限制
#2 返回值没有个数限制
# 返回1个值:调用函数拿到的结果就是一个值
# 返回多个值:调用函数拿到的结果就是一个元组
# 返回0个值,或者不写return:调用函数拿到的结果就是None
#二 return关键字:return是函数结束的标志,函数内可以有多个return,但只要执行一次,整个函数就结束
def f1(): print('first') return 1 print('second') return 2 print('third') return 3 res=f1() print(res) ---------------------------------------- first 1 def foo(): count=1 while True: print(count) if count == 3: return count+=1 foo() ------------------------------------------------------ 1 2 3 def bar(): pass def foo(): return [1,2],1,1.3,{'x':1},bar res=foo() print(res) ---------------------------------------------------------------- ([1, 2], 1, 1.3, {'x': 1}, <function bar at 0x000002478D872EA0>) def func(): print('from foo') # return res=func() print(res) ---------------------------------------------------------------------------- from foo None
五、函数参数的使用
#函数的参数分为两大类:
#1 形参: 指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名"
#2 实参: 指的是在调用函数阶段括号内传入的值,即实参本质就是"值"
# 形参与实参的关系:在调用函数时,会将实参(值)赋值(绑定)给形参(变量名),
# 这种绑定关系在函数调用时临时生效,在调用结束后就失效了
def foo(x,y): # x=1 y=2 # x=1 # y=2 print(x,y) foo(1,2)
# 形参与实参的具体分类
# 一 位置参数
# 1.1 位置形参: 在定义函数阶段按照从左到右的顺序依次定义的形参,称之为位置形参
# 注意:但凡按照位置定义的形参,必须被传值,多一个不行,少一个也不行(一一对应)
def foo(x,y): print(x,y) foo(1,2)
# 1.2 位置实参: 在调用函数阶段按照从左到右的顺序依次传入的值,称之为位置实参
# 注意:但凡按照位置定义的实参,会与形参一一对应
二 关键字参数
#关键字实参: 在调用函数阶段,按照key=value的形式指名道姓地为形参传值
#注意:
#1. 可以完全打乱顺序,但仍然能指名道姓为指定的形参传值
#2. 可以混合使用位置实参与关键字实参,但是必须注意:
# 2.1 位置实参必须放到关键字实参前面
# 2.2 不能对一个形参重复赋值
def foo(name,age): print(name,age) foo('egon',18) foo(18,'egon') foo(age=18,name='egon') foo('egon',age=18)
# 三 默认参数
#默认参数:指的是在定义函数阶段,就已经为某个形参赋值了,改形参称之为有默认值的形参,简称默认形参
#注意:
#1. 在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
#2. 位置形参应该放到默认形参前面
#3. 默认参数的值在函数定义阶段就已经固定死了
#4. 默认参数的值通常应该是不可变类型
def foo(x,y=2): print(x,y) foo(1) foo(1,3) foo(y=3,x=1) ---------------------------- 1 2 1 3 1 3
-
m=10 ## 必须要放在上面才可以被赋值到 def foo(x,y=m): print(x,y) m=20 #定义时不能定义在函数后面,否则会报 m 没有定义 foo(1) ------------------------- 1 10
def register(name,hobby,l=[]): l.append(hobby) print('%s 的爱好为 %s' %(name,l)) register('yxx','不洗澡') register('egon','read') ----------------------------- yxx 的爱好为 ['不洗澡'] egon 的爱好为 ['不洗澡', 'read'] def register(name, hobby, l=None): if l is None: l=[] l.append(hobby) print('%s 的爱好为 %s' % (name, l)) register('yxx', '不洗澡',[]) register('egon', 'read',[]) -------------------------------------------------- yxx 的爱好为 ['不洗澡'] egon 的爱好为 ['read']
# 位置形参vs默认形参
# 对于大多情况下传的值都不相同的,应该定义成位置形参
# 对于大多情况下传的值都相同的,应该定义成默认形参
def register(name,age,sex='男'): print(name,age,sex) register('李铁蛋',18,) register('刘二丫',19,'女')
# 四 可变长度的参数
# 站在实参的角度,参数长度可变指的是在调用函数时,传入的实参值的个数不固定
#而实参的定义方式无法两种:位置实参,关键字实参,对应着形参也必须有两种解决方案*与**,类分别应对溢出的位置实参与关键字实参
#1. 在形参中带*:会将调用函数时溢出位置实参保存成元组的形式,然后赋值*后的变量名
def foo(x,y,*z): #z=(3,4,5,6) print(x,y,z) foo(1,2,3,4,5,6)
# 2. 在实参中带*: 但凡在实参中带*星的,在传值前都先将其打散成位置实参,再进行赋值
def foo(x,y,*z): #z=(3,4,5,6) print(x,y,z) foo(1,*[2,3,4,5,6]) # foo(1,2,3,4,5,6) # def foo(x,y,z): print(x,y,z) foo(*(1,2,3)) #foo(1,2,3) foo(*'abc') #foo('a','b','c')
# 3. 在形参中带**:会将调用函数时溢出关键字实参保存成字典的形式,然后赋值**后的变量名
def foo(x,y,**z): #z={'z':3,'a':1,'b':2} print(x,y,z) foo(1,y=2,a=1,b=2,c=3) foo(1,2,**{"r":4,'g':9}) # 正确的 ------------------------------------------ 1 2 {'a': 1, 'b': 2, 'c': 3} 1 2 {'r': 4, 'g': 9} # 错误方式: # foo(1,2,{"r":4,'g':9}) # 错误的 #不能直接接收以字典的形式,可以是以关键字传入例如:(a=1,b=2),或是将字典的形式打散 例如: **{"a":1,'b':2}
# 4. 在实参中带**: 但凡在实参中带**星的,在传值前都先将其打散成关键字实参,再进行赋值
def foo(x,y,**z): #z={'a':100,'b':200} print(x,y,z) foo(1,**{'a':100,'b':200,'y':111}) #foo(1,b=200,a=100,y=111) --------------------------- 1 111 {'a': 100, 'b': 200} def foo(x,y,z): print(x,y,z) foo(**{'y':111,'x':222,'z':333}) #foo(z=333,x=222,y=111) ------------------------------------ 222 111 333
#5. 规范: 在形参中带*与**的,*后的变量名应该为args,**后跟的变量名应该时kwargs
def foo(*args,**kwargs): #args=(1,2,3,4,5) kwargs={'a':1,'b':2,'c':3} print(args) print(kwargs) foo(1,2,3,4,5,a=1,b=2,c=3) ------------------------------------ (1, 2, 3, 4, 5) {'a': 1, 'b': 2, 'c': 3}
注意:
!!!!!!!!!!!当我们想要将传给一个函数的参数格式原方不动地转嫁给其内部的一个函数,应该使用下面这种形式
def bar(x,y,z): print(x,y,z) def wrapper(*args,**kwargs): #args=(1,2) kwargs={'z':3} bar(*args,**kwargs) #bar(*(1,2),**{'z':3}) #bar(1,2,z=3) wrapper(1,2,z=3) # 虽然调用的是wrapper,但是要遵循的确是bar的参数标准 --------------------------------- 1 2 3
五 命名关键字参数: 放到*与**之间的参数称之为命名关键字参数
# 注意: 命名关键字参数必须按照key=value的形式传值
def foo(x,y,*args,m,n,**kwargs): #args=(3,4,5,6,7,8) print(x,y) # 1,2 print(args) #(3,4,5,6,7,8) print(m,n) #222,333 print(kwargs) foo(1,2,3,4,5,6,7,8,n=333,m=222,a=1,b=2) -------------------------------- 1 2 (3, 4, 5, 6, 7, 8) 222 333 {'a': 1, 'b': 2} def foo(*,x=1,y): print(x) print(y) foo(y=2222,x=1111) foo(y=2222) ----------------------------------- 1111 2222 1 2222 def foo(x,y=1,*args,m,**kwargs): print(x) print(y) print(args) print(m) print(kwargs) foo('from x',ars='tuple',m='from m',kw='from kw',ar='args') ----------------------------------------- from x 1 () from m {'ars': 'tuple', 'kw': 'from kw', 'ar': 'args'}
作业:
默写: 函数中参数的分类 及其作用 位置 关键字 形参 实参 可变长度参数 分类:形参,实参 作用: 位置:位置形参,位置实参 关键字:关键字实参: 在调用函数阶段,按照key=value的形式指名道姓地为形参传值 形参:指的是在定义函数阶段括号内指定变量名,即形参本质就是"变量名" 实参:指的是在调用函数阶段括号内传入的值,即实参本质就是"值" 可变长度参数: 站在实参的角度,参数长度可变指的是在调用函数时,传入的实参值的个数不固定,而实参的定义方式无法两种:位置实参,关键字实参,对应着形参也必须有两种解决方案*与**,类分别应对溢出的位置实参与关键字实参 练习题: 1.使用函数新的知识点继续完善,优化购物车 2.使用函数完成以下功能,数据格式如下 [ {“name”:”张无忌”,”number”,”sh01”,”math”:90,”english”:87,”chinese”:56}, {“name”:”武则天”,”number”,”sh02”,”math”:40,”english”:97,”chinese”:67}.... ] 提供以下功能函数 获取指定学生的成绩 获取指定学号的成绩 根据学生的学号修改姓名 根据姓名修改指定学科的成绩 删除指定学生及其成绩 3.博客作业 http://www.cnblogs.com/linhaifeng/articles/7531972.html#_label5 拓展: ATM 参考博客
六、函数对像
1、函数是第一类对象: 指的是函数名指向的值(函数)可以被当作数据去使用
def func(): # func=函数的内地址 print('from func') print(func) # 打印出 func=函数的内地址 # 1. 可以被引用 age=10 x=age print(x,age) f=func print(f) f() # 2. 可以当作参数传给另外一个函数 def bar(x): print(x) bar(age) bar(func) # 3. 可以当作一个函数的返回值 def bar(x): return x res=bar(age) print(res) res=bar(func) print(res) # 4. 可以当作容器类型的元素 l=[age,func,func()] print(l)
-------------------------------------------------------------------------------------------------------------------------------------------
def login(): print('login') def register(): print('register') def shopping(): print('shopping') def pay(): print('pay') def transfer(): print('transfer') func_dic={ '1':login, '2':register, '3':shopping, '4':pay, '5':transfer } print(func_dic) msg=""" 0 退出 1 登陆 2 注册 3 购物 4 支付 5 转账 """ while True: print(msg) choice=input('请输入您的操作: ').strip() if choice == '0':break if choice == '1': login() elif choice == '2': register() elif choice == '3': shopping() elif choice == '4': pay() else: print('输入错误指令,请重新输入') if choice in func_dic: func_dic[choice]() else: print('输入错误指令,请重新输入') # 拆分: # print(func_dic["1"]()) # 或者 # f=func_dic["1"] # f()
七、函数嵌套
# 函数嵌套分为两大类
# 一: 函数的嵌套调用:在调用一个函数过程中,其内部代码又调用了其他函数
def bar(): print('from bar') def foo(): print('from foo') bar() foo() -------------------------------------------------------------------- def max2(x,y): if x > y: return x elif x<y: return y else: return y == x # print(max2(3,3)) def max4(a,b,c,d): res1=max2(a,b) res2=max2(res1,c) res3=max2(res2,d) return res3 print(max4(1,2,3,4)
#二:函数的嵌套定义:一个函数内部又定义了另外一个函数
def outter(): x=1 print('from outter') def inner(): print('from inner') print(x) print(inner) inner() outter() ------------------------------------------------------------ from outter 1 <function outter.<locals>.inner at 0x000001E5234C5510> from inner
def f1(): print('from f1') def f2(): print('from f2') def f3(): print('from f3') f3() f2() f1() ------------------------------------------- from f1 from f2 from f3
from math import pi print(pi) def circle(radius,action=0): """ 圆形相关运算 :param radius: 半径 :param action: 0代表求面积,1代表求周长 :return: 面积或者周长 """ def area(radius): return pi * (radius ** 2) def perimiter(radius): return 2 * pi * radius if action == 0: res=area(radius) elif action == 1: res=perimiter(radius) return res print(circle(10,0)) print(circle(10,1)) ----------------------------------------------- 3.141592653589793 314.1592653589793 62.83185307179586
八、名称空间与作用域
1 什么是名称空间namespace
名称空间就是用来存放名字与值内存地址绑定关系的地方(内存空间)
age=18
但凡查找值一定要通过名字,访问名字必需去查找名称空间
2 名称空间分为三大类
内置名称空间:存放的是python解释器自带的名字
例如:
len
max
print
全局名称空间:存放的是文件级别的名字
例如:以下名字中x\y\z\b\foo都会存放于全局名称空间中
x=1
y=2
if x == 1:
z=3
while True:
b=4
break
def foo():
m=3
局部名称空间:在函数内定义的名字
例如:
foo内的m这个名字一定是存放于一个局部名称空间中
生命周期:
内置名称空间:在解释器启动时则生效,解释器关闭则失效
全局名称空间:在解释器解释执行python文件时则生效,文件执行完毕后则失效
局部名称空间:只在调用函数时临时产生该函数的局部名称空间,改函数调用完毕则失效
加载顺序:
内置->全局->局部
调用顺序:(从当前往外找,以下顺序不变)
局部-> 全局-> 内置
查找名字的顺序:
基于当前所在位置往上查找
假设当前站在局部,查找顺序:局部->全局->内置
假设当前站在全局,查找顺序:全局->内置
# 案列一:
len=111 def foo(): len=222 print(len) foo() print('站在全局找len: ',len) -------------------------------------- 222 站在全局找len: 111
x=111 def f1(): x=222 def f2(): def f3(): x=444 print(x) x=333 f3() f2() f1() -------------------------------- 444
# (******)名字的查找顺序,在函数定义阶段就已经固定死了(即在检测语法时就已经确定了名字的查找顺序),与函数的调用位置无关,也就是说无论在任何地方调用函数,都必须回到当初定义函数的位置去确定名字的查找关系#
# 案列二:
x=111 def outer(): def inner(): print('from inner',x) # x访问的是全局名称空间中x return inner f=outer() print(f) x=222 # 因为代码是从上往下读,而在最后面的x=222时是因为先读到x=111,后面再读到x=222 所以最终取得的结果是 print('from inner',x) >>> from inner 222 f() --------------------------------------------------- <function outer.<locals>.inner at 0x00000204E1D45510> from inner 222
# 案列三:
x=111 def outer(): def inner(): print('from inner',x) # x访问的时全局名称空间中x return inner f=outer() # x=222 def func(): x=333 #与函数的调用位置无关 f() x=444 func() ---------------------------------------------- from inner 444
# 案列四:
# 都不对 x=111 def outer(): def inner(): x=11111 # 先定义,后调用 print('from inner',x) # x是来自于当前层的名字 ,能找到,但是先现定义,后调用 x=2222222222 # 语法错误, 而这里是x 没有定义,后面再赋值,所以是语法错误 return inner f=outer() f()
# 作用域:
#域指的是范围,作用域指的就是作用的范围,分为两种
# 全局作用域:包含的是内置名称空间与全局名称空间中的名字
# 特点:全局有效,全局存活
# 局部作用域:包含的是局部名称空间中的名字
# 特点:局部有效,临时存活
x=1 def foo(): y=2 print(x) def bar(): print(x) foo() bar() ----------------------------- 1 1
# global,nonlocal
x=1 def foo(): x=2 foo() print(x) -------------------- 1
l=[] def foo(): l.append(1) # l=33333 foo() print(l) ------------------------------- Unbound Local Error #(错误的云地方)
#global:在局部声明一个名字是来自于全局作用域的,可以用来在局部修改全局的不可变类型
x=1 def foo(): global x x=2 foo() print(x) --------------------------- 2
#nonlocal:声明一个名字是来自于当前层外一层作用域的,可以用来在局部修改外层函数的不可变类型
x=0 def f1(): x=111 def f2(): # global x nonlocal x x=222 f2() print(x) f1() print(x) ------------------------------ 222 0
九、闭包函数
1 什么是闭包函数
闭:指的是闭包函数是定义在一个函数内部的函数
包:该内部函数包含对外层函数作用域名字的引用
需要结合函数对象的概念将闭包函数返回到全局作用域去使用,从而打破函数的层级限制
def outter():
x=111
def inner():
print(x)
return inner
f=outter() #f=outter内的inner
# x=22222
# f()
def func():
x=3333
f()
func()
2 为何要用闭包函数
闭包函数提供了一种为函数体传值的解决方案
3 如何用闭包函数
# 为函数体传值的方式一:参数
def func(x,y): print(x+y) func(1,2) ------------------- 3
# 为函数体传值的方式二:闭包
def outter(x,y): # x=1 # y=2 def func(): print(x+y) return func f=outter(1,2) f() ------------------ 3
# 解决方案一:参数
# pip3 install requests
import requests def get(url): response=requests.get(url) if response.status_code == 200: print(len(response.text)) get('https://www.baidu.com') get('https://www.baidu.com') get('https://www.baidu.com') get('https://www.tmall.com') get('https://www.tmall.com') url1='https://www.baidu.com' url2='https://www.tmall.com' get(url1) get(url1) get(url1) get(url2) get(url2) get(url2)
# 解决方案二:闭包
import requests def outter(url): # url='https://www.baidu.com' def get(): response=requests.get(url) if response.status_code == 200: print(len(response.text)) return get baidu=outter('https://www.baidu.com') baidu() baidu() baidu() tmall=outter('https://www.tmall.com') tmall() tmall() tmall()
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。