Python函数装饰器详解

1. 装饰器

增强函数或类的功能的一个函数,可以装饰函数,也可以装饰类

1.1 举个栗子

def time_decorator(func): #装饰器
    def wrapper(*args, **kwargs): #wrapper在第二节中具体讲
        # 在函数前打印开始时间
        start_time = time.time()
        print("start time is:", start_time)
        
        # 执行待装饰函数本身
        f = func(*args, **kwargs)
     
        # 打印结束时间和执行耗时
        end_time = time.time()
        print("end time is:" ,end_time)
        exec_time = end_time - start_time
        print("execute time is : ", exec_time)
        return f
    return wrapper

1.2 使用方法

  • 方法一:使用语法糖@符号

    @time_decorator
    def add(a,b):
     	print("inside decorator")
    	return a + b
    
    @time_decorator
    def sub(a,b):
    	return a - b
    
    print(add(2,3))
    
  • 方法二:常规方法 (!注意传参方式 !)

    def add(a,b):
    	print("inside decorator")
    	return a + b
    
    print(time_decorator(add)(2, 3))
    
      start time is: 1599904203.3112652
      inside decorator
      end time is: 1599904203.3112652
      execute time is :  0.0
      5
    

1.3 参数传递

  • 不传参的装饰器

    def login(func):
    	def wrapper(*args, **kargs):
        	print("function name is:", func.__name__)
        	return func(*args, **kargs)
    	return wrapper
    
    @login
    def f():
    	print("in the function f")
    
    f()
    
      function name is: f
      in the function f
    
  • 自身传参的装饰器

    def login(text):  # 装饰器login传入text参数
        def decorator(func):  # func函数的装饰器decorator
            def wrapper(*args, **kargs):
                print(text + "***" + func.__name__)
                return func(*args, **kargs)
            return wrapper
        return decorator
    
    @login("this is the text para")
    def f():
        print("in the function f")
    
    f()
    
      this is the text para***f
      in the function f
    

2 Wrapper

2.1 可变长度参数*args,**kwargs

  • args表示任何多个无名参数,它是一个tuple;kwargs(keyword arguments)表示关键字参数,它是一个dict
    同时使用*args和**kwargs时,*args参数必须要列在**kwargs前。
def function(x,y,*args,**kwargs):
    print(type(x))
    print(args)
    print(kwargs)
    print(type(args)) #*args返回的是数组
    print(type(kwargs)) #**kwargs返回的字典

function(1,2,3,4,5,a=1,b=2,c=3)
<class 'int'>
(3, 4, 5)
{'a': 1, 'b': 2, 'c': 3}
<class 'tuple'>  
<class 'dict'>

2.2 @wraps

  • 避免被装饰函数自身的信息丢失,不改变使用装饰器原有函数的结构(如__name__, doc)
  • def wrapper(func)和@wraps(func) 是一回事
    • from functools import wraps @wraps(func)

3 内置装饰器

3.1 @property

  • 通过@property装饰后的方法也可以通过一个实例像访问数据属性一样去访问函数,触发一个函数的执行,动态计算出一个值

    import math
    class Circle:
        def __init__(self, radius):
            self.radius = radius
    
        @property
        def area(self):
            return math.pi * self.radius**2
    
        @property
        def perimeter(self):
            return 2*math.pi*self.radius
    
    # 通过实例访问类中属性
    circle = Circle(10)
    print(circle.radius)
    
    # 访问函数
    print(circle.area)
    print(circle.perimeter)
    
      10
      314.1592653589793
      62.83185307179586
    
  • property对象还具有setter、deleter 可用作装饰器。setter是设置属性值,deleter用于删除属性值。

  • 官方文档中给出getter用于获取属性信息,但实际使用中可直接通过property获取属性信息

    class C:
        def __init__(self):
            self.x = None
    
            @property
            def x(self):
                return self.x
    
            @x.setter
            def x(self, value):
                self.x = value
    
            @x.deleter
            def x(self):
                del self.x
    
    
    c = C()
    print(c.x)
    c.x = 100
    print(c.x)
    del c.x  #删除属性
    
      None
      100
    

3.2 @staticmethod

  • 修饰的方法不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等

  • 除了@staticmethod装饰后的,python类方法都需要self参数

    class A():
        number = 10
    
        @classmethod
        def get_a(cls): #cls 接收的是当前类,类在使用时会将自身传入到类方法的第一个参数
            print("This is class itself:", cls)
            print("This is class's attribute:", cls.number)
    
    class B(A):
        number = 20
    
    
    A.get_a()
    B.get_a()
    
      This is class itself: <class '__main__.A'>
      This is class's attribute: 10
      This is class itself: <class '__main__.B'>
      This is class's attribute: 20
    

3.3 @classmethod

  • 改变一个方法为静态方法,静态方法不需要传递隐性的第一参数,静态方法的本质类型就是一个函数,一个静态方法可以直接通过类进行调用,也可以通过实例进行调用

    import time
    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    
        @staticmethod
        def now():
            t = time.localtime()
            return Date(t.tm_year, t.tm_mon, t.tm_mday)
    
        @staticmethod
        def tomorrow():
            t = time.localtime(time.time()+86400)
            return Date(t.tm_year, t.tm_mon, t.tm_mday)
    
    
    a = Date(2005, 11, 27)
    print(a.year, a.month, a.day)
    b = Date.now()
    print(b.year, b.month, b.day)
    c = Date.tomorrow()
    print(c.year, c.month, c.day)
    
      2005 11 27
      2020 9 12
      2020 9 13
    

3.4 区分一哈

通俗且不咋子准确的概括一下就是:

  • @property可以通过实例来调用函数,@staticmethod无需实例化直接调就完事
  • 访问类属性或调用实例方法时,@staticmethod需要把类名写上,@classmethod就是传一个类的参数cls代表本类然后直接用

官方一点来讲:

  • The difference between a static method and a class method is:
    Staticmethod knows nothing about the class and just deals with the parameters
    Classmethod works with the class since its parameter is always the class itself.

!总结 !

  • 不需要用到与类相关的属性与方法时,用静态方法@staticmethod
  • 要用到与类相关的属性与方法,且想表明这个方法是整个类通用的而不是对象特异的,用类方法@classmethod
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值