Python 内置装饰器
内置的装饰器
内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象,所以更难理解一些。
@property
在了解这个装饰器前,你需要知道在不使用装饰器怎么写一个属性。
def getWidth(self):
return self.__width
def setWidth(self, newwidth):
self.__width = newwidth
width = property(getWidth, setWidth)
以上就是一个Python属性的标准写法,其实和Java挺像的,但是太罗嗦。有了@语法糖,能达到一样的效果但看起来更简单。
@property
def width(self):
return self.__width
@width.setter
def width(self, newWidth):
self.__width = newWidth
属性有三个装饰器:setter, getter, deleter ,都是在property()的基础上做了一些封装,因为setter和deleter是property()的第二和第三个参数,不能直接套用@语法。getter装饰器和不带getter的属性装饰器效果是一样的,估计只是为了对称吧,本身没有任何存在的意义。经过@property装饰过的函数返回的不再是一个函数,而是一个property对象。
>>> property()
<property object at 0x10ff07940>
@staticmethod,@classmethod
有了@property装饰器的了解,这两个装饰器的原理是差不多的。@staticmethod返回的是一个staticmethod类对象,而@classmethod返回的是一个classmethod类对象。他们都是调用的是各自的__init__()构造函数。
class classmethod(object):
"""
classmethod(function) -> method
"""
def __init__(self, function): # for @classmethod decorator
pass
# ...
class staticmethod(object):
"""
staticmethod(function) -> method
"""
def __init__(self, function): # for @staticmethod decorator
pass
# ...
装饰器的@语法就等同调用了这两个类的构造函数
class Foo(object):
@staticmethod
def bar():
pass
# 等同于 bar = staticmethod(bar
装饰器里的缺点
装饰器可以让你代码更加优雅,减少重复,但也不全是优点,也会带来一些问题。
位置错误的代码
让我们直接看示例代码。
def html_tags(tag_name):
print 'begin outer function.'
def wrapper_(func):
print "begin of inner wrapper function."
def wrapper(*args, **kwargs):
content = func(*args, **kwargs)
print "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content)
print 'end of inner wrapper function.'
return wrapper
print 'end of outer function'
return wrapper_
@html_tags('b')
def hello(name='Toby'):
return 'Hello {}!'.format(name)
hello()
hello()
在装饰器中我在各个可能的位置都加上了print语句,用于记录被调用的情况。你知道他们最后打印出来的顺序吗?如果你心里没底,那么最好不要在装饰器函数之外添加逻辑功能,否则这个装饰器就不受你控制了。以下是输出结果:
begin outer function.
end of outer function
begin of inner wrapper function.
end of inner wrapper function.
<b>Hello Toby!</b>
<b>Hello Toby!</b>
错误的函数签名和文档
装饰器装饰过的函数看上去名字没变,其实已经变了。
def logging(func):
def wrapper(*args, **kwargs):
"""print log before a function."""
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
return wrapper
@logging
def say(something):
"""say something"""
print "say {}!".format(something)
print say.__name__ # wrapper
为什么会这样呢?只要你想想装饰器的语法糖@代替的东西就明白了。@等同于这样的写法。
say = logging(say)
logging其实返回的函数名字刚好是wrapper,那么上面的这个语句刚好就是把这个结果赋值给say,say的__name__自然也就是wrapper了,不仅仅是name,其他属性也都是来自wrapper,比如doc,source等等。
使用标准库里的functools.wraps,可以基本解决这个问题。
from functools import wraps
def logging(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""print log before a function."""
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
return wrapper
@logging
def say(something):
"""say something"""
print "say {}!".format(something)
print say.__name__ # say
print say.__doc__ # say something
看上去不错!主要问题解决了,但其实还不太完美。因为函数的签名和源码还是拿不到的。
import inspect
print inspect.getargspec(say) # failed
print inspect.getsource(say) # failed
如果要彻底解决这个问题可以借用第三方包,比如wrapt。
不能装饰@staticmethod或者 @classmethod
当你想把装饰器用在一个静态方法或者类方法时,不好意思,报错了。
class Car(object):
def __init__(self, model):
self.model = model
@logging # 装饰实例方法,OK
def run(self):
print "{} is running!".format(self.model)
@logging # 装饰静态方法,Failed
@staticmethod
def check_model_for(obj):
if isinstance(obj, Car):
print "The model of your car is {}".format(obj.model)
else:
print "{} is not a car!".format(obj)
"""
Traceback (most recent call last):
...
File "example_4.py", line 10, in logging
@wraps(func)
File "C:\Python27\lib\functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'staticmethod' object has no attribute '__module__'
"""
前面已经解释了@staticmethod这个装饰器,其实它返回的并不是一个callable对象,而是一个staticmethod对象,那么它是不符合装饰器要求的(比如传入一个callable对象),你自然不能在它之上再加别的装饰器。要解决这个问题很简单,只要把你的装饰器放在@staticmethod之前就好了,因为你的装饰器返回的还是一个正常的函数,然后再加上一个@staticmethod是不会出问题的。
class Car(object):
def __init__(self, model):
self.model = model
@staticmethod
@logging # 在@staticmethod之前装饰,OK
def check_model_for(obj):
pass
如何优化你的装饰器
嵌套的装饰函数不太直观,我们可以使用第三方包类改进这样的情况,让装饰器函数可读性更好。
decorator.py
decorator.py是一个非常简单的装饰器加强包。你可以很直观的先定义包装函数wrapper(),再使用decorate(func, wrapper)方法就可以完成一个装饰器。
from decorator import decorate
def wrapper(func, *args, **kwargs):
"""print log before a function."""
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
def logging(func):
return decorate(func, wrapper) # 用wrapper装饰func
你也可以使用它自带的@decorator装饰器来完成你的装饰器。
from decorator import decorator
@decorator
def logging(func, *args, **kwargs):
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
decorator.py实现的装饰器能完整保留原函数的name,doc和args,唯一有问题的就是inspect.getsource(func)返回的还是装饰器的源代码,你需要改成inspect.getsource(func.__wrapped__)。
wrapt
wrapt是一个功能非常完善的包,用于实现各种你想到或者你没想到的装饰器。使用wrapt实现的装饰器你不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至inspect.getsource(func)也准确无误。
import wrapt
# without argument in decorator
@wrapt.decorator
def logging(wrapped, instance, args, kwargs): # instance is must
print "[DEBUG]: enter {}()".format(wrapped.__name__)
return wrapped(*args, **kwargs)
@logging
def say(something): pass
使用wrapt你只需要定义一个装饰器函数,但是函数签名是固定的,必须是(wrapped, instance, args, kwargs),注意第二个参数instance是必须的,就算你不用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时你可以拿到这个类实例。根据instance的值你能够更加灵活的调整你的装饰器。另外,args和kwargs也是固定的,注意前面没有星号。在装饰器内部调用原函数时才带星号。
如果你需要使用wrapt写一个带参数的装饰器,可以这样写。
def logging(level):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print "[{}]: enter {}()".format(level, wrapped.__name__)
return wrapped(*args, **kwargs)
return wrapper
@logging(level="INFO")
def do(work): pass
Python 内置装饰器的更多相关文章
- python内置装饰器
前言 接着上一篇笔记,我们来看看内置装饰器property.staticmethod.classmethod 一.property装饰器 1. 普通方式修改属性值 code class Celsius ...
- Python内置装饰器@property
在<Python装饰器(Decorators )>一文中介绍了python装饰器的概念,日常写代码时有一个装饰器很常见,他就是内置的@property. 我们一步步的来接近这个概念. 一个 ...
- 面向对象之classmethod和staticmethod(python内置装饰器)
对象的绑定方法复习classmethodstaticmethod TOC 对象的绑定方法复习 由对象来调用 会将对象当做第一个参数传入 若对象的绑定方法中还有其他参数,会一并传入 classmetho ...
- python进阶04 装饰器、描述器、常用内置装饰器
python进阶04 装饰器.描述器.常用内置装饰器 一.装饰器 作用:能够给现有的函数增加功能 如何给一个现有的函数增加执行计数的功能 首先用类来添加新功能 def fun(): #首先我们定义一个 ...
- python基础语法16 面向对象3 组合,封装,访问限制机制,内置装饰器property
组合: 夺命三问: 1.什么是组合? 组合指的是一个对象中,包含另一个或多个对象. 2.为什么要用组合? 减少代码的冗余. 3.如何使用组合? 耦合度: 耦: 莲藕 ---> 藕断丝连 - 耦合 ...
- classmethod、staticclassmethod内置装饰器函数
# method 英文是方法的意思 # classmethod 类方法 # 当一个类中的方法中只涉及操作类的静态属性时,此时在逻辑上,我们想要直接通过类名就可以调用这个方法去修改类的静态属性,此时可以 ...
- property内置装饰器函数和@name.setter、@name.deleter
# property # 内置装饰器函数 只在面向对象中使用 # 装饰后效果:将类的方法伪装成属性 # 被property装饰后的方法,不能带除了self外的任何参数 from math import ...
- python基础--定义装饰器(内置装饰器)
装饰器的定义: 装饰器本质上就是一个python函数,它可以让其它函数在不需要做任何代码改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景中,比如-- >插入 ...
- python基础-内置装饰器classmethod和staticmethod
面向对象编程之classmethod和staticmethod classmethod 和 staticmethod都是python内置的装饰器 classmethod 的作用:给在类内部定义的方法装 ...
随机推荐
- MySQL 第八篇:ORM框架SQLAlchemy
一 介绍 SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取 ...
- where EXISTS (子查询)多对多中通过中间表查对方列表
用户表A,小组表B,小组和用户是多对多关系,中间有个中间表M 已知 小组 id 即teamId ,想知道这个小组中的用户列表信息,可以如下写sql: select * from A a where E ...
- HOJ 13102 Super Shuttle (圆的反演变换)
HOJ 13102 Super Shuttle 链接:http://49.123.82.55/online/?action=problem&type=show&id=13102 题意: ...
- python【文件操作:读写文件】
文件读写模式
- docker中实现服务日志轮转
问题背景 通常我们一个完整的应用镜像有两部分组成,一个是运行时环境,一个是应用程序.我们以php应用为例,一个完整的php应用需要包含openresty + php两个服务来配置运行时环境,然后再加上 ...
- NOIP2011 提高组 Day1
自测:8:27——11:51 实际得分:100+60+20=180 期望得分:100+60+40=200 T3读错题,失20 http://cogs.pro/cogs/page/page.php?ai ...
- js检测上传文件大小
前言: 项目中经常用到需要上传文件.照片等功能,同时需要限制所上传文件的大小.很多插件都会采用后台请求验证,前端Js校验比较少.本篇介绍一个前端JS便捷判断上传文件大小的方法. 代码很简单,关键就是怎 ...
- Gulp安装笔记
前言 总的来说,玩gulp的流程是这样的: 安装nodejs -> 全局安装gulp -> 项目安装gulp以及gulp插件 -> 配置gulpfile.js -> 运行任务 ...
- elasticsearch-head插件安装说明
下载及安装readme https://github.com/mobz/elasticsearch-head 安装: npm install npm run start 访问:http://local ...
- java学习第01天(程序开发体验)
1.基本写法 class Demo{ public static void main(String[] args){ System.out.print("Hello World") ...