Python各种类型装饰器详细介绍
目录
- 装饰器说明
- 装饰器分类
- 最简单的装饰器
- 用于修改对象的装饰器
- 用于模拟对象的装饰器--函数装饰器
- 用于模拟对象的装饰器--类方法装饰器
- 用于模拟对象的装饰器--类装饰器
- 特殊应用的装饰器
- 类实现的装饰器
- 装饰带参数/返回值的对象
- 装饰器带参数
- 装饰器应用
装饰器说明
python中的装饰器是一种可以装饰其它对象的工具。该工具本质上是一个可调用的对象(callable),所以装饰器一般可以由函数、类来实现。装饰器本身需要接受一个被装饰的对象作为参数,该参数通常为函数、方法、类等对象。装饰器需要返回一个对象,该对象可以是 经过处理的原参数对象、一个包装且类似原参数的对象;或者返回一个不相干内容(通常不建议使用)
相信通过上述一段文字的描述,大家应该更加的迷惑了!所以下面我们就结编程客栈合代码来理解Python中的装饰器。
装饰器分类
最简单的装饰器
def warp(obj): return obj
没错!!!这就是最简单的装饰器,并且是一个没有任何用处的装饰器。但是它确实是一个装饰器,并且可以用的很好。比如:
@warp # 等价于 foo = warp(foo) def foo(): print('hello decorator!') foo() # => hello decorator!
而上面使用了装饰器的代码,其实我们可以通过其它方式达到相同的效果。具体见下:
def foo(): print('hello decorator!') foo = warp(foo) foo() # => hello decorator!
So,通过最简单的代码,我们可以发现装饰器其实就是接受了一个函数(对象),并且返回了一个函数(对象)的函数(可调用对象)。
用于修改对象的装饰器
在理解了装饰器的含义之后,再来看一个稍微有点作用的装饰器。代码如下:
def warp(obj): obj.name = 'python' return obj
这个装饰器在上一个例子的基础上,只添加了一行代码,但是却有了实际的作用。它的作用就是给被装饰的对象,添加一个name属性并且设置值为python。这个装饰器的使用效果如下:
@warp # => Bar = warp(Bar) class Bar(object): def __init__(self): pass print(Bar.name) # => python
可以看到实际的使用过程中,warp装饰器已经成功的给Bar对象添加了name属性。除了给类对象添加属性之外,它还可以给函数对象添加属性。
@warp # => foo = warp(foo) def foo(): pass print(foo.name) # => python
用于模拟对象的装饰器--函数装饰器
上面例子中的装饰器,是直接修改了传入对象;而装饰器最常用的方式却是模拟一个传入对象。即返回一个和原对象相似的对象(即调用接口完全一样的另一个对象),并且该模拟对象是包装了原对象在内的。具体代码如下:
def outer(func): # 函数装饰器 def inner(): func() return inner
上面是一个函数装饰器,即用来修饰函数的装饰器。因为它返回了一个模拟func对象的inner对象。而这里inner对象是一个函数,所以这个装饰器只能装饰函数。(因为inner对象只能模拟func这样的函数对象,不能模拟class对象)
@outer # foo = outer(foo) def foo(): print('hello foo') foo() # => hello foo
上述代码中最后一行foo(),其实质上是执行的inner()。为了证明这一点,我们可以在inner中打印一条信息。并查看下foo的__name__属性。
def outer(func): # 函数装饰器 def inner(): print('hello inner') func() return inner @outer # foo = outer(foo) def foo(): print('hello foo') print(foo.__name__) foo()
上述代码执行后的结果如下:
inner hello inner hello foo
可以看到首先打印的是 foo.__name__代码,注意内容是inner而不是foo(说明其本质上是inner函数);其次打印的时候,先打印inner函数中的内容,后打印foo函数中的内容。
用于模拟对象的装饰器--类方法装饰器
与函数装饰器类似的还有类方法装饰器,其作用相同,格式相近。只是些微有些区别,下面就是类方法装饰器的代码。
def outer(obj): # 类方法装饰器 def inner(self): print('hello inner') obj(self) return inner class Zoo(object): def __init__(self): pass @outer # => zoo = outer(zoo) def zoo(self): print('hello zoo') zoo = Zoo() print(zoo.zoo.__name__) zoo.zoo()
可以看到类方法装饰器和函数装饰器,唯一的区别就是多了一个默认的self参数;这是因为类方法本身就比函数多这么一个参数。其执行的结果如下:
inner hello inner hello zoo
所以最后一行代码zoo.zoo函数执行的其实是inner函数。
用于模拟对象的装饰器--类装饰器
装饰器除了可以装饰函数、方法之外,还可以装饰器类对象。具体的代码如下编程客栈:
def outer(clss): # 类装饰器 class Inner(object): def __init__(self): self.clss = clss() def __getattr__(self, attr): return getattr(self.clss, attr) return Inner @outer # Zoo = outer(Zoo) class Zoo(object): def __init__(self): pass def say(self): print('hello world!') zoo = Zoo() print(zoo.__class__) # <class '__main__.outer.<locals>.Inner'> zoo.say() # hello world!
通过代码可以看出,类装饰器与函数装饰器类似。即模拟一个与原参数接口一致的类对象。所以对于模拟类的装饰器,只能用在其可以模拟的对象之上,并不能互相修饰其它类型的对象。
特殊应用的装饰器
上面都是比较常规的装饰器,python中还有另外一些特殊的装饰器。比如:类静态属性装饰器。比如下面的代码:
class Foo(object): def __init__(self, height, weigth): self.height = height self.weigth = weigth @property def ratio(self): return self.height / self.weigth foo = Foo(176, 120) print(foo.ratio) # => 1.4666666666666666
上述代码中的@property装饰器就是一个特殊的装饰器,它把ratio方法变成了一个属www.cppcns.com性。从最后一句调用代码可以看出,使用的是foo.ratio而不是foo.ratio()。
对于这类装饰器需要Python的特定属性和机制的支持才可以实现,不同特性的装饰器所需机制不同。如上述代码中的@property装饰器就可以使用下面的代码来实现。
class Prop(object): def __init__(self, fget): self.fget = fget def __get__(self, instance, owner): return self.fget(instance)
具体的使用效果如下:
class Foo(object): def __init__(self, height, weigth): self.height = height self.weigth = weigth @Prop def ratio(self): return self.height / self.weigth foo = Foo(176, 120) print(foo.ratio) # => 1.466666666666liRnvq6666
可以看到效果和原生的@property装饰器是一样的。
类实现的装饰器
在之前对于装饰器的说明中,有说道装饰器是一个callable对象。除了函数可以实现装饰器之外,还可以通过类来实现。那么类实现装饰器的具体代码如下:
class Warp(object): def __init__(self): pass def __call__(self, obj): obj.name = 'warp' return obj
这个类装饰器实现的功能,也是给传入的对象添加name属性,并设置其值为warp。其调用效果如下:
@Warp() def foo(): pass print(foo.name) # => warp
装饰带参数/返回值的对象
前面列举的所有例子,被装饰的对象都是无参数的。如果你需要装饰一个带参数的对象。那么就需要响应的修改下装饰器代码了。注意:这里特指那些模拟类型的装饰器。即函数装饰器、类方法装饰器、类装饰器。
假设我们先有一个带参数的函数,其内容如下:
def add(liRnvqx, y): return x * y
如果使用原来的函数装饰器,肯定就会出错。主要因为这个函数带参数,并且也有返回值。而原来的函数装饰器则不能支持,原函数装饰器如下:
def outer(func): # 函数装饰器 def inner(): func() return inner
可以看到inner模拟的仅仅是一个无参数、无返回值的对象。所以需要进行如下的修改:
def outer(func): # 函数装饰器 def inner(x, y): print('hello inner') return func(x, y) return inner
这样的函数装饰器就可以装饰add函数了。因为inner函数添加了x,y参数,调用func对象时也添加了参数,并且返回了func对象的返回值。具体使用效果如下:
@outer def add(x, y): return x * y print(add(2, 3)) # => 6
上述代码虽然可以实现add的装饰功能,但是如果现在我们在出现一个三个参数的函数需要装饰,或者一个带默认值参数的韩式需要装饰怎么办。我们不可能为没一个不同参数的函数都写一个相同功能的装饰器。所以终极的函数装饰器的写法如下:
def outer(func): # 函数装饰器 def inner(*args, **kwargs): print('hello inner') return func(*args, **kwargs) return inner
这里使用了python中动态参数的概念,这样装饰器就可以支持任意的组合参数的函数了。
装饰器带参数
上面说到的是被修饰的对象带参数的情况,还有一种情况就是装饰器本身希望支持带参数。这种情况类似于函数模块通过带参数可以更加灵活的道理一样。通过给装饰器带上参数,可以使得装饰器的功能更加的灵活。代码如下:
url_mapping = {} def route(url): def decorator(func): # 函数装饰器 url_mapping[url] = func return func return decorator
上面是一个URL路由映射的装饰器,可以给不同的函数绑定不同的路由。如果装饰器不能带参数,则无法实现这样的功能。其使用效果如下:
@route('/home') def home(): pass @route('/index') def index(): pass print(url_mapping) # => {'/home': <function home at 0x01DAD810>, '/index': <function index at 0x01DAD7C8>}
装饰器应用
Python装饰器的应用比较广泛,大部分场景的公共处理逻辑都可以使用装饰器去简化。(使用上类似于java中的注解)一般比较常见的场景比如:
日志记录
权限验证单
例模式竞争
资源管理
到此这篇关于Python各种类型装饰器详细介绍的文章就介绍到这了,更多相关Python装饰器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
精彩评论