云上的天涯

导航

< 2025年2月 >
26 27 28 29 30 31 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 1
2 3 4 5 6 7 8

统计

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函数是正确的,有没有返回值是无所谓的。

posted on   云上的天涯  阅读(214)  评论(0编辑  收藏  举报

编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
点击右上角即可分享
微信分享提示