我们都知道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. 入门项目 A5-2 interface-shop 第三方接口2

    from db import db_handler # 从db数据包下面调用db处理模块,以使用其内部的名称空间与函数功能 # 添加购物车接口 def add_shopping_cart_interf ...

  2. CC攻击原理及防范方法

    一. CC攻击的原理: CC攻击的原理就是攻击者控制某些主机不停地发大量数据包给对方服务器造成服务器资源耗尽,一直到宕机崩溃.CC主要是用来消耗服务器资源的,每个人都有这样的体验:当一个网页访问的人数 ...

  3. 【草稿】实验室新手HandBook

    PS:本文旨在给初入CV领域实验室的新手一个可供参考的学习列表,使得能够快速熟悉常用且必要的工具.

  4. [LeetCode&Python] Problem 504. Base 7

    Given an integer, return its base 7 string representation. Example 1: Input: 100 Output: "202&q ...

  5. c# 号码记录,车友

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. 使用MSF生成shellcode

    使用MSF生成shellcode payload和shellcode的区别 Payload是是包含在你用于一次漏洞利用(exploit)中的ShellCode中的主要功能代码.因为Payload是包含 ...

  7. 在html中做表格以及给表格设置高宽字体居中和表格线的粗细

    今天学习了如何用HTML在网页上做表格,对于我这种横列部分的属实有点麻烦,不过在看着表格合并单过格的时候我把整个表格看做代码就容易多了. 对于今天的作业让我学习了更多的代码,对于代码的应用希望更加熟练 ...

  8. Django 数据库迁移

    Django 数据库迁移 DATABASES = { # Django默认配置使用sqlite3数据库 # 'default': { # 'ENGINE': 'django.db.backends.s ...

  9. 在linux中编译grpc

    环境: centos_7_x86_x64 一.下载 1)下载grpc源代码:grpc-1.2.0.zip 2)下载grpc依赖库: 1)benchmark-master.zip 2)boringssl ...

  10. Processing 编程学习指南 (丹尼尔·希夫曼 著)

    https://processing.org/reference/ 第1章 像素 (已看) 第2章 Processing (已看) 第3章 交互 (已看) 第4章 变量 (已看) 第5章 条件语句 ( ...