1给函数添加一个包装

问题:给函数加一个外包装层,已添加额外的处理,例如,记录日志,计时统计等

解决方案:可以定义一个装饰器来包装函数

2编写装饰器时如何保存函数的元数据

问题:当一个函数被装饰器装饰时,一些重要的元数据比如:函数名、文档字符串、函数注解以及调用签名都丢失了
解决方案:每当定义一个装饰器时应该总是记得为底层的包装函数添加functools库中的@wraps装饰器
#问题:当一个函数被装饰器装饰时,一些重要的元数据比如:函数名、文档字符串、函数注解以及调用签名都丢失了

#解决方案:每当定义一个装饰器时应该总是记得为底层的包装函数添加functools库中的@wraps装饰器

import time
import functools
def timethis(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
start_time = time.time()
result = func(*args,**kwargs)
end_time = time.time()
print(func.__name__,end_time-start_time)
return result
return wrapper @timethis
def mysleep(num:int):
"""
原函数注释文档
:param num:
:return:
"""
time.sleep(num)
print("我是原函数") mysleep(3)
print(mysleep.__name__)
print(mysleep.__doc__)
print(mysleep.__annotations__) #如果装饰器使用@functools.wraps(func) 装饰,我们就可以使用下面的方法获取到原函数!!!
mysleep.__wrapped__(3)

3对装饰器进行解包装

问题: 我们已经把装饰器添加到函数上了,但是想撤销它,访问未经包装的原函数。

解决方案:假设装饰器已经实现了@warps(func),一般来说我们可以通过访问__wrapped__属性来获取到原函数

4定义一个可接收参数的装饰器

问题:我们想编写一个可接收参数的装饰器函数
解决方案:假设我们想编写一个为函数添加日志功能的装饰器,但是又允许用户指定日志的等级以及一些其他的细节操作作为参数。
import logging
import functools def logged(level,name=None,message=None):
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__ @functools.wraps(func)
def wrapper(*args,**kwargs):
log.log(level,message)
return func(*args,**kwargs)
return wrapper
return decorate

5定义一个属性可由用户修改的装饰器

问题:我们想编写一个装饰器来包装函数,但是可以让用户调整装饰器的属性,这样在运行时就能够控制装饰器的行为
from functools import wraps,partial
import logging def attach_wrapper(obj,func=None):
if func is None:
return partial(attach_wrapper,obj)
setattr(obj,func.__name__,func)
return func def logged(level,name=None,message=None):
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__ @wraps(func)
def wrapper(*args,**kwargs):
log.log(level,logmsg)
return func(*args,**kwargs) @attach_wrapper(wrapper)
def set_level(newlevel):
nonlocal level
level = newlevel @attach_wrapper(wrapper)
def set_message(newmsg):
nonlocal logmsg
logmsg = newmsg return wrapper
return decorate logging.basicConfig(level=logging.DEBUG)
@logged(logging.DEBUG)
def add(x,y):
return x + y add(2,5) add.set_message("Add called")
add(3,8)

6定义一个能接收可选参数的装饰器

问题:我们想编写一个单独的装饰器,使其既可以像@decorator 这样不带参数,也可以像@decorator(x,y,z)这样接收可选参数
from functools import wraps,partial
import logging def logged(func=None,*,level=logging.DEBUG,name=None,message=None):
if func is None:
return partial(logged,level=level,name=name,message=message) logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args,**kwargs):
log.log(level,logmsg)
return func(*args,**kwargs)
return wrapper @logged
def add(x,y):
logging.debug("hahahah")
return x+y #没有参数时,装饰器就相当于:logged(func),所以装饰器的第一个参数就是func,其他都是可选参数 @logged(level=logging.CRITICAL,name="example")
def spam():
print("spam!!!") #有参数时,装饰器就相当于logged(level=logging.DEBUG,name="example")(spam)
#巧妙的利用functools.partial 将构建好的方法返回 add(1,2)
spam()

7利用装饰器对函数参数强制执行类型检查

问题:我们想为函数参数添加强制类型检查功能,将其作为一种断言或者与调用者之间的契约
from inspect import signature
from functools import wraps def typeassert(*ty_args,**ty_kwargs): def decorate(func):
if not __debug__:
return func
sig = signature(func)#获取func的参数签名(x,y,z)
bound_types = sig.bind_partial(*ty_args,**ty_kwargs).arguments# 参数签名与类型参数做映射 [("x",<class "int">),("z",<class "int">)]
@wraps(func)
def wrapper(*args,**kwargs):
bound_values = sig.bind(*args,**kwargs).arguments# 参数签名与函数参数做映射
for name,value in bound_values.items():
if name in bound_types:#判断参数是否有类型限制
if not isinstance(value,bound_types[name]):
raise TypeError("Argument {} must be {}".format(name,bound_types[name]))
return func(*args,**kwargs)
return wrapper
return decorate class A():
def a(self):
print("a") @typeassert(int,A,z=int)
def add(x,y,z):
print(x,y,z)
return x add(1,A(),3) #想法:参数类型的限制可以使用在参数处理方法中,对前端接收的参数进行检查,也可以使用在一些需要限制传入参数类型的地方 #注:此装饰器一个微妙的地方,只检查传递的参数,如果是默认参数,没有进行传递,参数类型不进行检查 @typeassert(int,list)
def bar(x,items=None):
if items is None:
items = []
items.append(x)
return items print(bar(2))

8在类中定义装饰器

问题: 我们想在类中定义一个装饰器,并将其作用到其他函数或方法上
from functools import wraps

class A:
def decorator1(self,func):
@wraps(func)
def wrapper(*args,**kwargs):
print("decorator 1")
return func(*args,**kwargs)
return wrapper @classmethod
def decorator2(cls,func):
@wraps(func)
def wrapper(*args,**kwargs):
print("decorator 2")
return func(*args,**kwargs)
return wrapper #思考:@property 实际上是一个拥有 getter(),setter(),deleter()方法的类,每一个方法都可作为一个装饰器
#几个装饰器都可以操纵实例的状态,因此,如果需要装饰器在背后记录或合并信息,这是一个很明智的方法。

9把装饰器定义成类

问题: 我们想用装饰器来包装函数,但是希望得到的结果是一个可调用的实例。我们需要装饰器既能在类中工作,也可以在类外部使用

解决方案:要把装饰器定义成类实例,需要确保在类中实现__call__()和__get__()方法
import types
from functools import wraps class Profield:
def __init__(self,func):
wraps(func)(self)
self.ncalls = 0 def __call__(self, *args, **kwargs):
self.ncalls +=1
return self.__wrapped__(*args,**kwargs) def __get__(self, instance, cls):
if instance is None:
return self
else:
return types.MethodType(self,instance) #该装饰器相当于为函数添加一个属性 ncalls
10把装饰器作用到类和静态方法上
问题:我们想在类或者静态方法上应用装饰器

解决方案:将装饰器作用到类和静态方法上是简单而直接的,但是要保证装饰器在应用的时候需要放在@classmethod 和 @staticmethod 之前,示例如下:
import time
from functools import wraps def timethis(func):
@wraps(func)
def wrapper(*args,**kwargs):
start = time.time()
r = func(*args,**kwargs)
end = time.time()
print(end-start)
return r
return wrapper
@classmethod 和 @staticmethod 装饰器并不会返回一个可执行对象,所以装饰器都要放在他们下面!!!

11编写装饰器为被包装函数添加参数

问题:我们想编写一个装饰器,为被包装的函数添加额外的参数,但是添加的参数不能影响到该函数已有的调用约定

解决方案:
from functools import wraps

def optinoal_debug(func):
@wraps(func)
def wrapper(*args,debug=False,**kwargs):
if debug:
print("Calling",func.__name__)
return func(*args,**kwargs)
return wrapper
函数中的一部分参数被装饰器解析所用,剩下参数给到函数,可以用被包装函数的参数来控制装饰器的行为

12利用装饰器给函数定义打补丁

#问题:我们想检查或改写一部分类的定义,以此来修改类的行为,但是不想通过继承或者元类的方式来做

#解决方案:
def log_getattribute(cls):
orig_getattribute = cls.__getattribute__ def new_getattribute(self,name):
print("getting",name)
return orig_getattribute(self,name) cls.__getattribute__ = new_getattribute
return cls @log_getattribute
class A:
def __init__(self,x):
self.x = x def spam(self):
pass a = A(42)
a.x
可以通过此方法对类的属性做监控

cookbook_元编程的更多相关文章

  1. C++模板元编程(C++ template metaprogramming)

    实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...

  2. .Net元编程【Metaprogramming in NET】 序-翻译

    最近在看这本书,比较实用.抽点时间把公开的部分内容简单的翻译了一下,下文是序部分. 书的具体地址为: http://www.amazon.cn/Metaprogramming-in-NET-Hazza ...

  3. Effective C++ -----条款48:认识template元编程

    Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率. TMP可被用来生成“基于政策选择组合”(based on ...

  4. C++模板元编程 - 函数重载决议选择工具(不知道起什么好名)完成

    这个还是基于之前实现的那个MultiState,为了实现三种类型“大类”的函数重载决议:所有整数.所有浮点数.字符串,分别将这三种“大类”的数据分配到对应的Converter上. 为此实现了一些方便的 ...

  5. C++模板元编程 - 挖新坑的时候探索到了模板元编程的新玩法

    C++真是一门自由的语言,虽然糖没有C#那么多,但是你想要怎么写,想要实现什么,想要用某种编程范式或者语言特性,它都会提供. 开大数运算类的新坑的时候(又是坑),无意中需要解决一个需求:大数类需要分别 ...

  6. atitit.元编程总结 o99

    atitit.元编程总结 o99.doc 1. 元编程(Metaprogramming) 1 2. 元编程的历史and发展 1 3. 元类型and元数据 1 4. 元编程实现方式 2 4.1. 代码生 ...

  7. 读书笔记_Effective_C++_条款四十八:了解模板元编程

    作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...

  8. c++ 模板元编程的一点体会

    趁着国庆长假快速翻了一遍传说中的.大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身...论语言方面的深度,我看过的 c++ 书里大概只有 insi ...

  9. C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

    本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...

随机推荐

  1. log4net插入access自定义字段

    1.创建表格 2.创建log4net.xml,并设置属性始终复制,关键属性 <bufferSize value="1" /> <conversionPattern ...

  2. SQL Server 2012完全备份、差异备份、事务日志备份和还原操作;

    SQL Server 2012完全备份.差异备份.事务日志备份和还原操作: 1.首先,建立一个测试数据库,TestA:添加一张表,录入二条数据:备份操作这里我就不详细截图和讲解了.相信大家都会备份,我 ...

  3. Junit4使用详解一:测试失败的两种情况

    Junit4最佳实践 1.把测试文件夹和代码文件夹分离,这两者的代码互不干扰,代码目录和测试目录是并列的关系 2.Java代码 3.创建单元测试代码文件 4.运行测试代码  5.查看测试结果 现在的情 ...

  4. spring 5.x 系列第1篇 —— springmvc基础 (xml配置方式)

    文章目录 一.搭建hello spring工程 1.1 项目搭建 1.2 相关配置讲解 二.配置自定义拦截器 三.全局异常处理 四.参数绑定 4.1 参数绑定 4.2 关于日期格式转换的三种方法 五. ...

  5. NoSQL数据库兴起

    前言 近几年NoSQL数据库兴起,各种新的产品层出不穷,在此学习下NoSQL的基本理论,并认识下常见的NoSQL数据库. 一 NoSQL数据库兴起的原因 随着大数据技术兴起和Web2.0时代的到来.传 ...

  6. 理论+实践解析“IT治理”之模式与原则

    IT治理工作作为企业信息化建设的上层建筑,扮演着及其重要的角色.本文结合作者的学习及实践经验给出一些借鉴. 一.IT治理概述 1.1 何为IT治理 在企业信息化建设中的最大问题,往往不是技术问题,也不 ...

  7. 100天搞定机器学习|Day7 K-NN

    最近事情无比之多,换了工作.组队参加了一个比赛.和朋友搞了一些小项目,公号荒废许久.坚持是多么重要,又是多么艰难,目前事情都告一段落,我们继续100天搞定机器学习系列.想要继续做这个是因为,一方面在具 ...

  8. 好用的在线画图工具processon

    ProcessOn是一款基于SaaS的前沿.高效线上作图工具,它将Visio.Xmind等专业作图工具搬到了"云端" 注册链接:https://www.processon.com/ ...

  9. Gin 框架 - 安装和路由配置

    目录 概述 Gin 安装 路由配置 推荐阅读 概述 看下 Gin 框架的官方介绍: Gin 是一个用 Go (Golang) 编写的 web 框架. 它是一个类似于 martini 但拥有更好性能的 ...

  10. 使用flink Table &Sql api来构建批量和流式应用(2)Table API概述

    从flink的官方文档,我们知道flink的编程模型分为四层,sql层是最高层的api,Table api是中间层,DataStream/DataSet Api 是核心,stateful Stream ...