函数 -> 装饰器

  函数的4个核心概念

  1.函数可以赋与变量

def func(message):
print('Got a message: {}'.format(message)) send_message = func
send_message('hello world')
#输出
#Got a message: hello world

  2.函数可以当作函数的参数

def get_message(message):
return 'Got a message: ' + message def root_call(func, message):
print(func(message)) root_call(get_message, 'hello world')
输出
#Got a message: hello world

  3.函数里嵌套函数

def func(message):
def get_message(message):
print('Got a message: {}'.format(message))
return get_message(message) func('hello world')
输出
#Got a message: hello world

  4.函数作为函数返回值(闭包)

def func_closure():
def get_message(message):
print('Got a message: {}'.format(message))
return get_message send_message = func_closure()
send_message('hello world')
#输出
#Got a message: hello world
 

简单装饰器

  例

def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper def greet():
print('hello world') greet = my_decorator(greet)
greet()

  使用语法糖 @

def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper @my_decorator
def greet():
print('hello world') greet()
# 输出
# wrapper of decorator
# hello world

带有参数的装饰器

  直接在 wrapper函数中加上参数
def my_decorator(func):
def wrapper(message):
print('wrapper of decorator')
func(message)
return wrapper @my_decorator #相当于 greet == wrapper(message)
def greet(message):
print(message) greet('hello world')
# 输出
#wrapper of decorator
#hello world

  这个装饰器只能用在有一个参数的函数,如果想对任意参数的函数通用,可以这么写

def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper

带自定义参数的装饰器

  利用装饰器自定义参数这特性,实现重复执行装饰器内部函数
def repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
return my_decorator @repeat(4)
def greet(message):
print(message) greet('hello world') # 输出:
# wrapper of decorator
# hello world
# wrapper of decorator
# hello world
# wrapper of decorator
# hello world
# wrapper of decorator
# hello world

原函数还是原函数?

greet.__name__
#输出
'wrapper' help(greet)
# 输出
Help on function wrapper in module __main__: wrapper(*args, **kwargs)

  可以看出,原函数的原信息会被wrapper取代

  如果不想其改变,那么可用内置装饰器@functools.wraps将原函数的元信息拷贝过去。
import functools

def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper @my_decorator
def greet(message):
print(message) greet.__name__ # 输出
#'greet'

类装饰器

  类装饰器主要依赖于 __call__()函数,每当调用类实例时,__call__()函数会被执行一次
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0 def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs) @Count
def example():
print("hello world") example() # 输出
# num of calls is: 1
# hello world example() # 输出
# num of calls is: 2
# hello world

装饰器的嵌套

@decorator1
@decorator2
@decorator3
def func():
...
#相当于 decorator1(decorator2(decorator3(func)))
import functools

def my_decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator1')
func(*args, **kwargs)
return wrapper def my_decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('execute decorator2')
func(*args, **kwargs)
return wrapper @my_decorator1
@my_decorator2
def greet(message):
print(message) greet('hello world') # 输出
# execute decorator1
# execute decorator2
# hello world

装饰器的实例用法

  1)身份验证,不登录不允许操作

import functools

def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
if check_user_logged_in(request): # 如果用户处于登录状态
return func(*args, **kwargs) # 执行函数 post_comment()
else:
raise Exception('Authentication failed')
return wrapper @authenticate
def post_comment(request, ...)
...

  2)日志记录 可测试函数的执行时间

import time
import functools def log_execution_time(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
return res
return wrapper @log_execution_time
def calculate_similarity(items):
...

  3) 合法性检测

import functools

def validation_check(input):
@functools.wraps(func)
def wrapper(*args, **kwargs):
... # 检查输入是否合法 @validation_check
def neural_network_training(param1, param2, ...):
... LRU cache. @lru_cache缓存装饰器
@lru_cache
def check(param1, param2, ...) # 检查用户设备类型,版本号等等
...

  4) try...excaption

class ServerDebugHelper:
@classmethod
def debug(cls):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
import traceback
print(traceback.format_exc())
return wrapper
return decorator

  

参考

  极客时间《Python核心技术与实战》专栏

Python进阶: Decorator 装饰器你太美的更多相关文章

  1. python进阶04 装饰器、描述器、常用内置装饰器

    python进阶04 装饰器.描述器.常用内置装饰器 一.装饰器 作用:能够给现有的函数增加功能 如何给一个现有的函数增加执行计数的功能 首先用类来添加新功能 def fun(): #首先我们定义一个 ...

  2. Python进阶(六)----装饰器

    Python进阶(六)----装饰器 一丶开放封闭原则 开放原则: ​ 增加一些额外的新功能 封闭原则: ​ 不改变源码.以及调用方式 二丶初识装饰器 装饰器: ​ 也可称装饰器函数,诠释开放封闭原则 ...

  3. Python进阶之装饰器

    函数也是对象 要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用.既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一 ...

  4. 项目解析1、登录验证用户是否存在 储备知识 Python 之 decorator装饰器

    下面是我对 装饰器 这一小节的总结, 以及自己的理解. 注:[本文中的代码参考上述教程] 很多时候我会把Python的很多语法与C++相融合,在C++中,函数的名称即为函数的地址,我们可以通过定义成为 ...

  5. python进阶(三)~~~装饰器和闭包

    一.闭包 满足条件: 1. 函数内嵌套一个函数: 2.外层函数的返回值是内层函数的函数名: 3.内层嵌套函数对外部作用域有一个非全局变量的引用: def func(): print("=== ...

  6. [Python进阶]002.装饰器(1)

    装饰器(1) 介绍 HelloWorld 需求 使用函数式编程 加入装饰器 解析 介绍 Python的装饰器叫Decorator,就是对一个模块做装饰. 作用: 为已存在的对象添加额外功能. 与Jav ...

  7. Python进阶(装饰器)

    from datetime import datetime def log(func):#func表示装饰器作用于的函数 def wrapper(*args,**kw):#wrapper返回装饰器作用 ...

  8. python进阶:装饰器

    1.闭包 简单理解:闭包就是多层函数的嵌套,外层函数的返回值是内层函数的引用. def out_func(n): num = 100 def in_fucn(*args,**kwargs): # no ...

  9. Python进阶之decorator装饰器

    decorator装饰器 .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB&quo ...

随机推荐

  1. Authentication failure. Retrying - 彻底解决vagrant up时警告

    碰到的问题 使用vagrant启动虚拟机时,出现如下警告: vagrant up default: Warning: Authentication failure. Retrying... 原因分析 ...

  2. flink 读JDQ和写JDQ的流程

    ReadFromJDQ3 1)消费JDQ的必要信息,通过参数传入,有6个参数 2)获取flink JDQ3的鉴权客户端 3)根据鉴权客户端获取消费属性的配置 4)构建应用环境ENV和checkpoin ...

  3. vue-使用keepAlive对上下拉刷新列表数据 和 滚动位置细节进行处理

    [前言] 使用vue处理项目中遇到列表页面时,有一些细节需要注意,这里总结一下,以便优化以后的代码.如下: 1. 使用mint-ui中的LoadMore组件上下拉刷新时,有时无法触发上拉加载更多的方法 ...

  4. input标签在谷歌浏览器记住密码的自动填充问题

    //使用autocomplete="new-password" <Input type='password' autocomplete="new-password& ...

  5. C++中函数参数传递(非引用形参和引用形参)——学习笔记

    1.非引用形参-copy 指针是非引用,copy.但值也会改变. 引用形参: const相关问题: const形参可以拷贝. 不能修改,不能赋值! 可以使用const初始化非const: 指针+con ...

  6. openresty开发系列2--nginx的简单安装,正向、反向代理及常用命令和信号控制介绍

    openresty开发系列2--nginx的简单安装,正向.反向代理及常用命令和信号控制介绍 一.nginx的安装下载及编译安装1.Nginx下载:nginx-1.13.0.tar.gz,下载到:/u ...

  7. ISO/IEC 9899:2011 条款6.5.16——赋值操作符

    6.5.16 赋值操作符 语法 1.assignment-expression: conditional-expression unary-expression    assignment-opera ...

  8. spark sql插入表时的文件个数研究

    spark sql执行insert overwrite table时,写到新表或者新分区的文件个数,有可能是200个,也有可能是任意个,为什么会有这种差别? 首先看一下spark sql执行inser ...

  9. linux后台启动springboot并指定日志文件名称

    如果用nohup启动jar包的话,默认的日志文件就是nohup.out,那么如果启动多个jar包的话,看日志文件就麻烦了,因为他们都会写入到nohup.out文件中. 所以我们来指定一下不同jar包的 ...

  10. linux非root用户安装nginx

    先到官网http://nginx.org/en/download.html下载最新稳定版源码包,目前是1.16.1: 下完后通过rz上传至wlf用户soft目录下,退回上一级目录解压: $ cd so ...