今日内容:
1、 函数参数的使用
2、 函数对象
3、 函数嵌套
4、 名称空间与作用域
5、 闭包函数
6、 装饰器
课程复习:
1、 什么是函数
函数就是具备某一功能工具
2、 为何用函数
1、 增强程序的组织结构,可读性
2、 减少代码冗余
3、 可扩展性
3、 如何用函数
函数分为两大类:
内置函数
自定义函数
有参
无参
空函数
定义语法:
def 函数名(参数1,参数2…):
‘’’
文档注释
‘’’
code1
code2
return 返回值
调用函数的语法:
函数名(1,2,3)
函数的使用必须遵循原则:
先定义,后使用
在定义阶段:只检测语法,不执行函数
在调用阶段:通过函数名找到函数,执行函数体代码
函数名:是用来访问函数的内存地址,拿到函数的内存地址加括号就可以触发函数体代码执行
函数参数:外部调用者为函数体传值的媒介
函数体代码:是函数功能的具体实现
return 函数的返回值
函数的返回值是函数体执行的成果
1、 返回值没有类型限制
2、 返回值没有个数限制
没有return,默认返回值None
return值1:返回值1
return值1,值2,值3:返回(值1,值2,值3)
return注意点:
return是函数结束运行的标志,函数体内可以有多个return
但是只执行一次,整个函数终止运行
一、 函数的参数
1、 函数的参数分为两大类:形参与实参
形参:指的是定义函数时,括号内指定的参数,本质是变量名
实参:指的是在调用函数时,括号内传入的值,本质是值
只有在调用函数时才会在函数体内发生实参(值)与形参(变量名)绑定关系
改绑定关系只在调用函数时临时生效,在调用函数结束后就解除关系
2、 位置参数:
位置形参:在定义函数时,按照从左到右的顺序定义的形参称为位置形参
注意:
1、按照位置定义的形参,在调用函数时必须为其传值,多一个少一个都不行
位置实参:在调用函数时,按照从左到右的顺序依次传入的值
注意:
1、 在传值时按照顺序与形参一一对应
关键字实参:
在调用函数时,按照key=value的形式的实参,称之为关键字实参
注意:
1、 在传值时可以完全打乱顺序,但仍然能给目标的参数传值
2、 可以在调用函数时,混合时位置实参和关键字实参,但位置实参必须在关键字实参的左边,并且不能为一个形参重复传值
# def register(name,sex,age):
# print(name)
# print(sex)
# print(age)
#
# register('frank',age=18,sex='male')
执行结果:
3、默认参数:
在定义函数时,就已经为某些参数绑定值,称之为默认参数
注意:
1、 在定义阶段就已经有值,在调用阶段可以不用为其传值
# def foo(x,y,z=3):
# print(x)
# print(y)
# print(z)
# foo(1,2)
执行结果:
2、 默认形参必须放到位置形参后面
#def register(name,age,sex='male'):
# print(name)
# print(sex)
# print(age)
#
# register('frank',age=18)
# register('cxx',age=28,sex='female')
执行结果:
3、 默认形参的值只在定义阶段生效一次,在函数定以后发生的改动无效
# m=100
# def foo(x,y,z=m):
# print('x:%s' %x)
# print('y:%s' %y)
# print('z:%s' %z)
# m=200
# foo(1,2)
执行结果:
4、 默认形参的值通常是不可变的类型
位置形参vs默认形参
默认形参:大多数情况下值都一样
位置形参:大多数情况下值都不一样
5、 可变长度的参数:
可变长度指的是在调用函数时,函数的参数个数可以不固定
然而实参需要给形参传值,针对两种形式的实参个数不固定,对应的形参也需要有两种解决方案*,**来分别来处理溢出的位置实参和溢出的关键字实参
1、*会将溢出的位置实参组成元组,然后赋值给紧跟其后的变量名
1.1 形参中带*
# def foo(x,y,*args): #args=(3,4,5,6)
# print(x)
# print(y)
# print(args)
# foo(1,2,3,4,5,6)
执行结果:
1.2 形参中带*,实参中带*,窍门:实参中带*,先将其理解为单个位置实参,然后考虑传值
# def foo(x,y,*args): #args=(3,4,5,6)
# print(x)
# print(y)
# print(args)
# foo(1,2,[3,4,5,6])
# foo(1,2,*[3,4,5,6])
执行结果:
# foo(1,*[3,4,5,6])
执行结果:
# def foo(x,y,z):
# print(x,y,z)
# l=['frank','sunny','franksunny']
# foo(*l) #foo('frank','sunny','franksunny')
执行结果:
**会将溢出的位置实参组成字典,然后赋值给紧跟其后的变量名
# def foo(x,y,a,**kwargs): #args={'a': 1, 'b': 2, 'c': 3}
# print(x)
# print(y)
# print(a)
# print(kwargs)
# foo(1,2,a=4,b=5,c=6)
执行结果:
#1.2形参中带**,实参中带**,窍门:实参中带**,先将其理解为单个关键字实参,然后考虑传值
# def foo(x,y,**kwargs):
# print(x,y,kwargs)
# foo(1,2,**{'a':1,'b':2,'c':3}) #foo(1,2,a=1,b=2,c=3)
# foo(1,**{'y':3,'b':4,'c':5}) #foo(1,y=1,b=2,c=3)
执行结果:
#如果我们的需求是
#需要将外层的函数参数格式原封不动转嫁给内部调用函数,需要使用到下面形式
# def index(name,age,sex):
# print('name:%s age:%s sex:%s' %(name,age,sex))
#
# def wapper(*args,**kwargs): #注释args=('frank') kwargs={'sex':'male','age':18}
# index(*args,**kwargs)
# # 注释:
# # index(*('frank'),**{'sex':'male','age':18})
# # index('frank',sex='male',age=18)
# wapper('frank',sex='male',age=18)
执行结果:
我们虽然调用warpper函数,但我们遵循的其实是index函数的参数规则
# def foo(x,y=100,*args,m,**kwargs):
# print(x,y,args,m,kwargs)
# foo(1,2,3,4,5,6,m=200,a=11,b=22)
执行结果:
常用使用方式:
# def foo(x,y):
# pass
#
# def foo(x,y=1):
# pass
#
# def warpper(*args,**kwargs):
# index(*args,**kwargs)
#
# def foo(x,*args):
# pass
#
# def foo(x,**kwargs):
# pass
用函数求和需求:
# def my_sum(*args):
# res=0
# for item in args:
# res+=item
# return res
# print(my_sum(1,2,3))
二、 函数对象
#函数是第一类对象,意味函数可以当做数据去使用
#1、可以被引用
#2、可以当作参数传给另外一个函数
#def foo():
# print('from foo')
#
# print(foo)
# func=foo
# print(func)
# func()
#
# def bar(x):
# print(x)
# x()
# bar(foo)
#3、可以当作函数的返回值
# def foo():
# print('from foo')
#
# def bar():
# return foo
# f=bar()
# print(f is foo)
# f()
#4、可以当作容器元素
#def f1():
# print('from to f1')
#
# def f2():
# print('from to f2')
#
# l=[f1,f2]
# print(l)
# l[0]()
def pay():
# print('pay function')
#
# def withdraw():
# print('withdraw function')
#
# def auth():
# print('auth function')
#
# def shopping():
# print('shopping function')
#
# def transfer():
# print('transfer function')
#
# func_dic={
# '1':pay,
# '2':withdraw,
# '3':auth,
# '4':shopping,
# '5':transfer
# }
#
# while True:
# print('''
# ‘0’:‘退出’
# ‘1’:‘支付’
# ‘2’:‘取款’
# ‘3’:‘认证’
# ‘4’:‘购物’
# ‘5’:‘转账’
# ''')
# chocie=input('请输入您的指令:').strip()
# if chocie=='0':break
# if chocie not in func_dic:continue
# func_dic[chocie]()
三、 函数嵌套
#函数的嵌套调用:在调用一个函数时,其内部代码又调用其他函数
# def bar():
# print('from to bar')
#
# def foo():
# print('from to foo')
# bar()
# foo()
# def max2(x,y):
# if x>y:
# return x
# else:
# return y
# def max4(a,b,c,d):
# res=max2(a,b)
# res1=max2(res,c)
# res2=max2(res1,d)
# return res2
#
# print(max4(1,2,3,4))
#函数的嵌套定义:在一个函数内部又定义另外一个函数
#def f1():
# x=1
# def f2():
# print('from to f2')
# f2()
# f1()
# print(f1)
四、名称空间与作用域
1、 什么是名称空间
名称空间是存放名字与值绑定关系的地方
要取到值必须通过名字才能找,而名字在名字空间存放着,所以我们在取值时首先去名称空间找名字,找到名字自然拿到值得内存地址
2、 名称空间分为三种:
a) 、内置名称空间:存放的python解释器自带的名字
在解释器启动时产生,在解释器关闭是回收
b) 、全局名称空间:除了内置的与局部的之外的名字都属于全局名称空间
生命周期:在程序文件执行时就立刻产生,在程序执行完毕后立即回收
其中:x,y,foo,z都是全局名称空间中的名字
# x=1
# y=2
# def foo():
# x=1
# y=2
# foo()
#
# if y>x:
# z=3
c) 、局部名称空间:存放的是函数内部定义的名字
生命周期:在调用函数时临时生效,在函数结束后立刻回收
加载顺序:内置名称空间---》全局名称空间---》局部名称空间
加载名称空间的目的是为了将名字与值的绑定关系存放起来,
存的目的是为了取,也是说,当我查找名字时,必然在三者之一找到
# len=100
# def foo():
# len=10
# print(len)
# foo()
查找顺序:局部名称空间—》全局名称空间---》内置名称空间
基于当前所在的位置往后查找
强调:函数的形参名属于局部名称空间
# x=100
# y=200
# def foo(x,y):
# print(x,y)
# foo(1,2)
执行结果:
# X=222
def f1():
#x=1
def f2():
#x=2
print('from f2',x)
f2()
x=111
f1()
d)、作用域
域指的是范围,作用域指的是作用范围
分为:
全局作用域范围:包含内置名称空间与全局名称空间中的名字
特点:全局有效,全局存活
局部作用域范围:包含的局部名称空间的名字
特点:局部有效,临时存活
#x=1
# def f1():
# def f2():
# def f3():
# # x=3
# print(x)
# f3()
# f2()
# f1()
# 了解:globals与locals
# x=111
# def foo():
# y=2
# print(globals()) #返回的是全局名称空间的名字
# print(dir(globals()['__builtins__']))#返回的是全局作用域的名字
# print(locals() is globals())
#如何打破函数层级带来的访问的限制,让我在任意位置都可以访问到一个内部函数
#基于函数对象的概念将一个函数内部函数返回到全局使用,从而打破层级限制
#函数作用域的关系是在函数定义阶段已经固定了,与函数调用位置无关
#即在调用函数时一定要到定义函数的位置寻找作用域关系
# x=11
# def outter():
# # x=333
# def inner():
# print('from inner',x)
# return inner
#
# f=outter() #f=指向outter.<locals>.inner
# print(f)
#
# def foo():
# x=222
# f()
# x=444
# foo()
# x=[]
# def func():
# x.append(1)
# func()
# print(x)
# globals:在局部申明名字是全局的
# x=1
# def func():
# global x
# x=2
# func()
# print(x)
执行结果:
# x=222
# def f1():
# x=111
# def f2():
# global x
# x=0
# f2()
# print('------>',x)
# f1()
# print(x)
# nonlocal :申明变量是来自与当前层的外层(必须是函数内)
# x=222
# def f1():
# x=111
# def f2():
# nonlocal x
# x=0
# f2()
# print('------>',x)
# f1()
# print(x)
执行结果:
#函数的作用域是在函数在定义阶段就固定了,与调用位置无关
五、 闭包函数
1、 闭指定义在函数的内部的函数
2、 包指该内部函数包含对外层函数作用域名字的引用
3、 闭包函数通常需要结合函数对象的概念,将闭包函数返回到外部使用
# def outter():
# x=100
# def inner():
# print(x)
# return inner
# f=outter()
# f()
# def foo():
# def bar():
# x=300
# f()
# bar()
# foo()
执行结果:
闭包函数的基本形式:
# def outter(x):
# def inner():
# print(x)
# return inner
爬虫使用的模块:pip3 install requests
# 通过参数的形式为函数传值
# import requests
# def get(url):
# response=requests.get(url)
# print(len(response.text))
# get('https://www.baidu.com/')
#将包传给函数
# def outter(url):
# #url='https://www.baidu.com/'
# def get():
# response = requests.get(url)
# print(len(response.text))
# return get
#
# bd=outter('https://www.baidu.com/')
# bd()
执行结果:
六、 装饰器
1、 什么是装饰器
装饰是指被装饰的对象增加新的功能
器是指工具
装饰器本身可以任意调用的对象,被装饰的对象也是任意可调用对象
目标:写一个函数为另外一个函数添加新的功能,需要遵循开放封闭原则(对修改是封闭的,对扩展是开放的)
1、 不修改被装饰对象的源代码
2、 不修改被装饰对象的调用方式
import time
# def index():
# time.sleep(3)
# print('welcome to index page')
# # index()
# def outter(func): #func=最原始的index
# def wrapper():
# start=time.time()
# func() #最原始的index
# stop=time.time()
# print('run time is %s' %(stop - start))
# return wrapper
# index=outter(index) #index=wrapper
# index() #wrapper
#import time
# def index():
# time.sleep(0.5)
# print('welcom to index page')
# return 1234
#
# def home(name):
# time.sleep(1)
# print('welcome %s to home page ' %name)
#
# def timmer(func):
# def wrapper(*args,**kwargs):
# start=time.time()
# res=func(*args,**kwargs)
# stop=time.time()
# print('run time is %s' %(stop -start))
# return res
# return wrapper
# index=timmer(index)
# home=timmer(home)
# res=index() #wrapper()
# print(res)
# home('frank') #wrapper('frank')
装饰器的语法糖:在被装饰对象的正上方单独一行写上@装饰器的名字
#import time
# def timmer(func):
# def wrapper(*args,**kwargs):
# start=time.time()
# res=func(*args,**kwargs)
# stop=time.time()
# print('run time is %s' %(stop -start))
# return res
# return wrapper
# @timmer #index=timmer(index)
# def index():
# time.sleep(0.5)
# print('welcom to index page')
# return 1234
# @timmer # home=timmer(home)
# def home(name):
# time.sleep(1)
# print('welcome %s to home page ' %name)
#
# # index=timmer(index)
# # home=timmer(home)
# res=index() #wrapper()
# print(res)
# home('frank') #wrapper('frank')