首页 > 编程语言 >Python 2-07 装饰器 @decorator

Python 2-07 装饰器 @decorator

时间:2023-05-22 11:05:08浏览次数:43  
标签:07 Python wrapper say decorator func print def


Python 装饰器 @decorator

Python 装饰器其实就是对函数的包装,函数作为参数,在不修改函数源代码的基础上,并对函数做一些包装,然后返回增加了包装的函数,即生成了一个新函数。

登录校验,权限校验,日志记录等,这些功能在各个环节都可能需要,但又十分雷同,可以通过装饰器来抽象、剥离这部分代码。

def decorator_func(some_func):
    # define another wrapper function which modifies some_func
    def wrapper_func():
        print("Wrapper function started")   
        some_func()   
        print("Wrapper function ended")    

    return wrapper_func 
    # Wrapper function add something to the passed function and decorator returns the wrapper function    

def say_hello():
    print ("Hello")  

say_hello = decorator_func(say_hello)
say_hello()
# Output:
#  Wrapper function started
#  Hello
#  Wrapper function ended

上面例子中,decorator_func 就是定义的装饰器,它接受 some_func 作为参数。它定义了一个 wrapper_func 函数,该函数调用了 some_func 但也增加了一些自己的代码。

装饰器的Python语法

@decorator_func
def say_hi():
    print 'Hi!'

装饰器的顺序

@a
@b
@c
def foo():
    print('foo')

# 等同于:
foo = a(b(c(foo)))

带参数函数的装饰器

def decorator_func(some_func):
    def wrapper_func(*args, **kwargs):
        print("Wrapper function started")
        some_func(*args, **kwargs)
        print("Wrapper function ended")    

    return wrapper_func

@decorator_func   
def say_hi(name):
    print ("Hi!" + name)

上面代码中,say_hi 函数带有一个参数。通常情况下,不同功能的函数可以有不同类别、不同数量的参数,在写wrapper_func 的时候,我们不确定参数的名称和数量,可以通过 *args 和 **kwargs 来引用函数参数。

带参数的装饰器

不仅被装饰的函数可以带参数,装饰器本身也可以带参数。参考下面的例子:

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

简单来说,带参数的装饰器就是在没有参数的装饰器外面再嵌套一个参数的函数,该函数返回那个无参数装饰器即可。

类作为装饰器

使用类装饰器,优点是灵活性大,高内聚,封装性。通过实现类内部的__call__方法,当使用 @ 语法糖把装饰器附加到函数上时,就会调用此方法。

class Foo(object):
    def __init__(self, func):
       self._func = func

def __call__(self):
    print ('class decorator runing')
    self._func()
    print ('class decorator ending')

@Foo
def say_hi():
    print('Hi!')

say_hi()
# Output:
# class decorator running
# Hi!
# class decorator ending

functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、name、参数列表,先看看下面例子:

def decorator_func(some_func):
    def wrapper_func(*args, **kwargs):
        print("Wrapper function started")
        some_func(*args, **kwargs)
        print("Wrapper function ended")    

    return wrapper_func

@decorator_func   
def say_hi(name):
    '''Say hi to somebody'''
    print ("Hi!" + name)

print(say_hi.__name__)  # Output: wrapper_func
print(say_hi.__doc__)   # Output: None

可以看到,say_hi 函数被 wrapper_func 函数取代,它的 name 和 docstring 也自然是 wrapper_func 函数的了。
Python 有 functools.wraps,wraps 本身也是一个装饰器,它的作用就是把原函数的元信息拷贝到装饰器函数中,使得装饰器函数也有和原函数一样的元信息。

from functools import wraps

def decorator_func(some_func):
    @wraps(some_func)
    def wrapper_func(*args, **kwargs):
        print("Wrapper function started")
        some_func(*args, **kwargs)
        print("Wrapper function ended")    

    return wrapper_func

@decorator_func
def say_hi(name):
    '''Say hi to somebody'''
    print ("Hi!" + name)

print(say_hi.__name__)  # Output: say_hi
print(say_hi.__doc__)   # Output: Say hi to somebody

给函数打日志

下面这个示例演示了一个 logger 的 decorator,这个 decorator 输出了函数名,参数,返回值,内存占用和运行时间。

from functools import wraps
import time

def logger(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        ts = time.time()
        result = fn(*args, **kwargs)
        te = time.time()
        print("function      = {0}".format(fn.__name__))
        print("    arguments = {0} {1}".format(args, kwargs))
        print("    return    = {0}".format(result))
        print("    time      = %.6f sec" % (te-ts))
        print('    memory    = %d B' % fn.__sizeof__())
        return result
    return wrapper  

@logger
def multipy(x, y):
    return x * y  

@logger
def sum_num(n):
    s = 0
    for i in range(n+1):
        s += i
    return s  

print(multipy(2, 10))
print(sum_num(100))
print(sum_num(10000000))

类的内置装饰器

类属性 @property
静态方法 @staticmethod
类方法 @classmethod

通常,我们需要先实例化一个类的对象,再调用其方法。
若类的方法使用了 @staticmethod 或 @classmethod,就可以不需要实例化,直接类名.方法名()来调用。
从使用上来看,@staticmethod 不需要指代自身对象的self或指代自身类的cls参数,就跟使用普通函数一样。@classmethod 不需要 self 参数,但第一个参数必须是指代自身类的cls参数。如果在 @staticmethod 中要调用到这个类的一些属性方法,只能直接类名.属性名,或类名.方法名的方式。
而 @classmethod 因为持有 cls 参数,可以来调用类的属性,类的方法,实例化对象等。


标签:07,Python,wrapper,say,decorator,func,print,def
From: https://blog.51cto.com/u_1439909/6321619

相关文章

  • Python 4-09 time
    time 在 Python 中与时间处理有关的模块包括 time,datetime 以及 calendar。在 Python 中,用三种方式来表示时间,分别是时间戳、格式化时间字符串和结构化时间。时间戳(timestamp):1970年1月1日之后的秒,可以通过 time.time() 获得。时间戳是一个浮点数,可以进行加减运算,但......
  • Python 1-18 字典
    Python1-18字典Python的字典数据类型采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。例如,用list实现成绩单:#给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,list越长,耗时越长。names=......
  • Python 1-17 元组
    Python1-17元组classtuple([iterable])tuple是一个不可变的序列类型。>>>s='abc'>>>l=[1,2]>>>t=1,2>>>d=dict(a=1,b=2)>>>set={'a','b'}1、元组创建>>>tup=()#创建空元组>>......
  • Python 2-02 命名空间和作用域
    命名空间和作用域一、命名空间命名空间(Namespace)是从名称到对象的映射,一般用Python字典来实现。为了解决项目中名字冲突的问题引入了命名空间的概念,命名空间可以嵌套。1、命名空间分类:内置名称(built-innames),Python语言内置的名称,比如函数名abs、char和异常名称Exception......
  • Python 2-01 函数
    一、函数定义def函数名(参数列表):函数体判断一个数是不是素数?#方法一:for循环判断素数num=int(input('请输入一个正整数:'))foriinrange(2,int(num**0.5)+1):ifnotnum%i:print(f'{num}不是素数')breakelse: print(f'{num}是素数')......
  • Python 2-06 闭包
    闭包Closures嵌套函数(nestedfunction),内函数引用了外函数的临时变量,并且外函数返回内函数的引用,这样就构成了一个闭包。defouter():x,y,z=10,'abc',[1,2]definner():print(x,y)returninnerf=outer()print(f.__closure__)#celltuple......
  • Python 1-24 练习五 综合练习
    1、无重复字符的最长子串给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。#substr向右扩展一个元素,如果该元素已经在substr中,则需要将其及其前面的元素去掉。#可通过substr.index(c)定位元素或substr.split(c)[1]分割成子串#发现有重复字符时,可......
  • Python 2-05 高阶函数
    一、函数式编程函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。而函数式编程(请注意多了一个“式”字)——FunctionalProgrammi......
  • Python 3-11 异常处理
    异常处理一、错误和异常1、句法错误句法错误又称解析错误:>>>whileTrueprint('Helloworld')File"<stdin>",line1whileTrueprint('Helloworld')^SyntaxError:invalidsyntax解析器会复现出现句法错误的代码行,并用小“箭头”指向行里检测到的第一......
  • Python 05 Selenium 等待
    等待WebDriver通常可以说有一个阻塞API。因为它是一个指示浏览器做什么的进程外库,而且web平台本质上是异步的,所以WebDriver不跟踪DOM的实时活动状态。大多数由于使用Selenium和WebDriver而产生的间歇性问题都与浏览器和用户指令之间的竞争条件有关。例如,用户指示浏览......