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
Why do test.method
and test_wrapped.method
return different results ?
It seems that the first argument in test_wrapped.method
is self
, while it isn't for test.method
. Why does it differ from one to the other?
Walking through your code step-by-step:
You create a regular
TestClass
namedtest
.You manually call
Deco
and provide it withtest
, with the linetest = Deco('deco')(test)
.This makes your code go through the
__call__
function, which modifies the passed classtest
to set itsmethod
attribute to the nested function. It then returns it, and sotest
now contains a successfully modifiedTestClass
: callingtest.method('whatisit')
will successfully return'whatisit'
. Importantly, you're NOT accessing a method here : you're accessing a FUNCTION through an ATTRIBUTE.self
is passed to every method of classes in Python, but since this isn't a method, it doesn't come into play here. Try printingtype(test.method)
, you'll see<class 'function'>
and not<class 'method'>
. Importantly, you've passed an INSTANCE of aTestClass
, not the class definition itself : and only this instance namedtest
has had itsmethod
attribute set.You then create a
TestClassWrapped
namedtest_wrapped
. Upon creating it, it enters the__call__
once more, passing itTestWrappedClass
as thetest_class
parameter. Importantly, you've passed a DEFINITION of theTestWrappedClass
, not an instance of it. Settingmethod
here will modify it for every instance ofTestWrappedClass
you'll later create, and can even be accessed without instantiating anything. Try callingTestClassWrapped.method("abc")
: it will printabc
without instantiating aTestClassWrapped
at all. Interestingly, when set in this way, it's not set as an attribute but as a method! Try printingtype(test_wrapped.method)
. This is what I believe to be the source of confusion.In the case of
print(test_wrapped.method())
, you have to remember that every method of instantiated classes takeself
as their first parameter. This means thattest_wrapped.method()
will returnself
: hence whytest_wrapped == test_wrapped.method()
. Note that this doesn't apply to methods called from a class definition, like I've shown earlier.TestClassWrapped.method("abc")
HAS to take a parameter of some sort (likeabc
), else it will complain it's lacking an argument.So this is why
test.method('whatisit')
returns'whatisit'
and doesn't takeself
as parameter, and whytest_wrapped.method()
does returnself
.