在 class 实例和 class 定义上调用 class 装饰器有什么区别?
What's the difference between calling a class decorator on a class instance and on a class definition?
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.method
和 test_wrapped.method
return 不同的结果?
似乎 test_wrapped.method
中的第一个参数是 self
,而不是 test.method
。为什么两者不同?
逐步浏览您的代码:
您创建了一个名为 test
的常规 TestClass
。
您手动调用 Deco
并用 test
提供它,使用行 test = Deco('deco')(test)
.
这使您的代码通过 __call__
函数,该函数修改传递的 class test
以将其 method
属性设置为嵌套功能。然后 returns 它,所以 test
现在包含一个成功修改的 TestClass
:调用 test.method('whatisit')
将成功 return 'whatisit'
。重要的是,您不是在此处访问方法:您是通过属性访问功能。 self
被传递给 Python 中 classes 的每个方法,但由于这不是方法,因此它不会在这里发挥作用。尝试打印 type(test.method)
,您会看到 <class 'function'>
而不是 <class 'method'>
。重要的是,您已经传递了 TestClass
的实例,而不是 class 定义本身:并且只有这个名为 test
的实例设置了 method
属性。
然后您创建一个名为 test_wrapped
的 TestClassWrapped
。创建后,它再次进入 __call__
,将其作为 test_class
参数传递 TestWrappedClass
。重要的是,您传递的是 TestWrappedClass
的定义,而不是它的实例。在此处设置 method
将为您以后创建的每个 TestWrappedClass
实例修改它,甚至可以在不实例化任何东西的情况下访问它。尝试调用 TestClassWrapped.method("abc")
:它将打印 abc
而根本不实例化 TestClassWrapped
。有趣的是,当以这种方式设置时,它不是设置为属性而是设置为方法!尝试打印 type(test_wrapped.method)
。这就是我认为是混乱的根源。
在print(test_wrapped.method())
的情况下,你必须记住实例化的classes的每个方法都将self
作为它们的第一个参数。这意味着 test_wrapped.method()
将 return self
:因此为什么 test_wrapped == test_wrapped.method()
。请注意,这不适用于从 class 定义调用的方法,就像我之前展示的那样。 TestClassWrapped.method("abc")
必须采用某种参数(如 abc
),否则它会抱怨缺少参数。
所以这就是为什么 test.method('whatisit')
returns 'whatisit'
并且不将 self
作为参数,而 test_wrapped.method()
为什么 return self
.
区别不在于装饰的工作方式,而在于呼叫的工作方式。当实例调用在其 class 上定义的方法时,如 test_wrapped
所做的那样,它始终将 self
作为第一个参数传递。同时,当一个对象调用它自己的一个属性时,恰好是一个函数,但在它的 class 上不存在,它会在不传递 self
的情况下调用它。考虑这个简单的 class:
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
没有将 self
传递给 shout_hello
(这会引发错误 <lambda>() takes 0 positional arguments but 1 was given
),因为 shout_hello
是直接在例如,不在 class.
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.method
和 test_wrapped.method
return 不同的结果?
似乎 test_wrapped.method
中的第一个参数是 self
,而不是 test.method
。为什么两者不同?
逐步浏览您的代码:
您创建了一个名为
test
的常规TestClass
。您手动调用
Deco
并用test
提供它,使用行test = Deco('deco')(test)
.这使您的代码通过
__call__
函数,该函数修改传递的 classtest
以将其method
属性设置为嵌套功能。然后 returns 它,所以test
现在包含一个成功修改的TestClass
:调用test.method('whatisit')
将成功 return'whatisit'
。重要的是,您不是在此处访问方法:您是通过属性访问功能。self
被传递给 Python 中 classes 的每个方法,但由于这不是方法,因此它不会在这里发挥作用。尝试打印type(test.method)
,您会看到<class 'function'>
而不是<class 'method'>
。重要的是,您已经传递了TestClass
的实例,而不是 class 定义本身:并且只有这个名为test
的实例设置了method
属性。然后您创建一个名为
test_wrapped
的TestClassWrapped
。创建后,它再次进入__call__
,将其作为test_class
参数传递TestWrappedClass
。重要的是,您传递的是TestWrappedClass
的定义,而不是它的实例。在此处设置method
将为您以后创建的每个TestWrappedClass
实例修改它,甚至可以在不实例化任何东西的情况下访问它。尝试调用TestClassWrapped.method("abc")
:它将打印abc
而根本不实例化TestClassWrapped
。有趣的是,当以这种方式设置时,它不是设置为属性而是设置为方法!尝试打印type(test_wrapped.method)
。这就是我认为是混乱的根源。在
print(test_wrapped.method())
的情况下,你必须记住实例化的classes的每个方法都将self
作为它们的第一个参数。这意味着test_wrapped.method()
将 returnself
:因此为什么test_wrapped == test_wrapped.method()
。请注意,这不适用于从 class 定义调用的方法,就像我之前展示的那样。TestClassWrapped.method("abc")
必须采用某种参数(如abc
),否则它会抱怨缺少参数。
所以这就是为什么 test.method('whatisit')
returns 'whatisit'
并且不将 self
作为参数,而 test_wrapped.method()
为什么 return self
.
区别不在于装饰的工作方式,而在于呼叫的工作方式。当实例调用在其 class 上定义的方法时,如 test_wrapped
所做的那样,它始终将 self
作为第一个参数传递。同时,当一个对象调用它自己的一个属性时,恰好是一个函数,但在它的 class 上不存在,它会在不传递 self
的情况下调用它。考虑这个简单的 class:
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
没有将 self
传递给 shout_hello
(这会引发错误 <lambda>() takes 0 positional arguments but 1 was given
),因为 shout_hello
是直接在例如,不在 class.