Python 装饰器

Python装饰器真是个很有用的武器呢!一定要理解透彻,因为面试会让你写装饰器的,比如加锁的装饰器,统计函数被调用的装饰器!

python 函数的特性

1,函数可以作为参数

def func():
    return "func"
def test_func(x):
   return x()

A=func
res=test_func(A)
print(res)

结果:func

2,函数可以作为返回值

def func():
    return "func"

def test(x):
    return x

A=func
res=test(A)
print(res()) #func

3,函数可以作为变量

def func():
    return "func"

A=func
res=A()
print(res) #func

4,函数可以嵌套及跨越访问

def test_func():
    x="test_func"
    def test_inner():
        print(x)
    return test_inner()


test=test_func() #test_func

闭包

闭包就是引用了外部函数的变量的内部函数,而闭包的实现正是利用了以上函数特性,下面我们来看看闭包是如何实现的:

def outer(x):
    def inner():                  # 函数嵌套
        return x                  # 跨域访问,引用了外部变量x
 
    return inner                  # 函数作为返回值
 
closure = outer('外部变量')        # 函数作为变量赋给closure
print(closure())                  # 执行闭包
 
# 外部变量

装饰器与装饰器语法糖

装饰器就是利用闭包实现,使用函数作为参数。

def test_func(f):
    x="test_func"
    def test_inner():
        print("docorator")
        f()
    return test_inner

def func(): 
    print("func")

deco_test=test_func(func)
deco_test()
#运行结果
#docorator
#func

@装饰器语法糖


def test_func(f):
    x="test_func"
    def test_inner():
        print("docorator")
        f()
    return test_inner

@test_func     #实质是:test_func(func)
def func():  
    print("func")

func()  #实质是:test_func(func)(),即test_inner()

#运行结果
#docorator
#func

装饰器是在转载的时候被运行的,@test_func使用在func函数上,实质是对装饰器的调用:test_func(func)

自由变量及nonlocal的使用

上面函数计数的例子中两个变量有料!coun与 count_l 在内层函数的赋值大不同

coun是外层函数的局部变量,对内部函数(闭包函数)来说是自由变量,可以使用,但是不可以赋值,要赋值的话,必须使用nonlocal。
nonlocal的作用申明此变量为自由变量,声明的变量可以赋予新值,闭包中保存的绑定会更新。

list类型的,不需要使用nonlocal,为什么可以在内层函数使用append()方法?
因为append并没有给列表赋值,只是追加了内容。利用了列表是可变对象

装饰器函数

一个被类的装饰器修饰的方法就是一个类的实例。
类装饰器离不开__call__魔术方法的对象,实现了__call__魔术方法的对象是可调用的,能够使得允许一个类的实例像函数一样被调用

# 函数是可调用对象
def add():
    pass

add.__call__()
add()

# 类实现了__call__方法
class Add():
    def __call__(self, *args, **kwargs):
        print(args)
        print(kwargs)

add_instance = Add()

add_instance.__call__(1,2,3, course='Python')

add_instance(1,2,3,course='Python')

call:允许一个类的实例像函数一样被调用:x(a, b)调用为 x.call(a, b)

class CallingCounter(object):
    def __init__ (self, func):
        self.func = func
        self.count = 0

    def __call__ (self, *args, **kwargs):
        print("类--当前被执行的函数名为:{}".format(self.func.__name__))
        self.count += 1
        print("{}被调用{}次".format(self.func.__name__,self.count))
        return self.func(*args, **kwargs)

@CallingCounter  #实质是CallingCounter(func),一个被类的装饰器修饰的方法就是一个类的实例
def func():
    print("func")

func()
func()
func()

打印内容:
类–当前被执行的函数名为:func
func被调用1次
func
类–当前被执行的函数名为:func
func被调用2次
func
类–当前被执行的函数名为:func
func被调用3次
func

装饰器的实例

1, 计算函数被调用次数的装饰器:

def func_count(func):
    print("我是装饰器,我被调用了。。。")
    coun=[] 
    count_l=0
    def _call_times(*args,**kwargs):
        print("函数--当前被执行的函数名为:{}".format(func.__name__))
        nonlocal count_l 
        count_l+=1
        coun.append(1) 
        print("我被调用了{}次".format(coun))
        print("***我被调用了{}次".format(count_l))
        return func(*args,**kwargs)

    return _call_times

装饰器使用语法糖,加载在两个函数上

@func_count
def test():
    return 1+1

@func_count
def test_test():
    return 3

即使函数没有被执行,打印的内容如下:

我是装饰器,我被调用了。。。
我是装饰器,我被调用了。。。

@func_count
def test():
函数头部@装饰器后,实质是装饰器的调用:func_count(test),所以如上信息被打印

运行函数

test()
test()
test()

test_test()

结果:

我是装饰器,我被调用了。。。
我是装饰器,我被调用了。。。
函数–当前被执行的函数名为:test
我被调用了[1]次
***我被调用了1次
函数–当前被执行的函数名为:test
我被调用了[1, 1]次
***我被调用了2次
函数–当前被执行的函数名为:test
我被调用了[1, 1, 1]次
***我被调用了3次
函数–当前被执行的函数名为:test_test
我被调用了[1]次
***我被调用了1次

2,替换函数

有时候更改旧代码,发现个别处的函数功能需要替换,但是这个函数很多地方已经被调用了,不想改动那么多地方的话,又不想改变调用关系及函数名称,这个时候可以使用装饰器来改变函数功能

import inspect
def respeat_func_dec(replace_func):
    print("replace_func:",replace_func.__name__)
    def decor(func_):
        print("func_:", func_.__name__)
        def wrap(*args, **kwargs):
            print("wrap")
            return replace_func(**inspect.getcallargs(func_, *args, **kwargs))
        return wrap
    return decor

def new_func(name="",age=0):
    print(f"name={name}")

@respeat_func_dec(new_func)  #实质是:doc_func(new_func)(old_func)
def old_func(name="",age=0):
    print(f"age={age}")


old_func(name="xiaoyu",age=2)

打印:

replace_func: new_func
func_: old_func

执行装饰器:

old_func(name="xiaoyu",age=2) #实质是:doc_func(new_func)(old_func)(name="xiaoyu",age=2)

打印:

replace_func: new_func
func_: old_func
wrap
name=xiaoyu

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值