前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python装饰器语法与应用#学习猿地

Python装饰器语法与应用#学习猿地

作者头像
学习猿地
发布2020-03-19 17:00:13
3200
发布2020-03-19 17:00:13
举报
文章被收录于专栏:学习猿地学习猿地

### 装饰器定义

**在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或者给类增加属性和方法)**

**核心思想**:用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或者新类)

**应用场景**:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等

**语法规则**:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖

#### 1.装饰器原型(闭包)

```python

# 1。 装饰器的原型

### 利用闭包,把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数

# 定义外函数,接收一个函数作为参数

def outer(f):

# 定义内函数,并且在内函数中调用了外函数的参数

def inner():

print('我是外函数中的内函数1')

f()

print('我是外函数中的内函数2')

return inner

# 定义普通函数

# def old():

# print('我是一个普通的函数')

#

# # old() # 作为普通函数直接调用

# old = outer(old) # outer返回了inner函数,赋值给了old

# old() # 此时再调用old函数时,等同于调用了 inner 函数

# 改为装饰器用法

@outer # 此处使用的@outer的语法就是把outer作为了装饰器,等同于 old = outer(old)

def old():

print('我是一个普通的函数')

old() # old函数经过 outer装饰器进行了装饰,代码和调用方法不变,但是函数的功能发送了改变

```

#### 2.装饰器的应用:统计函数的执行时间

```python

# 装饰器应用场景-统计函数执行时间

import time

# 定义一个统计函数执行时间的 装饰器

def runtime(f):

def inner():

start = time.perf_counter()

f()

end = time.perf_counter() - start

print(f'函数的调用执行时间为:{end}')

return inner

# 定义一个函数

@runtime

def func():

for i in range(5):

print(i,end=" ")

time.sleep(1)

func()

```

#### 3.装饰器嵌套语法

```python

# 1.定义装饰器

# 外函数

def outer(func):

#内函数

def inner():

print('找到妹子,成功拿到微信。。。3')

func() # 在内函数中调用外函数中的行参-函数

print('约妹子,看一场午夜电影。。。4')

# 在外函数中返回内函数

return inner

# 2。在定义一个装饰器

def kuozhan(f):

def kzinner():

print('扩展1')

f()

print('扩展2')

return kzinner

# 3. 装饰器的嵌套 先执行下面的,再执行上面的。

@kuozhan # 2。再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数

@outer # 1。先使用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数

def love():

print('跟妹子畅谈人生和理想。。。5')

love()

''' 结果和过程的解析

1 3 5 4 2

1 先使用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数

2 再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数

最后在调用love函数的时候是怎么执行的

love() == kzinner()

===> 1

===> inner()

===> 3

===> love() ===> 5

===> 4

===> 2

'''

```

#### 4.对带有参数的函数进行装饰

```python

# 定义装饰器

def outer(func):

# 如果装饰器带有参数的函数,需要在内函数中定义行参,并传递给调用的函数。因为调用原函数等于调用内函数

def inner(var):

print(f'找到{var}妹子,成功拿到微信。。')

func(var)

print(f'约{var}妹子,看一场午夜电影。。')

return inner

# 有参数的函数

@outer

def love(name):

print(f'跟{name}妹子畅谈人生。。。')

love('思思') #love() ==> inner() love('思思') ===> inner('思思')

```

#### 5.对多参数的函数进行装饰

```python

# 装饰带有多参数的函数

def outer(func):

def inner(who,name,*args,**kwargs):

print('约到妹子,聊微信。。。')

func(who,name,*args,**kwargs)

print('天色一晚,怎么办?')

return inner

# 定义多参数的 函数

@outer

def love(who,name,*args,**kwargs):

print(f'{who}跟{name}畅谈人生。。。')

print('完事去吃了好多美食',args)

print('看了一场电影',kwargs)

love('三多','思思','火锅','辣条','7块钱的麻辣烫',mov='唐山大地震')

'''

love() ==> inner()

love(...) ==> inner(...)

inner(...) ==> love(...)

'''

```

#### 6.带有参数的装饰器

> 你会遇到带有参数的装饰器,例如Django框架中的 @login_required(login_url='/accounts/login/')

```python

# 如果你的装饰器需要有参数,那么给当前的装饰器套一个壳,用于接收装饰器的参数

def kuozhan(var):

def outer(func):

def inner1():

print('妹子给了你微信')

func()

def inner2():

print('妹子给介绍了个大妈')

func()

# 装饰器壳的参数,可以用于在函数内去做流程控制

if var == 1:

return inner1

else:

return inner2

return outer

@kuozhan(2) # kuozhan(var) ==> outer() ==> outer(love) ==> inner()

def love():

print('谈谈人生。。。')

love()

```

#### 7.用类装饰器装饰函数

```python

# 类装饰器装饰函数

class Outer():

# 魔术方法:当把该类的对象当作函数调用时,自动触发 obj()

def __call__(self,func):

self.func = func # 把传进来的函数作为对象的成员方法

return self.inner # 返回一个函数

# 在定义的需要返回的新方法中 去进行装饰和处理

def inner(self,who):

print('拿到妹子的微信。。。')

self.func(who)

print('看一场午夜电影。。。')

@Outer() # Outer() ==> obj @obj==>obj(love) ==> __call__(love) ==> inner()

def love(who):

print(f'{who}和妹子谈谈人生和理想。。。')

love('川哥') # inner('川哥')

print(love) # 此时的 love就是属于Outer类这个对象中的inner方法

```

#### 8.用类方法装饰函数

```python

# 用类方法装饰函数

class Outer():

def newinner(func):

Outer.func = func # 把传递进来的函数定义为类方法

return Outer.inner # 同时返回一个新的类方法

def inner():

print('拿到妹子微信')

Outer.func()

print('看一场午夜电影')

@Outer.newinner # Outer.newinner(love) ==> Outer.inner

def love():

print('和妹子谈谈人生喝喝茶。。。')

love() # love() ==> Outer.inner()

```

到目前为止以上所以形式的装饰器,包括 函数装饰器,类装饰器,类方法装饰器,都有一个共同特点:都是在给函数去进行装饰,增加功能。

---

### 用装饰器装饰类

> 还有一种装饰器,是专门装饰类的。也就是在类的定义的前面使用@装饰器这种语法

> @装饰器

> class Demo():

> pass

> 装饰器给函数进行装饰,目的是不改变函数调用和代码的情况下给原函数增加了新的功能。

> 装饰器给类进行装饰,目的是不改变类的定义和调用的情况下给类增加新的成员(属性或方法)。

#### 9.用函数装饰器装饰类

```python

# 使用函数装饰器,给类进行装饰,增加新的属性和方法

# 定义函数,接收一个类。返回修改后的类

def kuozhan(cls):

def func2():

print('我是在装饰器中追加的新方法,func2')

cls.func2 = func2 # 把刚才定义的方法赋值给 类

cls.name = '我是在装饰器中追加的新属性 name'

#返回时,把追加类新成员的 类 返回去

return cls

@kuozhan # kuozhan(Demo) ==> cls ==> Demo

class Demo():

def func():

print('我是Demo类中定义的func方法')

Demo.func() # 此时在调用的Demo类是通过装饰器,更新过的Demo类

Demo.func2()

print(Demo.name)

```

#### 10.使用类装饰器装饰类

```python

class KuoZhan():

def __call__(self, cls):

# 把接收的类,赋值给当前对象,作为一个属性

self.cls = cls

# 返回一个函数

return self.newfunc

def newfunc(self):

self.cls.name = '我是在类装饰器中追加的新属性 name'

self.cls.func2 = self.func2

# 返回传递进来的类的实例化结果,obj

return self.cls()

def func2(self):

print('我是在类装饰器中追加的新方法 func2')

@KuoZhan() # KuoZhan() ==> obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc

class Demo():

def func(self):

print('我是Demo类中定义的func方法')

obj = Demo() # Demo() ==> newfunc() ==> obj

obj.func()

obj.func2()

print(obj.name)

# 思考: 此时的 obj这个对象,是哪个类的对象。Demo还是KuoZhan

print(obj) # 此时的obj依然是Demo类的实例化对象,只不过经过装饰后,增加了新的属性和方法

```

作业:如何用一个装饰器,装饰带有返回值的函数

掌握学习方法,不如会弯道超车!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档