测试开发-晋级之路4-函数进阶(闭包 装饰器)***

一、闭包

在上面我们见过了再函数中调用函数本身。那么在函数中可不可以定义一个函数。

问题需求:如何函故外部调用函数内部定义的函数?

问题的引入: ,到底什么是闭包?

闭包的概念:

一个完整的闭包须满足一下三个条件:
1.函数中嵌套一个函数
2.外层函数返回内层嵌套函数的变量名
3.内层函数对外部作用域有一个非全局的变量进行引用

函数内部是否可以定义函数?
可以

def fun(): 
    print("fun")
    def fun2(): 
        print("fun2")

函数内部的函数是否能够进行调用?
不行

如何来实现对内部函数的调用?
通过return来返回内部函数名,代码如下:

def fun():
    print("fun") 
    def fun2(): 
        print("fun2") 
    return fun2

如何来实现内嵌函数的调用?

# 方法一fun()()
#方法二
res = fun()
res()

参考闭包的条件,不满足第三点“内层函数对外部作用域有一个非全局的变量进行引用”,加入非全局变量。

最简单的闭包案列

def fun(): 
    x = 100 
    print("fun") 
    def fun2(): 
        print("fun2")
        print(x)
   return fun2

#接下来我们对上面这个闭包进行分析,看是否满足我们说的三个条件
#第一个条件:函数中嵌套一个函数: fun中嵌套了fun2
#第二个条件:外层函数返回内存函数的变量名: fun中返回了fun2
#第三个条件:内层函数对外部作用域有一个非全局的变量进行引用:fun2中引用了fun中的变量x,而x是fun2的外部作用域fun中的局部变量
综上分析:闭包的三个条件都满足,那么确定是闭包无疑了。

带参数的闭包

def func_a(fun):
    print("func_a被调用了")
    def func_w(): 
        print("func_w被调用了")
        fun()

#与我们前面的闭包函故相比。有什么区别?
#是不是多了个参数?这是一个有参数的闭包。那么装饰器又是什么呢?
#我们在上面定义的闭包函数传入的参数,在后面有调用,那么这个参数是不是一个函数?
#接下来我们定义一个函数

def func_demo() :
    print'执行功能开始吃饭'

#这个时候我们调用上面的闭包函数func_a(func_demo)
I
#然而内部函数并没有被调用,那么怎么办,是不是用一个变量来接收一下,然后再调用

func_demo2= func_a(func_demo)
func_demo2()

作用和原理

作用:实现数据锁定,提高稳定性 装饰器
原理:python在运行到函数时,会为函数划分一块地址,用于存储函数,内部嵌套的函数也在该地址中,引用的局部变量会记录到__closure__中,相当于使用的是自身的属性,不受外部影响,也不影响外部变量。

装饰器

讲装饰器之前找们先来了解一下开放封闭原则(面向对象原则的核心)

开放封闭原则︰软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
装饰器的作用:在不更改原功能函数内部代码,并且不改变调用方法的情况下为原函数添加新的功能。

装饰器的应用场景:

1.登录验证
2.函数运行时间统计
3.执行函数之前做准备工作
4.执行函故后清理功能

5.1、实现一个装饰器

在上面的过程中其实我们已经完成了一个装饰器定义并应用,只是现在的语法并不是按照整个的装饰器来写的,装饰器之要在上面的基础稍加改动一点点就可以了。

假设有一个网站的页面,我现在已经定义好了index页面的调用方法,代码如下:

def index():
    print('This is the index page')

在基于开放封闭原则的基础上,我不再修改原有的函数内容,如何对原函数进行功能的拓展,例如我需要登录后才能进行index访问,首先我们要定义一个login函数:

#定义登陆的装饰器方法:
def login(func):#传入需要装饰的函数
    def fun(): 
        username = 'joker'
        password = 'python'
        user = input('请输入账号')
        pwd = input('请输入密码')
        if user == username and pwd == password: 
            print('登陆成功')
            func()#验证通过后调用原函数 
        else: 
            print('账号或者密码错误') 
     return fun

在原函数的基础上加上装饰器,效果如下:

@login
def index():
    print('This is the index page')
index()

当调用index()方法时会先去调用login()方法,通过装饰器实现功能的扩充。
@login:语法糖 等价于 index = login(index)
即把index当做参数传给login,并将return的fun用index进行接收,原来的index被保存在closure中(定义的fun的closure,实际的index的closure,即原来的index方法被保存到了login方法的带吗块中)。
在最后调用index时,实际上执行的是login(index)()。

5.2、实现有参数的装饰器

def add(func):
    def fun(a,b): 
        print('实现装饰器功能') 
        print('相乘:',a * b) 
        func(a,b)    
    return fun
@add
def add_num(a,b):
    print('相加:',a + b)
add_num(11,22)

参数的传递过程如下add_num= add(add_num)
1.在调用add_num时,实际上调用的是定义的fun函数,如果fun不进行参数定义,那么程序就会报错,不需要参数,但是传入了两个参数。
2.设置fun的传入参数之后,fun往下进行执行,执行到func()时(func实际上就是之前定义的add_num函数,定义时需要传入两个参数),如果参数不进行传入,程序也会进行报错,程序需要两个参数,但是没有进行参数的传入。

11和22两个参数,首先传递给了内置函数fun,fun接收后再传给传入的函数func。

5.3、实现一个通用装饰器

希望装饰器既可以用于装饰没有参数的函数,也可以装饰带参数的函数

def tongyong(func):
    def fun(*args,**kwargs): 
        print('实现装饰器功能')
        func(*args,**kwargs)    
    return fun
@tongyong
def index(): 
    print('这是初始页面')
@tongyong
def goods_list(n): 
    print('这是第{}页商品页面'.format(n))
index()
print("-"*30)
goods_list(7)

传参带*号主要做解包的操作。

5.4、实现一个类装饰器

def classzhuangshi(func):
    def fun(*args,**kwargs): 
        print('实现装饰器功能')
        return func(*args,**kwargs)
    return fun
@classzhuangshi
class MyClass():
    def __init__(self): 
        pass
m = MyClass()
print(m)

关键点在于内置函数要做一个return操作,当没有return时,默认会返回None类型,无法获取到创建的实例对象信息。

前面我们是用闭包函数来实现的装饰器,那么接下来给大家扩展一下,使用类来当做一个装饰器来用如果要把类当做一个装饰器来用,有两步操作,
首先在__init__—方法中,把被装饰的函数赋值给一个实例属性。
然后再了类里面实现一个__call__方法,在call方法中调用原来的函数。

class Test(object):
    def __init__(self,func) :
        self.func = func
    def .__call__(self,*args,**kwargs ):
        print('这个是类装饰器')
        self.func(*args,**kwargs)

#说明:
#1,当用Test来装作装饰器对func函数进行装饰的时候,首先会创建Test的实例对象,并且会把func这个函数名当做参数传递到__init__方法中,即在__init__方法中的func变量指向了func函数体
#2. func函数相当于指向了用Test创建出来的实例对象
#3.当在使用func()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#4.为了能够在__call__方法中调用原来func指向的函数体,所以在__init__—方法中就需要一个实例属性来保存这个函数体的引用,所以才有了

self.__func = func

这句代码,从而在调用__call__方法中能够调用到func之前的函数体。

多个装饰器装扮同一个函数。
@counttime #index = counttime(index) 此时传入的index就是check,赋值以后的index就是fun
@login_check # index = login(index) 此时的index就是check
def index(): 
    print('index')

实际的运行结果如下:

这个是时间函数
1624455280.1388054
这个是登陆函数
账号:joker
密码:python
index
1624455290.8447
耗时为.{} 10.705894708633423

从下往上装饰,从上往下执行。

常用的三个内置类函数
class MyClass():
    def __init__(self,name):
        self.name = name
        
    @classmethod #类方法,类可直接进行调用,对象也可以进行调用,传参self 变成cls    
    def test(cls): 
        print(cls)  
        
    @staticmethod #静态方法,不需要进行传参self,实例和类都可以调用    
    def static():
        print('This is static method')   
        
    @property #设置只读属性    
    def setattr(self):
        self.age = '18' 
        return self.age
        
MyClass.test()
t = MyClass('joker')
age = t.setattr
print(age)
t.age = 25
print(t.setattr)
print(t.age)

输出结果如下:

<class '__main__.MyClass'>
18
18
18
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值