Регистрация метода класса при выходе из программы с использованием цепочки декораторов - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть класс, один из его атрибутов - это метод класса, который я хочу запустить при выходе из программы.Эта непосредственная идея:

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'

Я совершенно новичок в концепции декораторов.

  • Есть ли простой способ исправить этот код, все еще используя цепочку декораторов?
  • Если нет, то что бы вы порекомендовали в качестве альтернативного решения?

1 Ответ

2 голосов
/ 24 апреля 2019

Сначала вы должны прочитать ответ для Как работает объект метода класса?

Но было бы интересно увидеть вывод следующего кода:

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)
...