Python Class Method and Class Only Method

In this post, I will be discussing Python method especially class method.

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 = 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)


How It Works -- Descriptor


 Every function object outside of or inside of class is a descriptor because it implements __get__ magic method.

>>> 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 method needs to pass objtype to the function object as the first argument, so it might be implemented as follows:


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")

Popular posts from this blog

LeetCode 68 Text Justification (C++, Python)

How Django Works (4) URL Resolution