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

问题描述 投票:0回答:1

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

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'

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

  • 有没有简单的方法如何修复此代码仍然使用装饰链?
  • 如果没有,您会建议什么作为替代解决方案?
python python-decorators class-method atexit
1个回答
2
投票

首先你应该阅读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)
© www.soinside.com 2019 - 2024. All rights reserved.