cookbook_元编程
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
问题:我们想在类或者静态方法上应用装饰器 解决方案:将装饰器作用到类和静态方法上是简单而直接的,但是要保证装饰器在应用的时候需要放在@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_元编程的更多相关文章
- C++模板元编程(C++ template metaprogramming)
实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...
- .Net元编程【Metaprogramming in NET】 序-翻译
最近在看这本书,比较实用.抽点时间把公开的部分内容简单的翻译了一下,下文是序部分. 书的具体地址为: http://www.amazon.cn/Metaprogramming-in-NET-Hazza ...
- Effective C++ -----条款48:认识template元编程
Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率. TMP可被用来生成“基于政策选择组合”(based on ...
- C++模板元编程 - 函数重载决议选择工具(不知道起什么好名)完成
这个还是基于之前实现的那个MultiState,为了实现三种类型“大类”的函数重载决议:所有整数.所有浮点数.字符串,分别将这三种“大类”的数据分配到对应的Converter上. 为此实现了一些方便的 ...
- C++模板元编程 - 挖新坑的时候探索到了模板元编程的新玩法
C++真是一门自由的语言,虽然糖没有C#那么多,但是你想要怎么写,想要实现什么,想要用某种编程范式或者语言特性,它都会提供. 开大数运算类的新坑的时候(又是坑),无意中需要解决一个需求:大数类需要分别 ...
- atitit.元编程总结 o99
atitit.元编程总结 o99.doc 1. 元编程(Metaprogramming) 1 2. 元编程的历史and发展 1 3. 元类型and元数据 1 4. 元编程实现方式 2 4.1. 代码生 ...
- 读书笔记_Effective_C++_条款四十八:了解模板元编程
作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...
- c++ 模板元编程的一点体会
趁着国庆长假快速翻了一遍传说中的.大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身...论语言方面的深度,我看过的 c++ 书里大概只有 insi ...
- C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE
本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的 ...
随机推荐
- flask(四)
1.Flask-Session from flask import session,Flask from flask_session import Session #导入 from redis imp ...
- 解决npm install卡住不动的小尴尬
npm install卡顿问题记录 遇到的问题 npm install -g @angular/cli 安装angular cli工具时,发现进度条一直卡住不动,相信很多朋友也遇到过.原因应该是国内的 ...
- 报错:java.sql.SQLException: The server
报错:java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized 在IDEA运行是报出例如相识的错误时: ...
- css之rem布局
rem介绍和原理网上都是,这里不具体介绍 以iphone6设计稿 let htmlWidth = document.documentElement.clientWidth || document.bo ...
- vue的懒加载如何实现?
个人通过查找发现一个比较好用的模块,vue-lazyload 第一步 下载安装这个包 npm install vue-lazyload 第二步 在main.js中引入这个模块 import Vu ...
- js深入(三)作用域链与闭包
在之前我们根绝对象的原型说过了js的原型链,那么同样的js 万物皆对象,函数也同样存在这么一个链式的关系,就是函数的作用域链 作用域链 首先先来回顾一下之前讲到的原型链的寻找机制,就是实例会先从本身开 ...
- 使用MVVM的常见误区(1)在ViewModel中和用户交互
缺点,不能进行单元测试 比如,用户在界面点击按钮,实现用户选择一个文件,然后对文件内容进行解析.常见错误如下 using Microsoft.Win32; namespace View和ViewMod ...
- HDU 6207:Apple(Java高精度)
题目链接 题意 给出三个圆上的点,和一个目标的点,问目标点是否在这三个点构成的圆外面. 思路 许久没见过的Java高精度,不要加package!!! import java.math.BigDecim ...
- Codeforces 730A:Toda 2(multiset模拟)
http://codeforces.com/problemset/problem/730/A 题意:有n个人打天梯,想让这n个人的分数相同,每场比赛必须有2-5个人参赛,参赛的人会降低一分,问一个合理 ...
- MySQL数据库设计与开发规范
目录 1. 规范背景与目的 2. 设计规范 2.1. 数据库设计 2.1.1. 库名 2.1.2. 表结构 2.1.3. 列数据类型优化 2.1.4. 索引设计 2.1.5. 分库分表.分区表 2.1 ...