python 的类装饰器
我们都知道python的函数有装饰器,那么类也有装饰器吗?有的,为什么没有呢,来看下代码吧
def out(args):
def inner(cls):
cls._args = args
return cls
return inner class Student:
pass print(Student.__dict__)
Student.name = "ALICE"
print(Student.__dict__)
###来看下执行结果###
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None, 'name': 'ALICE'}
我们定义了一个空类Student,里面没有任何属性,第一次打印类的dict属性时候大家看到是没有任何属性的,只有类的特殊属性
然后我们增加了一个name属性,然后再打印一次dict属性,就看到有一个常规的name属性,属性值是ALICE
然后看最上面的函数,这个函数是个装饰函数,out函数接收常规str参数,当然不限制类型,你也可以传入int参数等等。
Inner函数的参数值是cls,也就是一个类,我们把类当做一个参数传进去,既然函数装饰器都可以把函数当做 参数传进去,类也可以当做参数传进去,在python里万物皆对象,只要是对象就是可以被传入的
cls._args = args 这里就是给这个类增加一个新属性,新属性是_args 并且值是形参args的实参值
然后最重要的来了,必须要有 return cls 不然的话下面的类的调用就要出问题了,一会我们测试下,因为给类增加新的属性后,一定要返回类,具体为什么,我们一会测试下就明白了
最后我们使用装饰器的@方式来装饰类,我们来看下代码与执行结果
def out(args):
def inner(cls):
cls._args = args
return cls
return inner
@out("TOM") ##=>>这里必须要带上args的实参
class Student:
pass print(Student.__dict__)
Student.name = "ALICE"
print(Student.__dict__)
####执行结果如下#####
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None, '_args': 'TOM'}
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None, '_args': 'TOM', 'name': 'ALICE'}
我们可以看到,新增了_args属性,并且属性值是TOM。
我们来看下是否是在python内部新建了一个类?
def out(args):
def inner(cls):
cls._args = args
print("The new class id is {}".format(id(cls)))
return cls
return inner
@out("TOM")
class Student:
pass print("The old class id is {}".format(id(Student)))
foo = out("TOM")
foo(Student) ###我们返回每个类的ID#####
The new class id is 32509208
The old class id is 32509208
The new class id is 32509208
ID值完全相同,看来在内部并没有创建一个新类,只是装饰器给其增加了一个属性
我们来测试下,在装饰器函数内部如果不返回类也就是cls呢?
def out(args):
def inner(cls):
cls._args = args
print("The new class id is {}".format(id(cls)))
#return cls
return inner
@out("TOM")
class Student:
pass #print("The old class id is {}".format(id(Student)))
#foo = out("TOM")
#foo(Student)
print(Student.__dict__)
###看下执行结果####
Traceback (most recent call last):
The new class id is 7146776
File "E:/python_learn/test1.py", line 15, in <module>
print(Student.__dict__)
AttributeError: 'NoneType' object has no attribute '__dict__'
为什么会是NoneType呢?因为在inner函数里没有返回值,所以是空类型,所以不能调用类的任何属性
看下面代码就明白了
def out(args):
def inner(cls):
cls._args = args
print("The new class id is {}".format(id(cls)))
return cls
return inner
#@out("TOM")
class Student:
pass foo = out("TOM")
print(id(foo(Student)))
print(foo(Student).__dict__)
###看下结果###
The new class id is 32967960
32967960
The new class id is 32967960
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None, '_args': 'TOM'}
我们注释掉了@调用
直接以赋值的方式来调用
首先定义foo = out("TOM") ,此时foo的值是inner函数
print(foo(Student)) 这时是打印inner函数的返回值,如果inner函数没有return的话,那么inner函数的返回值就是None,还记得吗?如果函数不写return那么默认返回就是空None,这也就是为什么上面代码会报NoneType error 了。
在inner函数里,我们把Student类以参数方式传入,再用return返回Student类也就是形参cls,如果不返回的话,下面装饰调用就无法调用到了,调用过程和装饰器一样,所以必须有return cls
def out(args):
def inner(cls):
cls._args = args
print("The new class id is {}".format(id(cls)))
#return cls
return inner
#@out("TOM")
class Student:
pass foo = out("TOM")
print(id(foo(Student)))
print(foo(Student).__dict__) ###注释掉return,一样的报错###
The new class id is 32247064
1577322688
The new class id is 32247064
Traceback (most recent call last):
File "E:/python_learn/test1.py", line 13, in <module>
print(foo(Student).__dict__)
AttributeError: 'NoneType' object has no attribute '__dict__'
然后下面的调用方式是不会出错的
def out(args):
def inner(cls):
cls._args = args
print("The new class id is {}".format(id(cls)))
#return cls
return inner
#@out("TOM")
class Student:
pass foo = out("TOM")
print(id(foo(Student)))
print(foo(Student))
#####来看下结果########
The new class id is 37621016
1577322688
The new class id is 37621016
None
看到了有个None了吗?那是inner函数的默认返回值,这样调用是不会出错的,因为你没调用特殊属性啊,比如__dict__属性,空类型一调用肯定出错啊,所以这里不调用就没事了
return cls 的作用是,你传入的cls参数是什么类型,它给你返回的也是什么类型,只不过你传入的参数类型是个类,返回的是个增加了一个新属性的类而已
可以测试下
def out(args):
def inner(cls):
cls._args = args
print("The new class id is {}".format(id(cls)))
print(type(cls))
return cls
return inner
#@out("TOM")
class Student:
pass foo = out("TOM")
print(id(foo(Student)))
print(foo(Student))
####看下执行结果###
The new class id is 37883160
<class 'type'>
37883160
The new class id is 37883160
<class 'type'>
<class '__main__.Student'>
###看到了吧,类型为class###
def out(args):
def inner(cls):
#cls._args = args
print("The new class id is {}".format(id(cls)))
print(type(cls))
return cls
return inner
#@out("TOM")
class Student:
pass foo = out("TOM")
print(id(foo("tools")))
#####传入的是str那么返回的也是str###
The new class id is 32593504
<class 'str'>
32593504
总的来说,多实践出真知,才能明白其中的道理
在函数装饰器里,如果不返回任何值是不会报错的
def out(fn):
def inner(args):
print("这个是个装饰器,是用来装饰其他函数用的")
ret = fn(args)
print("******************")
#return ret
return inner #@out
def test(name):
print("这个是fn函数,是被装饰的")
return name
#print(test("Bob"))
foo = out(test)
print(foo("JOke"))
####来看下结果####
这个是个装饰器,是用来装饰其他函数用的
这个是fn函数,是被装饰的
******************
None
下面也一样
def out(fn):
def inner(args):
print("这个是个装饰器,是用来装饰其他函数用的")
ret = fn(args)
print("******************")
#return ret
return inner @out
def test(name):
print("这个是fn函数,是被装饰的")
return name
print(test("SBB"))
############### 这个是个装饰器,是用来装饰其他函数用的
这个是fn函数,是被装饰的
******************
None
具体为什么,很简单,打印的是inner函数,只要inner函数是正确的,有没有返回值是无所谓的。
python 的类装饰器的更多相关文章
- python 描述符 上下文管理协议 类装饰器 property metaclass
1.描述符 #!/usr/bin/python env # coding=utf-8 # 数据描述符__get__ __set__ __delete__ ''' 描述符总结 描述符是可以实现大部分py ...
- 详解Python闭包,装饰器及类装饰器
在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...
- 面向切面编程AOP——加锁、cache、logging、trace、同步等这些较通用的操作,如果都写一个类,则每个用到这些功能的类使用多继承非常难看,AOP就是解决这个问题的,python AOP就是装饰器
面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同.面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用. 举两个大家都接触过的AOP的例子: 1)java中myba ...
- python高级 之(二) --- 类装饰器
装饰器-初级 在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能.使代码可读性更高.结构更加清晰.冗余度更低 简介 """ 闭包: 函数嵌套的格式就是闭包.写装饰器 ...
- python 进阶篇 函数装饰器和类装饰器
函数装饰器 简单装饰器 def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapp ...
- python装饰器2:类装饰器
装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 本文是装饰器相关内容的第二篇,关于类装饰器. "类装饰器"有两种解读方式:用来装饰类的装饰器:类作为装饰器装饰其它东西.你 ...
- [b0019] python 归纳 (五)_类装饰器
总结: 类装饰器, 本质是一个函数,输入一个类,返回一个类 Case 1 啥都没做 def deco(in_class): return in_class @deco class Cat: def _ ...
- python带参数的类装饰器
# -*- coding: utf-8 -*- # author:baoshan # 带参数的类装饰器(和不带参数的类装饰器有很大的不同) # 类装饰器的实现,必须实现__call__和__init_ ...
- Python不带参数的类装饰器
# -*- coding: utf-8 -*- # author:baoshan # 不带参数的类装饰器 # 类装饰器的实现,必须实现__call__和__init__两个内置函数. # __init ...
随机推荐
- repeter 控制一行中显示几条内容
repeter 控制一行中显示几条内容 <asp:Repeater ID="Repeater1" runat="server" DataSourceID ...
- [LeetCode&Python] Problem 674. Longest Continuous Increasing Subsequence
Given an unsorted array of integers, find the length of longest continuousincreasing subsequence (su ...
- 记录 spf13-vim 遇到的问题
一.spf13-vim 常用快捷键: https://blog.csdn.net/BjarneCpp/article/details/80608706 https://www.cnblogs.com/ ...
- mac添加redis 环境变量
cd /etc/paths.d touch redis vim redis 写入 /Users/love/Downloads/redis-4.0.10/src 之后就可以直接执行redis-cli r ...
- spring redis 注解实现缓存机制
1.xml配置 <bean id="poolConfigTax" class="redis.clients.jedis.JedisPoolConfig"& ...
- 小程序通过background-image设置背景图片
微信小程序通过background-image设置背景:只支持线上图片和base64图片,不支持本地图片:base64图片设置步骤如下: 1.在网站http://imgbase64.duoshiton ...
- 如何在idea中引入一个新maven项目
如何在idea中引入一个新的maven项目,请参见如下操作:
- 理念的创新——从keep和得到app谈起
浅谈keep创新之路 不得不说,这是一个健康越来越重要的时代,也是身体素质越来越被重视的一个年代.随着交通工具日新月异地发展,我们不太需要再徒步远行,甚至连骑自行车的机会也越来越少,这给我们的出行带来 ...
- 如何将maven的jar项目简单快速的转变成war项目
第一种方法: 首先在pom文件中的version标签下下方加入 <packaging>war</packaging>标签 然后右键项目 Java EE Tools 选择 Gen ...
- 使用nrm工具高效地管理npm源
在使用npm时,官方的源下载npm包会比较慢,国内我们基本使用淘宝的源,如果公司内部搭建了一套npm私有仓库,公司内部的源不可能把npm官方的npm包都同步,所以需要切换npm源.如果使用npm/cn ...