使用装饰器链在程序出口处注册类方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用装饰器链在程序出口处注册类方法相关的知识,希望对你有一定的参考价值。

我有一个类,它的一个属性是我想在程序退出时运行的类方法。这个直接的想法:

import atexit

class Foo:
    @atexit.register
    @classmethod
    def foo(cls):
        pass

引发以下异常:

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    class Foo:
  File "test.py", line 5, in Foo
    @classmethod
TypeError: the first argument must be callable

另一个直接的想法(注意改变装饰器的评估顺序):

import atexit

class Foo:
    @classmethod
    @atexit.register
    def foo(cls):
        pass

引发以下异常:

Error in atexit._run_exitfuncs:
TypeError: foo() missing 1 required positional argument: 'cls'

我对装饰器的概念很陌生。

  • 有没有简单的方法如何修复此代码仍然使用装饰链?
  • 如果没有,您会建议什么作为替代解决方案?
答案

首先你应该阅读How does a classmethod object work?的答案

但是看到以下代码的输出会很有趣:

def mydeco(func):
    print(repr(func))
    return func

class Foo:
    @mydeco
    @classmethod
    def foo(cls):
        pass

print(repr(Foo.foo))

如果你运行它,你会看到

<classmethod object at 0x6ffffd1dc18>
<bound method Foo.foo of <class '__main__.Foo'>>

所以第一行来自mydeco()装饰器,第二行来自底部的print()语句。你看他们是不同的。它们不同的原因是因为classmethod不像你期望的函数装饰器。它不会给你一个函数,而是一个classmethod对象,它不可调用。然而,与此同时,包含它的类Foo记得在Foo.__dict__中,当你回想起装饰类方法时,它会给你一个可调用的方法。

仅仅因为classmethod对象不可调用。你不能用atexit.register包装它。 staticmethod的类似案例。

所以现在你应该意识到,要将它注册到atexit,你可以在课外进行,如下所示:

import atexit

class Foo:
    @classmethod
    def foo(cls):
        pass

atexit.register(Foo.foo)

以上是关于使用装饰器链在程序出口处注册类方法的主要内容,如果未能解决你的问题,请参考以下文章

使用装饰器自动注册类方法

python使用上下文对代码片段进行计时,非装饰器

如何使用 Python 装饰器以便方法使用 functools.lru_cache 并自行注册?

类装饰器不绑定自我

第八篇递归装饰器

类方法的python装饰器