第五章 函数
函数式编程:增加代码的可读性和重用性
函数的基本格式
def func():
"""
函数是干什么的
:param name:
:param age:
:return:
"""
return None
函数可以当参数,也可以做变量和返回值
5.1自定义函数
#方式一:
def func():
return 666
#方式二:lambda表达式
func = lambda : return 666
5.2参数和返回值
5.2.1基本的参数知识
- 任意个数
- 任意类型
def func(a1,a2):
print(a1,a2)
func(1,"apple")
- 形式参数和实际参数
5.2.2位置传参
根据参数的位置依次传递参数
5.2.3 关键字传参
def func(a1,a2):
pass
func(a2=6,a1=4) #可以改变参数的位置
5.2.4位置传参和关键字传参
位置传参和关键字传参可以一起混合使用,但是需要满足位置传入的参数在前,关键字参数在后
5.2.5默认参数
采用默认参数则说明此参数可传可不传
若不传递参数,则默认此参数的值为定义函数的时候的值。(注意:也需要满足位置参数>关键字参数的要求)
注意:此处有坑
5.2.6万能参数
-
*args
-
可以接受任意个数的参数,并且将参数 转换成元组
-
有*
必须要传一个元组(列表、集合也可以,也会将其添加)
def func(*args): print(args) #其实内部是将其循环添加到元组中 func(*[11,2,3]) #(11, 2, 3) func(*{1,2,3}) #(1, 2, 3)
-
无*
-
-
只能用位置传参
-
def func(*args):
print(args)
func(1,2) #args=(1,2)
func((11,22,33)) #args=((11,22,33),)
func(*(11,22,33)) #args=(11,22,33)
*args与位置参数、关键字参数
def func(a1, *args, a2):
print(a1, args, a2)
#func(1, 2, 3, 1, 3) #报错:func() missing 1 required keyword-only argument: 'a2'
func(1,2,3,1,a2=3) #1 (2, 3, 1) 3
#虽然可以这样,但是并不推荐这样使用
-
**kwargs
-
可以接受任意个数的参数,并且将参数转换成字典
-
有*
必须要传一个字典
-
无*
-
-
只能用关键字传参
-
def func(**v1):
print(v1)
func(k1=1) #{'k1': 1}
#等式前面的作为key,后面的值作为value保存到字典中
func(**{"k1":1,"k2":"apple"}) #{'k1': 1, 'k2': 'apple'}
-
*args 和 **kwargs
def func(*args,**kwargs): print(args,kwargs) func(1,2,3,k1=4,k5=6,k6=666) #(1, 2, 3) {'k1': 4, 'k5': 6, 'k6': 666} #func(k1=1,22,33,k6=88) #报错:positional argument follows keyword argument func(*[1,2,3],**{"k1":1,"k2":"apple"}) #func(*[1,2,3],**{"k1":1,"k2":"apple"}) func(111,222,*[1,2,3],k1=666,**{"k2":"888"}) #(111, 222, 1, 2, 3) {'k1': 666, 'k2': '888'} #记住位置参数传递给*args,关键字参数传递给**kwargs,且位置参数在关键字参数之前
测试:
#看代码写结果
def func(*args,**kwargs):
print(args,kwargs)
func(12,3,*[11,22]) #有*会输出四个元素,没有*会输出三个元素
#(12, 3, 11, 22) {}
func(("alex","apple",),fruit="pear")
#(('alex', 'apple'),) {'fruit': 'pear'}
5.2.7参数的重点
def func(a1,a2):
pass
def func1(a1,a2=8):
pass
def func2(*args,**kwargs):
pass
#调用参数的时候,位置参数>关键字参数
#函数的参数传递的是什么?
"""
v=[11,22,33]
def func(arg):
print(id(arg))
print(id(v)) #打印列表的内存地址
func(v) #传送的地址和上面的地址是一样的,代表函数的参数传递的是内存地址
"""
5.2.8参数的补充
参数的补充:函数的默认值慎重使用可变数据类型
#如果想要给value设置默认是空列表
#不推荐(坑)
def func(data.value=[]):
pass
#推荐
def func(data,value=None):
if not value:
value=[]
def func(data,value=[]):
value.append(data)
return value
v1=func(1)
v2=func(1,[11,22,33])
v3=func(2)
print(v1,v2,v3) #[1, 2] [11, 22, 33, 1] [1, 2]
#可以发现v1和v3由于列表是可变数据类型,append方法添加以后才打印,所以v1=v3=[1,2]
#v2是由于其自己有列表,不用默认的列表,所以没有产生影响
#变形
def func(a,b=[]):
b.append(a)
print(b)
#添加完立即打印出结果
func(1) #[1]
func(2,[11,22,33]) #[11,22,33,2]
func(3) #[1,3]
5.3作用域
python是以函数作为一个作用域的
- 全局作用域
- 局部作用域
5.3.1作用域的规则
-
优先在自己的作用域查找,自己没有就去父级-->父级-->直到全局,全局没有就报错
-
父级拿不到子级得局部变量,子作用域中只能找到父级中的值,无法为父级的变量进行赋值
在函数里面定义的属于自己的局部的作用域,对于字符串、布尔值、字符串等不可变的数据类型,赋值在全局中不会生效,只是在该作用域中再创建一个这样的值,而像列表等数据类型,可以修改,那么对应得全局变量也会发生变化。(子作用域只能读 或修改父级的值,不能重新赋值)
NUM=[1,2,3] def func(): NUM.append(666) #修改 func() print(NUM) #[1,2,3,666]
5.3.2函数的执行与作用域
a=1
def func(): #定义函数的时候不会执行,当调用此 函数的时候才会执行
x1=666
print(x1)
a=6
print(a)
print(b)
#func() #报错:name 'b' is not defined
b=2 #在函数前定义了b,故可以在func函数中可以用b
print(a)
func() #1,666,6,2
print(a,b) #1 2
函数中调用另一个函数
def s1():
print("hello")
def s2():
print(s1)
s2() #<function s1 at 0x000001C8DDD32378>
#发现s1其实是函数 的地址,在全局中定义的s1函数,s2函数可以在全局中找到
5.3.3 global 和nonlocal
global将对全局的变量进行赋值,nonlocal对父级的变量进行赋值
一般情况下不会使用,这样会导致代码可读性差。
#global 和 nonlocal
name = "apple"
def func():
global name
name="pear"
func()
print(name) #pear
name="apple"
def func1():
name="pear"
def func2():
nonlocal name
name=666
func2()
print(name)
func1() #666
print(name) #apple
5.3.4全局变量的使用
由于之前都没有使用函数,没有全局变量的概念,并没有对全局变量提出要求
但是 现在,出于专业性的考虑,需要将全局变量全部大写,以后一旦在函数中看到大写则可以认为此为全局变量
USER_LIST=[11,2,3] #规范,全局变量大写
def func():
name="aaa"
def show():
USER_LIST.append(123)
5.4函数返回值
默认return 会停止运行程序,并返回None。若程序有return的值,则返回对应的值
#函数的返回值
def func():
pass
v=func()
print(v) #None
返回值的特殊情况
def func():
return 1,2,3
v=func()
print(v) #(1, 2, 3)
练习1:
def func(arg):
return arg.pop(1)
result=func([11,22,33,44])
print(result) #22
练习2:
def f1():
print("f1")
return f3()
def f2():
print("f2")
return f1
def f3():
print("f3")
func=f2()
result=func()
print(result) #f2,f1,f3,None
练习3:
name="大佬"
def func():
def inner():
print(name)
return inner()
v=func()
print(v) #大佬,None
5.5函数的嵌套
def func():
name="apple"
def func1(): #在小作用域创建的函数
print(name)
func1() #只能在 此作用域内使用
print(name)
func() #打印两次apple
5.6函数的进阶
5.6.1函数的赋值
(1)函数名可以当作变量使用
def func():
print(123)
V1=func
func() #123
V1() #123
def func():
print(123)
FUNC_LIST=[func,func,func]
FUNC_LIST[0]() #123
for i in FUNC_LIST:
i() #执行三次输出123
FUNC_SET={func,func,func}
for i in FUNC_SET:
i() #执行一次输出123
#集合中的元素不能重复,一般不会放函数进集合中
#同理函数也可以放入元组和字典中
def func():
print(123)
FUNC_DICT={func:"1"} #函数可以作为字典的键,但是很少这样做
for i in FUNC_DICT:
i() #123
FUNC_DICT_NEW={"k1":func,"k2":func}
FUNC_DICT_NEW["k1"]() #123
容易混淆:
def func():
print(123)
FUNC_LIST=[func,func,func] #保存的是函数的内存地址
FUNC_LIST_NEW=[func(),func(),func()] #会执行函数,打印三次123,返回值为None
print(FUNC_LIST_NEW) #[None,None,None]
(2)函数可以当作参数进行传递
def func(arg):
print(arg)
func(1)
func([1,2,3,4])
def show():
return 999
func(show)
def func(arg):
arg()
def show():
print(666)
func(show) #666
#函数执行过程:func函数以show为参数进入,遇到括号,执行show函数,打印666
def func(arg):
v1=arg()
print(v1)
def show():
print(666)
RESULT=func(show) #show为参数入func函数,执行show(),打印出666,返回值为None给v1,打印输出v1
print(RESULT) #RESULT为None
#所以结果为 666 None None
5.6.2lambda表达式
也称为匿名函数
#lambda 表达式解决简单函数的问题
def func(a1,a2):
return a1+100 #直接给返回值
func1=lambda a1,a2:a1+100 #等价于func(),隐含着一个return
lambda表达式的 不同形式
#lambda表达式的不同形式
func1=lambda :100 #没有参数,返回值为100
result1=func1()
print(result1) #100
func2=lambda *args,**kwargs:len(args)+len(kwargs)
result2=func2(1,2,3,a1=4,a2=5,a3=6)
print(result2) #6 返回传入的参数的个数
#不能在lambda表达式创建变量
DATA=100
def func3():
DATA=1000
func4=lambda a1:a1+DATA #此时的DATA在父级找,父级找不到才在全局找
v=func4(1)
print(v)
func3() #1001
#lambda表达式的应用
#比较两个数的大小,返回较大的那个值
def func1(n1,n2):
if n1>n2:
return n1
else:
return n2
func2=lambda n1,n2: n1 if n1>n2 else n2
result1=func1(5,10)
result2=func2(5,10)
print(result1,result2) #10 10
lambda表达式的一些练习
USER_LIST=[]
def func1(x):
v=USER_LIST.append(x) #append方法有返回值,v接收返回值
return v
result1 = func1("apple")
print(result1) #None 返回值为空
print(USER_LIST) #['apple']
def func2(x):
v=x.strip() #返回新的值
return v
result2=func2(" apple ")
print(result2) #apple 返回没有空白的
#小结:列表的所有方法基本上都是返回None
#字符串的方法基本上都是返回新的值
USER_LIST=[]
func3 = lambda x:USER_LIST.append(x)
result3=func3("apple")
print(result3) #None 与func1()功能类似,返回的是append方法的值
print(USER_LIST) #['apple'] 将字符串添加进列表中,与func1()结果相同
func4=lambda x:x.split("l")
result4=func4("apple")
print(result4) #['app','le'] 得到的是split()划分以后的结果(列表),并返回func4
func_list=[lambda x:x.strip(),lambda y:y+1999,lambda x,y:x+y]
result5=func_list[0](" apple ")
print(result5) #apple 返回去除空白的值
5.7内置函数
python的内置函数
Built-in Functions | ||||
---|---|---|---|---|
abs() | dict() | help() | min() | setattr() |
all() | dir() | hex() | next() | slice() |
any() | divmod() | id() | object() | sorted() |
ascii() | enumerate() | input() | oct() | staticmethod() |
bin() | eval() | int() | open() | str() |
bool() | exec() | isinstance() | ord() | sum() |
bytearray() | filter() | issubclass() | pow() | super() |
bytes() | float(0 | iter() | print() | tuple() |
callable() | format() | len() | property() | type() |
chr() | forzenset() | list() | range() | vars() |
classmethod | getattr() | locals() | repr() | zip() |
compile() | globals() | map() | reversed() | import() |
complex() | hasattr() | max() | round() | |
delattr() | hash() | memoryview() | set() |
5.7.1内置函数的分类
-
len
-
open
-
range
-
id 找内存地址
-
type
-
输入和输出: input print
-
强制转换:bool str int list tuple dict set
-
进制转换相关的
-
bin,将十进制转换成二进制
-
oct,将十进制转化为八进制
-
int,将其他进制转换成十进制
#int 作为一个中介,可以进行其他进制与其他进制之间的转换 #int的默认参数base是10 #1.二进制转换成十进制 v1="0b1101" #字符串 result=int(v1,base=2) #将二进制转换成十进制 result2=bin(result) #十进制转换成二进制 #2.八进制转换成十进制 v2="0o1101" result3=int(v2,base=8) #3.十六进制转换成十进制 v3="0x1101" result4=int(v3,base=16)
-
hex,将十进制转换成十六进制
-
-
数学相关的
-
abs() 取绝对值
v=abs(-1) print(v) #1 返回绝对值
-
float() 转换成浮点类型(小数)
v=float(55) print(v) #55.0
-
int() 转换 成整数
v=int(55.5) print(v) #55 保留整数部分
-
max() 找到最大的值
v=[1,2,3,666] result=max(v) print(result) #666 注意内置函数和列表的方法的使用不同
-
sum() 求和
v=[1,2,3,4] result=sum(v) print(result) #10
-
divmod() 返回两个值,前面返回商,后面返回余数
v=1001 result=divmod(1001,5) print(result) #(200,1) 前面返回商,后面返回余数 a,b=divmod(1001,5) print(a,b) #200 1
-
pow() 指数函数
result1=2**3 #2的3次方 reulut2=pow(2,3) #用传参的方式计算指数 print(result1,result2) #8,8
-
round() 保留几位小数,并且还能四舍五入
result=round(1.2567,2) print(result) #1.26
-
5.7.2内置函数的练习
USER_LIST=[]
for i in range(1,888):
temp={"name":"hello %s"%i,"email":"123%s.qq.com"%i}
USER_LIST.append(temp)
#请通过分页对于以上数据进行展示
#要求:每页输入10条数据,让用户输入要查看的页面:页码
#分析:对于现在的USER_LIST已经有了数据,需要判断数据的长度
#数据总长度
total_count=len(USER_LIST)
#每页显示十条数据
per_page_count=10
#总页码
max_page_num,a=divmod(total_count,per_page_count) #max_page为最大的页码数,a 为余数
num=input("请输入需要查看的页码:")
choice=int(num)
if choice<=max_page_num+1 and choice>0:
print("输入合法")
start=(choice-1)*per_page_count
end=choice*per_page_count
data=USER_LIST[start:end]
for i in data:
print(i)
else:
print("输入不合法,必须是 1 - %s"%(max_page_num))
#第一页:USER_LIST[0:10] ->0123456789
#第二页:...
5.7.3强化补充
#1字节等于八位
#ip=192.168.12.79 ->转换成四个点分的二进制
ip="192.168.12.79"
data=ip.split(".")
result_list=[]
for i in data:
result_list.append(bin(int(i)))
print(",".join(result_list)) #0b11000000,0b10101000,0b1100,0b1001111
#拼接起来转换成一个整的二进制,再转换成十进制
#注意两个地方:1.需要将二进制的位数补足八位,总共是三十二位的二进制2.要将0b去掉
fin_result=""
for i in result_list:
new_data=i.split("0b") #去掉0b
if len(new_data[1])<8: #判断如果二进制的位数小于八位,则在其前面补上足够的0
zero_num=8-len(new_data[1])
for i in range(0,zero_num):
new_data[1]="0"+new_data[1]
fin_result=fin_result+new_data[1]
print(fin_result)
print(int(fin_result,base=2))
5.7.4编码相关
-
chr (def chr(code:int) -> str) 将十进制数字转换成unicode 编码中的对应的字符串
v1=chr(65) print(v1) #A v2=chr(10000) print(v2) #✐
-
ord 将字符串转换成unicode编码的十进制数字
v="中国" for item in v: data=ord(item) print(data) #20013 22269 #再用bin就可以将其十进制数转换为二进制数
小案例:生成随机验证码
import random
def get_random_code(length=6):
data=[]
for i in range(length):
v=random.randint(65,90) #生成一个范围内的随机数
data.append(chr(v))
return "".join(data)
code =get_random_code()
print(code)
5.7.5内置高级函数
-
map,遍历序列,对序列中每个元素进行操作,最终获取新的序列
v1=[11,22,33,44] #要求:将列表中的每一个元素都进行加100 for i in range(0,len(v1)): v1[i]=v1[i]+100 print(v1) #[111, 122, 133, 144] def func(arg): return arg+100 result=map(func,v1) #将函数的返回值添加到返回值[] print(list(result)) #[211, 222, 233, 244] result1=map(lambda x:x+1000,v1) #py2会直接返回列表,但是py3为了节省内存,只返回一个特殊的值 print(list(result1)) #[1111, 1122, 1133, 1144] #发现此时用的V1是没用第一个map函数之前的结果 #这是由于map有返回值,不影响v1 #map的第一个参数:必须是一个函数 #map的第二个参数:必须是可以迭代的类型(可以被for循环)
-
filter,对序列中的元素进行筛选,最终获取符合条件的序列
v1=[11,22,33,"as",44,"ds"] def func(x): if type(x)==int: return True return False result=filter(func,v1) #生成一个列表,若函数返回True,则将其值放入到列表中,返回False则不放入 print(list(result)) #[11, 22, 33, 44] #result1=filter(lambda x:True if type(x)==int else False,v1) #print(list(result1)) #[11, 22, 33, 44] result1=filter(lambda x:type(x)==int,v1) print(list(result1)) #[11, 22, 33, 44]
-
reduce,对序列内所有元素进行累计操作
import functools #py3中内置函数已经没有reduce v1=[1,2,3,4,5,6] def func(x,y): return x+y result=functools.reduce(func,v1) print(result) #进行累加输出结果21 #改用lambda表达式 result2=functools.reduce(lambda x,y:x+y,v1) print(result) #21
小结:
1.函数可以当作是一个变量:参数传值/当元素嵌套到字典中
2.lambda表达式
3.内置函数
5.8函数的中高级
- 函数的中高级(闭包和高阶函数)
- 内置函数
- 内置模块(.py文件)
#第一题
for item in range():
pass
print(item) #9
#第二题
def func():
for item in range(10):
pass
print(item)
func() #执行函数之后item值为9
#第三题
item = 10
def func():
item = 2
def inner():
print(item)
for item in range(10):
pass #item的值修改为9
inner()
func() #9
#第四题
def func():
for num in range(10):
pass
v4=[lambda:num+10,lambda:num+100,lambda:num+100,]
result1=v4[1]()
num=66
result2=v4[2]()
print(result1,result2)
func() #109 , 166
补充:
1.数据类型中的函数到底有没有返回值?
-
无返回值
v=[11,22,33] v.append() #列表的方法一般没有返回值
-
有返回值
v="alex" result=v.split("l") #字符串的方法大部分有返回值,且split返回的是列表,其他的strip/replace等返回字符串 v={"k1":"v2"} result1=v.get("k1") result2=v.keys() #字典的方法中keys()/values()/items()都是有返回值的
-
有返回值+修改数据
v=[11,22,33] resut=v.pop() #返回修改的值
常用的需要记住的
-
str
- strip,返回字符串
- split,返回列表
- replace,返回字符串
- upper,返回字符串
- join,返回字符串
-
list
- append,无返回值
- insert,无返回值
- pop,返回要删除的数据
- remove,无
- find/index,返回索引的值
-
dict
-
keys(),返回列表
-
values(),返回列表
-
items(),返回字典
v={"k1":"v1","k2":"v2"} result=v.items() print(result) #dict_items([('k1', 'v1'), ('k2', 'v2')])
-
-
5.8.1函数可以做返回值
先看一段代码分析结果:
def func(arg):
return arg.replace("猪头","**")
def run():
msg="我看你像一个猪头"
result=func(msg)
print(result)
data=run() #先执行run函数,打印出result"我看你像一个**",然后函数run没有返回值,返回None给data
print(data) #data输出为None
再看一段代码
DATA_LIST=[]
def func(arg):
#return DATA_LIST.insert(0,arg)
DATA_LIST.insert(0,arg)
return None
data=func("绕不死你")
print(data) #None
print(DATA_LIST) #["绕不死你"]
再次强调:函数不被调用,内部的代码永远不会执行
func_list=[]
for i in range(10):
func_list.append(lambda :i)
result=func_list[0]()
print(result) #9
练习:
func_list=[]
for i in range(10):
func_list.append(lambda x:x+i)
for i in range(0,len(func_list)):
result=func_list[i](i)
print(result) #0,2,4,6,8,10,12,14,16,18
5.8.2闭包
闭包的概念:为函数创建一块区域,并为其维护自己的数据,为以后执行时方便调用
闭包的应用场景:装饰器/SQL Alchemy源码中
def func(name):
def inner():
print(name)
return inner #返回的是嵌套函数的函数名
v1=func("alex") #保留嵌套函数的函数名,以便为后面函数运行的时候提供数据
v1()
v2=func("eric")
v2()
- 函数内部执行相互之间不会混乱
- 执行完毕+内部元素不被其他变量使用的时候 => 销毁
def func():
print(123)
def bar():
return func
v=bar() #v保存bar函数的返回值func
v() #v一加括号就可以运行,打印出123
def bar():
def inner():
print(123)
return inner
v=bar() #返回值为inner,故bar函数里面的inner还在被使用,故bar函数不会被销毁
v() #找到inner函数执行
name="apple"
def bar():
name="cherry"
def inner():
print(name)
return inner
v=bar() #执行bar函数,返回inner
v() #执行inner函数,发现其没有name这个变量,故其到父级去找name,父级的name为cherry,打印出cherry
name="fruit"
def bar(name):
def inner():
print(name)
return inner
v1=bar("apple") #{name=apple,inner} 闭包,为函数创建一块区域(内部变量供字节使用,为以后函数的执行提供数据)
v2=bar("cherry") #{name=cherry,inner}
v1()
v2()
练习题:
#第一题
name="alex"
def base():
print(name)
def func():
name="eric"
base() #base()和func()都是由全局创建的,func函数执行到此相当于直接到base函数中创建一块内存空间
func() #alex
#第二题
name="alex"
def func():
name="eric"
def base():
print(name)
base()
func() #eric
#第三题
name="alex"
def func():
name="eric"
def base():
print(name)
return base
base=func()
base() #eric
注意:函数在何时被谁创建?
面试题:
#第一题
info = []
def func():
print(item)
for item in range(10):
info.append(func) #循环不会创建作用域
info[0]() #9
#第二题
info = []
def func(i):
def inner():
print(i)
return inner
for item in range(10):
info.append(func(item))
info[0]() #0
回顾:函数执行时,会创建一块内存保存自己函数执行的信息->闭包
def base(arg):
return arg
def func(arg):
def inner():
return arg
return inner
base_list = []
func_list = []
for i in range(10):
base_list.append(base)
func_list.append(func(i))
# 1.base_list和func_list中保存的是什么?
# base_list中存储的是base函数
# func_list中存储的是inner函数,特别是每个inner是在不同的地址创建,每次循环中传入的arg不同
# 2.如果循环打印什么?
for item in base_list:
v = item(i) #此时必须要传入参数,不然会报错
print(v) # 打印出的全部都是9
for item in func_list:
v = item()
print(v) # 打印出的是0-9
含lambda表达式的闭包
def func(name):
return lambda x:x+name
v1=func(1)
v2=func(2)
result1=v1(10)
result2=v2(100)
print(result1,result2) #11,102
区分闭包和不是闭包的区别
#不是闭包
def func1(name):
def inner():
return 123
return inner
#是闭包:封装值+内层函数需要使用
def func2(bane):
def inner():
print(name)
return 123
return inner
5.8.3高阶函数
- 把函数当作参数进行传递
- 把函数当作返回值
注意:队函数进行赋值
5.9装饰器
5.9.1装饰器原理
先看一段代码
v=1
v=2
# ##########################
def func():
pass
v=10
v=func
# ##########################
def base():
print(1)
def bar():
print(2)
bar=base
bar() #打印出的是1
def func(arg):
def inner():
return arg()
return inner
def f1():
print(123)
return 666
v1=func(f1) #返回的是inner函数
result=v1() #执行inner(),再执行f1(),打印123,返回的是f1()的返回值666
print(result) #打印666
=装饰器============
def func(arg):
def inner():
returm arg()
return inner
def index():
print(123)
return 666
#示例一:
v1=index() #执行index函数,打印123,并返回666
#示例二:
v2=func(index) #v2是inner函数,arg=index
index=666 #index被修改为666,但是arg仍保留原来index函数的地址
v3=v2() #执行inner函数,执行index(),打印666,返回123
#示例三:
v4=func(index)
index=v4 #将inner函数的地址给index
index() #此时执行的是inner() arg=index
5.9.2装饰器示例
def func(arg):
def inner():
print("before")
v=arg()
print("after")
return v
return inner
#第一步:执行func函数并将下面的函数参数传递,相当于:func(index)
#第二步:将fun的返回值重新赋值给下面的函数名:index=func(index)
@func
def index():
print(123)
return 666
index() #先打印before,再打印123,最后打印after
5.9.3装饰器编写和应用
#装饰器的编写
def x(func):
def y():
ret=func()
return ret
return y
#装饰器的应用
@x
def index():
pass
#执行函数,自动触发装饰器
v=index()
print(v)
记住:装饰器的编写格式:
def 外层函数(参数):
def 内层函数(*args,**kwargs):
return 参数(*args,**kwargs)
return 内层函数
装饰器的应用格式:
@外层函数
def index():
pass
index()
问:为什么要加*args **kwargs
答:传入万能参数,使得装饰器在应用的时候可以接收不一样个数参数的函数,而不用写多个装饰器
-
理解:
-
变量赋值
def func(): print(i) v1=func func=666
-
看看return的到底是什么
-
自己>上级作用域
-
5.9.4带参数的装饰器及其返回值
装饰器的内层函数的参数选哟和原来的函数的参数统一,目的是为了给index传参
所以建议用万能参数,使用一个万能参数的装饰器就可以满足全部的需求,
同样,对于其返回值来说也是如此
def x1(func):
def inner(*args,**kwargs):
return func(*args.**kwargs)
return inner
@x1
def f1():
print(123)
v1=f1()
def x1(func):
def inner(*args,**kwargs):
data=func(*args,**kwargs)
return inner
@x1
def f1():
print(123)
return 666
v1=f1()
print(v1) #None
#inner函数执行之后没有返回值,故返回None
装饰器建议的写法:
def x1(func):
def inner(*args,**kwargs):
data=func(*args,**kwargs)
return data
return inner
=带参数的装饰器=========
#原来的装饰器:1.执行ret=xxx(index) 2.将返回值赋值给index=ret
@xxx
def index():
pass
#带参数的装饰器:第一步,执行v1=uuu(9)
#第二步:ret=v1(index)
#第三步:index=ret
@uuu(9) #先执行函数得到返回值再当作原来的装饰器,但是其传入一个参数可以进行更多的操作
def index():
pass
def x(counter):
def wrapper(func):
def inner(*args,**kwargs):
data=func(*args,**kwargs)
return data
return inner
return wrapper
@x(9) #等价于执行wrapper=x(9),多传入参数9
def index():
pass
#即使没有调用index函数,x(9)仍然会被执行,且在装饰器写@x(9)的时候就会被执行
#装饰器生成的时候x函数和wrapper函数都会被执行,inner函数不会被执行
应用:当需要不同的装饰器的时候,可以写成一个装饰器
def x(counter):
def wrapper(func):
def inner(*args,**kwargs):
if counter==False:
pass
else:
pass
data=func(*args,**kwargs)
return data
return inner
return wrapper
@x(True)
def func1():
pass
@x(Flase)
def func2():
pass
对于写缓存的时候会有应用,对于缓存的存活时间控制
5.9.5关于函数执行的前后
def x1(func):
def inner(*args,**kwargs):
print("调用原函数之前")
data=func(*args,**kwargs) #执行原函数并给出返回值
print("调用原函数之后")
return data
return inner
示例/:
给一个读取文件的函数添加装饰器,使得其在执行函数前先判断路径是否存在
#检查路径是否存在
import os
def wrapper(func):
def inner(*args,**kwargs):
#传入的位置参数在args[]中可以找到
path=args[0]
if not os.path.exists(path):
print("路径不存在")
return None
result=func(*args,**kwargs)
return result
return inner
@wrapper
def read_userinfo(path):
file_obj=open(path,mode="r",encoding="utf-8")
data=file_obj.read()
file_obj.close()
return data
content=read_userinfo("/usr/bin")
##练习:请为下面所有函数编写一个装饰器,添加上装饰器以后可以实现:将装饰的函数执行5次,讲每次执行函数的结果按照##顺序放到列表中,最终返回列表
import random
def wrapper(func):
def inner(*args,**kwargs):
data=[]
for i in range(5):
result=func(*args,**kwargs)
data.append(result)
return data
return inner
@wrapper
def func():
return random.randint(1,4)
result=func() #执行五次,并将每次执行的结果追加到列表中 ,最终返回给result
print(result)
5.9.6元数据:Flask框架
5.9.7多个装饰器:Flask框架
@x1
@x2
def func():
pass
5.10递归
def func():
print(1)
func()
func() #不断递归直到递归次数达到上限
def func(1):
print(1)
func(i+1)
func(1)
#递归形成斐波那契数列
def func(a,b):
print(b)
func(b,a+b)
func(0,1)
def func(a):
if a==5:
return 10000
result=func(a+1)+10
return result
v=func(1)
print(v) #10040
练习:
#找到400万内斐波那契最大的数
"""
"""
num1=0
num2=1
count=0
while num2<4000000:
print(num2)
num1,num2=num2,num1+num2
count+=1
print(num1,count)