Python Class Method and Class Only Method
In this post, I will be discussing Python method especially class method.
>>> foo = Foo()
>>> foo.bar_inst
<bound method Foo.bar_inst of <test.Foo object at 0x025A0EB0>>
>>> foo.bar_static
<function Foo.bar_static at 0x025A34F8>
>>> foo.bar_class
<bound method type.bar_class of <class 'test.Foo'>>
foo.bar_static just returns a function object as if bar_static were defined outside of class. (In Python 2, it would return an unbound method; Python 3 has removed the concept of unbound methods. So a function is either a bound method or a regular function object)
class method needs to pass objtype to the function object as the first argument, so it might be implemented as follows:
Example
class Foo: def bar_inst(self): print(self, "This is bar_inst") @staticmethod def bar_static(): print("This is bar_static") @classmethod def bar_class(cls): print(cls, "This is bar_class")
>>> foo.bar_inst
<bound method Foo.bar_inst of <test.Foo object at 0x025A0EB0>>
>>> foo.bar_static
<function Foo.bar_static at 0x025A34F8>
>>> foo.bar_class
<bound method type.bar_class of <class 'test.Foo'>>
foo.bar_static just returns a function object as if bar_static were defined outside of class. (In Python 2, it would return an unbound method; Python 3 has removed the concept of unbound methods. So a function is either a bound method or a regular function object)
How It Works -- Descriptor
Every function object outside of or inside of class is a descriptor because it implements __get__ magic method.
Same translations happen for foo.bar_static and foo.bar_class
>>> def bar(): ... pass ... >>> bar.__get__ <method-wrapper '__get__' of function object at 0x025A3420>According to descriptor rules, foo.bar_inst is translated to
>>> Foo.__dict__['bar_inst'].__get__(foo, type(foo)) <bound method Foo.bar_inst of <test.Foo object at 0x025A0EB0>>
Same translations happen for foo.bar_static and foo.bar_class
>>> Foo.__dict__['bar_static'].__get__(foo, type(foo)) <function Foo.bar_static at 0x025A34F8> >>> Foo.__dict__['bar_class'].__get__(foo, type(foo)) <bound method type.bar_class of <class 'test.Foo'>>
My Implementation of Static Method
Utilizing descriptor and decorator, it's very easy to implement static_method. We just ignore obj and objtype return self.f -- the function object.
class MyStaticMethod: def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f
class Foo: @staticmethod def bar_static(): print("This is bar_static") @MyStaticMethod def bar_my_static(): print("This is bar_my_static")Suppose foo = Foo(), both foo.bar_static and foo.bar_my_static would work essentially the same way.
My Implementation of Class Method
class MyClassMethod: def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): if objtype is None: objtype = type(obj) def classfunc(*args, **kwargs): self.f(objtype, *args, **kwargs) return classfunc
class Foo: def bar_inst(self): print(self, "This is bar_inst") @classmethod def bar_class(cls): print(cls, "This is bar_class") @MyClassMethod def bar_my_class(cls): print(cls, "This is bar_my_class")
Implementation of Class Only Method
Below is an implementation of class only method by Django (django/utils/decorators.py)
class classonlymethod(classmethod): def __get__(self, instance, owner): if instance is not None: raise AttributeError("This method is available only on the class, not on instances.") return super(classonlymethod, self).__get__(instance, owner))If a function is decorated by classonlymthod, call it from instance will raise an exception.
class Foo: @classonlymethod def bar_class_only(cls): print(cls, "This is bar_class_only")