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
TestClassnamedtest.You manually call
Decoand provide it withtest, with the linetest = Deco('deco')(test).This makes your code go through the
__call__function, which modifies the passed classtestto set itsmethodattribute to the nested function. It then returns it, and sotestnow 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.selfis 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 namedtesthas had itsmethodattribute set.You then create a
TestClassWrappednamedtest_wrapped. Upon creating it, it enters the__call__once more, passing itTestWrappedClassas thetest_classparameter. Importantly, you've passed a DEFINITION of theTestWrappedClass, not an instance of it. Settingmethodhere will modify it for every instance ofTestWrappedClassyou'll later create, and can even be accessed without instantiating anything. Try callingTestClassWrapped.method("abc"): it will printabcwithout instantiating aTestClassWrappedat 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 takeselfas 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 takeselfas parameter, and whytest_wrapped.method()does returnself.