我是靠谱客的博主 粗暴日记本,最近开发中收集的这篇文章主要介绍python 装饰器 java,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()

@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 "{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.

Hello Toby!

Hello Toby!

错误的函数签名和文档

装饰器装饰过的函数看上去名字没变,其实已经变了。

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:Python27libfunctools.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

https://www.cnblogs.com/yangliguo/category/1104728.html

最后

以上就是粗暴日记本为你收集整理的python 装饰器 java,Python 内置装饰器的全部内容,希望文章能够帮你解决python 装饰器 java,Python 内置装饰器所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部