我们都知道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. 非常好的开源C项目tinyhttpd(500行代码)

    编译命令 gcc -W -Wall -lpthread -o httpd httpd.c 源码 #include <stdio.h> #include <sys/socket.h&g ...

  2. 吴恩达机器学习-octave笔记

    隐藏前缀提示符:PS1('>>') 不显示打印内容:;结尾 字符串:a=’hi’ 屏幕输出:disp(sprint(‘2 decimals:%0.2f’,a)) 生成集合(矩阵):V=1: ...

  3. iOS 证书申请和使用详解(详细版)阅读

    对于iOS开发者来说,apple开发者账号肯定不会陌生.在开发中我们离不开它.下面我简单的为大家分享一下关于iOS开发中所用的证书相关知识. 第一部分:成员介绍 1.Certification(证书) ...

  4. contos防爆力破解密码

    最近看了一篇文章ssh的爆力破解所以自己就做了一下防爆力破解denyhost 下载denyhost的软件包并上传的服务器下载地址https://sourceforge.net/projects/den ...

  5. Ubuntu安装lrzsz

    本文主要介绍在Ubuntu下安装lrzsz工具,方便windows和linux间的文件上传下载 方法1:二进制包自动安装1.1 在终端中,输入命令,自动安装,简单方便: sudo apt-get in ...

  6. requests模块的使用

    requests模块 什么是request模块:requests是python原生一个基于网络请求的模块,模拟浏览器发起请求. requests-get请求 # get请求 import reques ...

  7. git的优缺点

      git可以说是世界上最先进的版本控制系统,大多语句的执行为linux语句,也不难怪,,起初他就是为了帮助开发linux开发内核而使用. 我们先来说git的主要功能,知道了这个,我们也就知道了为什么 ...

  8. FlexRay笔记

    1.FlexRay具有高速.可靠及安全的特点.FlexRay在物理上通过两条分开的总线通信,每一条的数据速率是10MBit/s.CAN网络最高性能极限为1Mbps,而FlexRay总数据速率可达到20 ...

  9. Oracle的导入和导出

    导出命令: EXP 用户名/密码@数据库名  BUFFER=64000 file=G:\dmp\full1.dmp  OWNER=用户名 导入命令: IMP 用户名/密码@数据库名 BUFFER=64 ...

  10. ubuntu-docker入门到放弃(八)创建支持SSH服务的镜像

    我们知道进入docker容器可以使用attach.exec等命令来操作和管理,但是如果需要远程登录并管理容器,就需要ssh服务的支持了. 1.基于commit命令创建 docker提供了commit命 ...