Python在项目中的实战-装饰器

一、装饰器介绍

1、闭包函数

在介绍装饰器之前,先给大家介绍下Python的闭包函数。闭包函数是必须内嵌函数且引用一个定义在闭合范围内即外部函数里的变量,同时外部函数必须返回内嵌函数的一个函数。闭包函数简单来说就是函数里面包含函数,同时满足以下三个条件:

  • 必须内嵌函数
  • 引用一个定义在闭合范围内即外部函数里的变量
  • 外部函数必须返回内嵌函数

闭包函数示例

def test():
     symbol="*" # test函数变量,对于inner函数来说是外部函数变量
     def inner(n):   # test函数的内嵌函数
         symbol_str = symbol*n # symbol引用来自test函数
         return symbol_str
     return inner # 返回内嵌函数inner结果
 f=test()
 print(f(1))  # *
 print(f(4)   # ****

2、装饰器

装饰器本质上是一个函数,装饰器顾名思义就是装饰函数、增强函数功能的函数,可以在原函数不需要做任何代码变动的前提下增加额外的功能。装饰器函数的外部函数传入要装饰的函数名,返回经过修饰后函数的名字;内层闭包函数修饰被修饰函数。简单来说装饰器可以总结为以下几点:

  • 性质:函数
  • 传入参数:修饰函数名
  • 返回:已装饰函数名

装饰器示例

def out_wrapper(func):
  def wrapper(*args, **params):
    return func(*args, **params)
  return wrapper

二、装饰器构建与应用

1、装饰器构建

在文章的开头已经给大家介绍了什么是装饰器,现在我将通过一个示例教大家如何构建一个装饰器。
(1)需求:输出每个接口性能,即运行花费时间。示例接口如下:

    import json
    from django.http import HttpResponse,request

    def test_api(request):
        result = 25 * 123
        return HttpResponse(json.dumps("res":result))

(2)需求实现:

  • 普通实现方式
    我们一般会先想到以下实现方式,在逻辑开始和结束的时候记录时间,最后计算时间差则是这个接口的执行时间。假如我们有几十个接口,那么我们需要在每个接口都要加上这样一个逻辑,如果我们需要修改计算执行时间的逻辑,那么我们需要修改每个接口的代码,而且如果计算逻辑比较多,也会让接口代码看见来很累赘,这个时候我们可以考虑装饰器的处理方式。

      # 接口函数增加计算执行时间逻辑
      import time
      import json
      from django.http import HttpResponse,request
    
      def test_api(request):
          start_time = time.time()
          result = 25 * 123
          end_time = time.time()
          print(f"The api cost {end_time -start_time }s")
          return HttpResponse(json.dumps("res":result))
  • 引入装饰器
    使用装饰器,装饰接口函数,计算接口函数执行时间,有计算逻辑修改只需修改装饰器,无需改动接口函数,同时让接口函数更简洁。

      # 无参数装饰器
      import json
      from django.http import HttpResponse,request
    
      def cost_time(func):
          def wrapper(*args, **kargs): 
              start_time = time.time()
              func(*args, **kargs)
              end_time = time.time()
              print(f"The api cost {end_time -start_time }s")
              return wrapper(*args, **kargs)
          return decorator(func)
    
      @cost_time
      def test_api(request):
          result = 25 * 123
          return HttpResponse(json.dumps("res":result))

    如果在打印接口执行时间的时候需要额外输出每个接口自定义的信息,那么这时候我们可以使用带参数的形式定义装饰器,这时候我们可以采用三层函数定义装饰器。

      # 带参数装饰器
      import json
      from django.http import HttpResponse,request
    
      def cost_time(desc):
          def decorator(func):
              def wrapper(*args, **kargs): 
                  start_time = time.time()
                  func(*args, **kargs)
                  end_time = time.time()
                  print(f"The api {desc} cost {end_time -start_time }s, fun name {func.__name__}")
              return wrapper(*args, **kargs)
          return decorator(func)
    
      @cost_time("计算测试接口")
      def test_api(request):
          result = 25 * 123
          return HttpResponse(json.dumps("res":result))

    另外需要说明的是,装饰器的使用有以下两种方式,以装饰器cost_time为例:
    第一种:使用语法糖@符号+装饰器函数名的方式,同以上实例所写@cost_time。语法糖方式简洁方便,推荐此使用方法。
    第二种:若装饰器无参数写法为:f=cost_time(函数名);若有参数则为:f=(cost_time(参数))(函数名)

2、装饰器使用方式

  • 函数装饰函数
import time
def cost_time(func):
    def wrapper(arg1, arg2):
        start_time = time.time()
        func(arg1, arg2)
        end_time = time.time()
        print(f"The api cost {end_time -start_time }s")
        return func(arg1, arg2)
    return wrapper
@cost_time
def add(a, b):
    return a + b
print(add(2,3))
运行结果:
The api cost 1.9073486328125e-06s
5
  • 函数装饰类
def wrapClass(cls):
    def inner(a):
        print('class name:', cls.__name__)
        return cls(a)
    return inner

@wrapClass
class Company():
    def __init__(self, dept):
        self.dept = dept

    def fun(self):
        print('department =', self.dept)


m = Company('Game Center')
m.fun()
运行结果:
class name: Company
department = Game Center
  • 类装饰函数
class ShowFunName():
    def __init__(self, func):
        self._func = func

    def __call__(self, arg):
        print('function name:', self._func.__name__)
        return self._func(arg)

@ShowFunName
def Game(game_name):
    return game_name

print(Game('LOL'))
运行结果:
function name: Game
LOL
  • 类装饰类
class ShowFunName():
    def __init__(self, func):
        self._func = func

    def __call__(self, arg):
        print('function name:', self._func.__name__)
        return self._func(arg)

@ShowFunName
def Game(game_name):
    return game_name

print(Game('LOL'))
运行结果:
function name: Game
LOL
  • 函数装饰器嵌套

3、装饰器应用

装饰器介绍中我们知道装饰器主要就是用于装饰函数,增强函数功能,在实际项目中函数需要增强功能的应用有以下:

- 登录
- 日志
- 函数执行时间
- 函数逻辑执行前处理
- 函数逻辑执行完成后处理
- 事务
...

函数执行时间在函数的构建中举例过,本次实例我们用常用的登录。假设需求是用户需要通过用户认证之后才能调用函数,我们使用装饰器实现。

def cost_time(func):
    def wrapper(arg1, arg2):
        username = input('username:').strip()
        password = input('password:').strip()
        if username == 'test' and password == 'test':
            func(arg1, arg2)
        return func(arg1, arg2)
    return wrapper
@cost_time
def add(a, b):
    return f"add result: {str(a + b)}"
print(add(2,3))
运行结果
username:"test"
password:"test"
add result: 5

三、内置装饰器

Python常见内置装饰器有@property、@staticmethod、@classmethod,接下来分别对这三个内置装饰器做介绍。

1、@property

属性装饰器,主要用来修饰类方法,将函数变成作为属性使用。

class Calculate(object):
    def __init__(self,a, b):
        self.a = a
        self.b= b
    @property
    def add(self):  # 引入@property装饰器,表示此方法为Calculate类的一个只读属性,用户无法更改属性,因为用户调用的时候是看不到self.a、self.b的,从而也保护了类的属性
        return self.a + self.b

    def sub(self):
        return self.a - self.b

    def ge(self):
        return self.a - self.b
c = Calculate(5, 2)
print(c.add) # 输出结果为7,由于加上了@property,add此时是一个属性,所以用户可以直接使用,不需要(),如果带上()的话会报错
print(c.sub()) # 输出结果为3,sub是类Calculate的一个方法,跟调用方法一样使用,如果不带()是错误的

2、@staticmethod 静态方法

staticmethod用于修饰类方法,不仅可以像普通类方法一样使用实例调用,也可以在直接调用,无需实例化,执行效率较高。接下来我们通过实例说明如何使用静态方法。

class A(object):
    def common_f(self):  # 常规函数中,slef通常用作实例方法的第一参数
        print('It's a common func')
    @staticmethod
    def static_f():  # 参数不是必须的,同普通方法一样
        print('It's a staticmethod');

A.static_f();          # 静态方法无需实例化
aj = A()
aj.static_f()        # 也可以实例化后调用

3、@classmethod 类方法

classmethod类方法修饰对应的函数不需要实例化,也不需要self参数,但第一个参数需要是表示自身类的cls参数,可以来调用类的属性,类的方法,实例化对象等。接下来我们通过实例说明如何使用类方法。

class A(object):
    desc = "Hello world!"
    def common_f(self):  # slef通常用作实例方法的第一参数
        print('It's a common func')
    @classmethod
    def class_f(cls):
        print (cls.desc)
        cls().common_f()   # 调用自身常规函数common_f

A.class_f()               # 不需要实例化

@classmethod和@staticmethod区别

classmethod和staticmethod方法类似,我们可以通过以下几个方面进行对比:

方法 调用方式 参数 访问自身类属性
classmethod 无需实例化 不需要self参数,但第一个参数需要是表示自身类的cls参数 通过cls访问
staticmethod 直接调用无需实例化或先实例化再调用 参数非必须,同常规函数 通过类名称访问

具体区别体现如下代码所示:

class A(object):
    desc = "Hello world!"
    def common_f(self):  # slef通常用作实例方法的第一参数
        print('It's a common func')
    @classmethod
    def class_f(cls):
        print (cls.desc)
        cls().common_f()   # 类方法通过cls参数调用自身类属性
        print('classmethod !!!')
    @staticmethod
    def static_f():
        print (A.desc) # 静态方法通过类名调用类属性,可以无参数
        print('staticmethod !!!')

A.class_f()  
全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务