When an instance of a class is created, for each function in the class definition, the instance will have an attribute in its directory with the same name as the function. This “function attribute”, assuming the function is either a class method or an instance/bound method, will have another directory as its value which includes all of the attributes found in function objects in addition to __func__
and __self__
. __func__
contains yet another directory—the directory of the function as found in the class itself—which of course contains all the attributes found in function objects (e.g. all attributes in __func__
point to the same object as the identically named attributes in the class’ function directory). Presumably, the attributes in the instance’s functions’ directory defer to the the identical attribute found in the __func__
directory when the function is called from the instance (and when the function is called from the instance, the instance's method's __call__
attribute defers to __func__.__call__
). Why then do instances create this __func__
attribute anyway? Why not just have the immediate instance function directory attributes point directly to the class function directory attributes just as static methods operate? It seems that instances are using more memory than they need.
To make this discrepancy more clear:
class Solution:
def maxArea(self, height):
pass
@staticmethod
def i_am_static():
pass
@classmethod
def i_am_a_class_method():
pass
s = Solution()
print("\n\nDirectory of Instance Method:")
print(dir(s.maxArea))
print("\n\nDirectory of Instance Method __func__ attribute:")
print(dir(s.maxArea.__func__))
print("\n\nEqualities:")
print("s.maxArea.__func__.__call__== Solution.maxArea.__call__? ",
s.maxArea.__func__.__call__ == Solution.maxArea.__call__)
print("s.maxArea.__call__ == Solution.maxArea.__call__? ",
s.maxArea.__call__ == Solution.maxArea.__call__)
print("\n\nDirectory of Static Method:")
print(dir(s.i_am_static))
print("\nInstance Method has these other methods:")
for i in dir(s.maxArea):
if i not in dir(s.i_am_static):
print(i)
print("\nStatic Method has these other methods:")
for i in dir(s.i_am_static):
if i not in dir(s.maxArea):
print(i)
Prints:
Directory of Instance Method:
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
Directory of Instance Method __func__ attribute:
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
Equalities:
s.maxArea.__func__.__call__ == Solution.maxArea.__call__? True
s.maxArea.__call__ == Solution.maxArea.__call__? False
Directory of Static Method:
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
Instance Method has these other methods:
__func__
__self__
Static Method has these other methods:
__annotations__
__closure__
__code__
__defaults__
__dict__
__globals__
__kwdefaults__
__module__
__name__
__qualname__
Looking at the equalities, we can see that the attributes in __func__
reference the same object created by the Class itself. However, the attributes not in __func__
(for instance __call__
) do not reference the same object, yet they presumably call that object. Why go through the trouble of recreating these attributes outside of __func__
simply to call those in __func__
? Why not dump the attributes in __func__
in the dir(s.maxArea) and the __self__
attribute will express that the method should be called as an instance method?