В чем разница между вызовом декоратора класса для экземпляра класса и для определения класса?

      class Deco:
    def __init__(self, name):
        self.name = name
    def __call__(self, test_class):
        def inner_func(whatisit):
            return whatisit
        test_class.method = inner_func
        return test_class

class TestClass:
    def __init__(self, name):
        self.name = name
    
@Deco('deco')
class TestClassWrapped:
    def __init__(self, name):
        self.name = name

test = TestClass('test')
test = Deco('deco')(test)

test_wrapped = TestClassWrapped('test')

print(test.method('whatisit')) >> whatisist
print(test_wrapped == test_wrapped.method()) >> True

Почему делать и возвращать разные результаты?
Кажется, что первый аргумент в test_wrapped.methodявляется self, пока это не для test.method. Почему он отличается от одного к другому?

2 ответа

Разница не в том, как работает украшение, а в том, как работает вызов. Когда экземпляр вызывает метод, определенный в его классе, как test_wrappedделает, он всегда проходит как первый аргумент. Между тем, когда объект вызывает атрибут самого себя, который оказывается функцией, но не существует в его классе, он вызывает его без передачи . Рассмотрим этот простой класс:

      class LoudTalker:
    def __init__(self, name):
        self.shout_hello = lambda: print("HELLO, I'M {}".format(name.upper()))

>>> john = LoudTalker("John")
>>> LoudTalker.shout_hello()
HELLO, I'M JOHN

Обратите внимание, что johnне прошел selfto (что вызвало бы ошибку <lambda>() takes 0 positional arguments but 1 was given) потому что shout_helloбыл определен непосредственно в экземпляре, а не в классе.

Пошаговое изучение вашего кода:

  1. Вы создаете обычный файл с именем .

  2. Вы вручную звоните Decoи снабдить его строкой test = Deco('deco')(test).

  3. Это заставляет ваш код проходить через функцию, которая изменяет переданный класс, чтобы установить его атрибут для вложенной функции. Затем он возвращает его, и теперь он содержит успешно измененный : вызов будет успешно возвращен. Важно отметить, что здесь вы НЕ получаете доступ к методу: вы получаете доступ к ФУНКЦИИ через АТРИБУТ. передается каждому методу классов в Python, но поскольку это не метод, здесь он не играет роли. Попробуйте распечатать type(test.method), вот увидишь <class 'function'>и не <class 'method'>. Важно отметить, что вы прошли ЭКЗЕМПЛЯР TestClass, а не само определение класса: и только этот экземпляр с именем testбыл установлен его атрибут.

  4. Затем вы создаете именованный test_wrapped. При его создании он входит в __call__еще раз, передав его как test_classпараметр. Важно отметить, что вы передали ОПРЕДЕЛЕНИЕ , а не его экземпляр. Параметр methodздесь изменит его для каждого экземпляра TestWrappedClassвы позже создадите, и к ним можно будет получить доступ даже без создания экземпляра чего-либо. Попробуйте позвонить: он будет печатать без создания экземпляра TestClassWrappedвообще. Интересно, что когда он установлен таким образом, он устанавливается не как атрибут, а как метод! Попробуйте распечатать type(test_wrapped.method). Это то, что я считаю источником путаницы.

  5. В случае print(test_wrapped.method()), вы должны помнить, что каждый метод созданных классов принимает в качестве своего первого параметра. Это означает, что вернется: поэтому, почему test_wrapped == test_wrapped.method(). Обратите внимание, что это не относится к методам, вызываемым из определения класса, как я показал ранее. TestClassWrapped.method("abc")ДОЛЖЕН принимать какой-либо параметр (например, abc), иначе он будет жаловаться на отсутствие аргумента.

Так вот почему test.method('whatisit')возвращается 'whatisit'и не принимает в качестве параметра, и почему test_wrapped.method()возвращается self.

Другие вопросы по тегам