3
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.methodtest_wrapped.method返回不同的结果?
似乎 in 的第一个参数test_wrapped.methodself,而它不是 for test.method。为什么它从一个到另一个不同?

4

2 回答 2

2

区别不在于装饰的工作方式,而在于调用的工作方式。当实例调用定义在其类上的方法时test_wrapped,它总是self作为第一个参数传递。同时,当一个对象调用其自身的一个属性时,该属性恰好是一个函数,但在其类中不存在,它调用它而不传递self. 考虑这个简单的类:

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没有传递selfshout_hello(这会引发错误<lambda>() takes 0 positional arguments but 1 was given),因为shout_hello它是直接在实例上定义的,而不是在类上。

于 2020-12-09T17:23:51.947 回答
1

逐步浏览您的代码:

  1. 您创建一个TestClass名为test.

  2. 您手动调用Deco并提供它与test, 与线test = Deco('deco')(test)

  3. 这使您的代码通过该__call__函数,该函数修改传递的类test以将其method属性设置为嵌套函数。然后它返回它,所以test现在包含一个成功修改的TestClass:调用test.method('whatisit')将成功返回'whatisit'。重要的是,您不是在此处访问方法:您正在通过属性访问 FUNCTION。self被传递给 Python 中类的每个方法,但由于这不是方法,所以它在这里不起作用。尝试打印type(test.method),你会看到<class 'function'>,而不是<class 'method'>。重要的是,您传递了 a 的实例TestClass,而不是类定义本身:并且只有这个名为的实例test具有其method属性集。

  4. 然后创建一个TestClassWrapped名为test_wrapped. 创建它后,它再次进入__call__,将其TestWrappedClass作为test_class参数传递。重要的是,您传递了 的定义,而TestWrappedClass不是它的实例。此处设置method将为您稍后创建的每个实例修改它,TestWrappedClass甚至可以在不实例化任何内容的情况下访问它。尝试调用TestClassWrapped.method("abc"): 它会打印abc而不实例化 a TestClassWrapped。有趣的是,以这种方式设置时,它不是设置为属性而是设置为方法!尝试打印type(test_wrapped.method)。这就是我认为混乱的根源。

  5. 在 的情况下print(test_wrapped.method()),您必须记住,实例化类的每个方法都self作为它们的第一个参数。这意味着test_wrapped.method()将返回self:因此为什么test_wrapped == test_wrapped.method()。请注意,这不适用于从类定义中调用的方法,就像我之前展示的那样。TestClassWrapped.method("abc")必须采用某种参数(如abc),否则它会抱怨它缺少参数。

所以这就是为什么test.method('whatisit')返回'whatisit'并且不self作为参数,以及为什么test_wrapped.method()返回self

于 2020-12-09T10:55:54.533 回答