我们都知道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 的类装饰器的更多相关文章

  1. python 描述符 上下文管理协议 类装饰器 property metaclass

    1.描述符 #!/usr/bin/python env # coding=utf-8 # 数据描述符__get__ __set__ __delete__ ''' 描述符总结 描述符是可以实现大部分py ...

  2. 详解Python闭包,装饰器及类装饰器

    在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...

  3. 面向切面编程AOP——加锁、cache、logging、trace、同步等这些较通用的操作,如果都写一个类,则每个用到这些功能的类使用多继承非常难看,AOP就是解决这个问题的,python AOP就是装饰器

    面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同.面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用. 举两个大家都接触过的AOP的例子: 1)java中myba ...

  4. python高级 之(二) --- 类装饰器

    装饰器-初级 在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能.使代码可读性更高.结构更加清晰.冗余度更低 简介 """ 闭包: 函数嵌套的格式就是闭包.写装饰器 ...

  5. python 进阶篇 函数装饰器和类装饰器

    函数装饰器 简单装饰器 def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapp ...

  6. python装饰器2:类装饰器

    装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 本文是装饰器相关内容的第二篇,关于类装饰器. "类装饰器"有两种解读方式:用来装饰类的装饰器:类作为装饰器装饰其它东西.你 ...

  7. [b0019] python 归纳 (五)_类装饰器

    总结: 类装饰器, 本质是一个函数,输入一个类,返回一个类 Case 1 啥都没做 def deco(in_class): return in_class @deco class Cat: def _ ...

  8. python带参数的类装饰器

    # -*- coding: utf-8 -*- # author:baoshan # 带参数的类装饰器(和不带参数的类装饰器有很大的不同) # 类装饰器的实现,必须实现__call__和__init_ ...

  9. Python不带参数的类装饰器

    # -*- coding: utf-8 -*- # author:baoshan # 不带参数的类装饰器 # 类装饰器的实现,必须实现__call__和__init__两个内置函数. # __init ...

随机推荐

  1. 2.常用adb命令的使用

    使用电脑连接手机,查看手机的唯一编号,如果是模拟器,就是显示地址和端口号: adb devices 使用adb安装app应用: adb install apk路径和包名 -r 允许覆盖安装 -s 将a ...

  2. UVa699

    这个建树的根选的很有意思,在中间作为树的根.所以二叉树建树的方法虽然一般是有两种数组的方法,一个是如果深度不太大的话,可以之间用2*k+1,2*k建树,如果很大的话,就挨着建树,弄一个结构体,有左右子 ...

  3. JDK 8 安装及配置

    1.配置java环境变量 注意:jdk文件夹名字取名不要用汉语取名. 1)鼠标右键点击我的电脑(计算机)选择属性栏 2)再点击左边高级系统设置 3)点击环境变量 4)在用户变量窗口新建变量名为JAVA ...

  4. MongoDB的启动与停止(一)

    1:启动和停止Mongodb    1)从命令行启动      执行mongod,启动MongoDB服务器,mongod有很多可配置的启动选项,可以使用mongod --help查看所有选项   -- ...

  5. 十八、springcloud(四)熔断器

    1.熔断器(Hystrix) a.断路器机制 断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直 ...

  6. [JAVA]对象的别名问题

    对于JAVA的基本数据类型,a=b就是把b的内容复制给a.若接着又修改了a,对b是没有影响的. 但是在为对象“赋值”的时候,情况发生了变化.对一个对象进行操作时,我们真正操作的是对象的引用. 下面对两 ...

  7. py-day2-5 python 百分号字符串拼接

    #### 字符串格式化. # %s 代替任何的元素 (数字,字符串,列表··) print('I live %s crty' %'my') print('I live %s crty' %'[6,8, ...

  8. 阅读 video in to axi4-stream v4.0 笔记

    阅读 video in to axi4-stream v4.0 笔记 axi4 stream里面只传输的有效数据. 引用: 使能了video timing controller core 的所用信号, ...

  9. 黄聪:iOS $299刀企业证书申请的过程以及细节补充

    最近申请了iOS的 299刀企业证书,相关过程有些问题,分享出来,以便后来人参考.申请的过程我主要参考了别人以前的文章,链接如下: 1.https://developer.apple.com/cn/s ...

  10. mosquitto centos安装配置

    周末弄wordpress的Mysql,一不小心把wordpress弄不好了,写了的好几遍文章也没有了,一怒之下,把整个系统重装了,安装了不带任何软件的新系统,重新搭一遍. 0.安装ftp服务器 #yu ...