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

  • 相关阅读:
    codeforces 1295 题解(完结)
    Leetcode 第 173 场周赛 题解(完结)
    P2564-[USACO12MAR]花盆Flowerpot
    Task 1-2
    LeetCode--169--多数元素(python)
    049字符异位词分组(python)
    矩阵路径(python)
    全排列
    滑动窗口最大值leetcode & nowcoder(python)
    取topK
  • 原文地址:https://www.cnblogs.com/hh2737/p/9202759.html
Copyright © 2020-2023  润新知