关于python装饰器的个人理解

一、装饰器是个什么东西?

装饰器可以说是python中的一种编程特性,也可以说是非常好用的一种python编程技巧。

二、它有什么用呢?

在我们编写代码的时候,对于很多函数当我们逻辑写完之后,它就基本不会在修改了(在开发时我们一般情况不会轻易修改源代码)。那么如果有一天我想为它增加一点新功能时,如何保证不修改源代码的情况下增加新的功能呢?   答案就是-->装饰器

三、看一个简单的例子

@是python中的语法糖,@后面跟着的就是我们的装饰器函数

# 现在我们有一个fun函数,在不修改原函数的情况下添加一点额外的功能 

def decorator(func):
    def wrapper(*args, **kwargs):
        print("这是在装饰器中的功能")
        func(*args, **kwargs)
    return wrapper

@decorator
def fun():
    print("this is fun")


# 输出:
这是在装饰器中的功能
this is fun

我们可以从这段代码中简单了解装饰器的基本语法与使用方法。对于其运行流程而言还是比较清晰的,作为有一定基础的人来说应该不成问题-..-。

装饰器要素:内函数使用外函数的变量,外函数返回内函数。

记住这点时非常重要的,这是装饰器的实现基本要素。

在上面我们实现了一个简单的无功能装饰器,现在我们需要来实现一个计时的装饰器

import time
def time_decorator(fun)
    def wrapper(*args, **kwargs)
        # 在这里真正的执行函数
        time1 = time.time()
        fun(*args, *kwargs)
        time2 = time.time()
        print("函数执行时间:", time2 - time1)
    return wrapper


@time_decorator()
def fun():
    # 让这个函数睡个几秒
    time.sleep(3)

上述代码很容易理解吧,当然这是装饰器最简单明了的用法了。

四、带参数的装饰器

对于不带参数的装饰器,其用法,只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数,执行固定逻辑。装饰器传参就是它比较高级的用法,当然我们还是先从简单的例子入手。

例子:根据传入的国家名称打印出不同的问候语言(例子仅仅用于体验器运行流程)

 

def say_hello(name):
    def decortaor(func):
        def wrapper(*args, **kwargs):
            if name == "china":
                print("你好")
            else:
                print("hello")

            # 函数在这里执行
            func(*args, **kwargs)

        return wrapper
    return decortaor

@say_hello("china")
def hello_word():
    print("this is hello word")

hello_word()

在这里我们使用了两层嵌套, 第一从层传入的name明显时我们的参数‘china’, 第二层传入的func才是代表了函数本身,

从这个例子我们可以知道,对于装饰器传参的时候,我们需要使用两层的嵌套才能完成传参,对于传参的顺序也必须有固定的顺序。除了上诉例子之外,还有一个应用较多的场景,在网站发请求时可对其请求方式作分类处理(POST, GET等等)。

 

五、高阶用法-不带参数的类装饰器

我们上面所讲到的装饰器都是基于函数的装饰器,类装饰则是比较高阶的用法。

我们想要实现类装饰器首先在类中必须实现两个魔法方法:__call__以及__init__

他们的作用,__init__:接收被装饰的函数, __call__ :实现装饰逻辑

以代码为例

calss decorator():
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        print("这是类装饰器")

        # 函数真正的执行
        self.func(*args, **kwargs)
        

@decorator
def func():
    print("this is func")

func()

 函数是通过init传入类中。__call__ 的作用则是重载了“()”,在此情况下,对象就可以使用p()这种格式调用的方法就是call。

六、带参数的类装饰器

其实在实现了带参数的函数装饰器之后,我们对其运行过程就有一定的理解。

下面还是使用一个简单的例子看一看。

class decorator():
    def __init__(self, name):
        # init接受装饰器传入的参数
        self.name = name

    # 从call传入函数对象
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(self.name)

            # 真正的函数执行
            func(*args, **kwargs)
        return wrapper

@decorator('hello')
def fun():
    print("this is fun")

fun()

与不参数的类装饰器不一样的是,函数的传递位置发生了改变。参数通过init传入,函数对象则是通过call传入。

目前就先了解这几种装饰器的原理与实现。

 

--例子:现在写一个面试常遇见的装饰器实现单例模式(不涉及传参)。

def Singlestance(cls, *args, **kwargs):
    _instace = {}
    def wrapper():
        if cls not in _instace:
            _instance[cls] = cls(*args, **kwargs)
    return wrapper

@Singlestance
class A()
    def __init__(self):
        self.name = "lixin"

if __name__ == '__main__':
    one = A()
    two = A()
    print(id(one), id(two))

# 输出
139900931696624 139900931696624

从输出上可以看出来两个对象的地址时一样的,那么也就意味着这两个对象是同一个对象。(单例模式完成)

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值