我们都知道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. repeter 控制一行中显示几条内容

    repeter  控制一行中显示几条内容 <asp:Repeater ID="Repeater1" runat="server" DataSourceID ...

  2. [LeetCode&Python] Problem 674. Longest Continuous Increasing Subsequence

    Given an unsorted array of integers, find the length of longest continuousincreasing subsequence (su ...

  3. 记录 spf13-vim 遇到的问题

    一.spf13-vim 常用快捷键: https://blog.csdn.net/BjarneCpp/article/details/80608706 https://www.cnblogs.com/ ...

  4. mac添加redis 环境变量

    cd /etc/paths.d touch redis vim redis 写入 /Users/love/Downloads/redis-4.0.10/src 之后就可以直接执行redis-cli r ...

  5. spring redis 注解实现缓存机制

    1.xml配置 <bean id="poolConfigTax" class="redis.clients.jedis.JedisPoolConfig"& ...

  6. 小程序通过background-image设置背景图片

    微信小程序通过background-image设置背景:只支持线上图片和base64图片,不支持本地图片:base64图片设置步骤如下: 1.在网站http://imgbase64.duoshiton ...

  7. 如何在idea中引入一个新maven项目

    如何在idea中引入一个新的maven项目,请参见如下操作:      

  8. 理念的创新——从keep和得到app谈起

    浅谈keep创新之路 不得不说,这是一个健康越来越重要的时代,也是身体素质越来越被重视的一个年代.随着交通工具日新月异地发展,我们不太需要再徒步远行,甚至连骑自行车的机会也越来越少,这给我们的出行带来 ...

  9. 如何将maven的jar项目简单快速的转变成war项目

    第一种方法: 首先在pom文件中的version标签下下方加入 <packaging>war</packaging>标签 然后右键项目 Java EE Tools 选择 Gen ...

  10. 使用nrm工具高效地管理npm源

    在使用npm时,官方的源下载npm包会比较慢,国内我们基本使用淘宝的源,如果公司内部搭建了一套npm私有仓库,公司内部的源不可能把npm官方的npm包都同步,所以需要切换npm源.如果使用npm/cn ...