Python中staticmethod方法和classmethod方法区别

前言

staticmethod和classmethod两个方法在python里是通过装饰器来实现的,语法分别是@staticmethod和@classmethod,本文就讨论下这两种方法的区别以及使用场景

定义方式差异

@classmethod和@staticmethod装饰方法时,对于被装饰方法本身定义有差异,主要体现在形参上。

@classmethod
#第一个参数是类本身
def class_method(cls, data):

@staticmethod
#不存在任何与类、实例相关的参数,包括cls、self
def static_method(data):

调用方式差异

看看下面这个类:

class MyClass(object):

    def __init__(self):
        pass

    def normal_method(self, data):
        print "normal method: %s %s" % (self, data)

    @classmethod
    def class_method(cls, data):
        print "class method: %s %s" % (cls, data)

    @staticmethod
    def static_method(data):
        print "static method: %s " % (data)
  • 正常调用方法

正常情况下,调用类的方法之前必须实例化

>>> mc = MyClass()
>>> mc.normal_method("Hello World!")
normal method: <__main__.MyClass object at 0x10667c590> hello world!

可以看到def normal_method(self, data)第一个参数是self,Python解释器在运行时会自动把运行实例传递给被调用方法,所以方法调用输出的结果是实例化后的object的内容。

  • @classmethod调用
    @classmethod 装饰器实现的功能是:类可以直接调用@classmethod装饰的方法,无需实例化。
>>> MyClass.class_method("hello world!")
class method: <class '__main__.MyClass'> hello world!

可以看到被@classmethod装饰的函数cls变量被传递成类名

  • @staticmethod调用
    @staticmethod 装饰器实现的功能是:无论类是否实例化,可以直接调用@staticmethod修饰的函数
#类直接调用
>>> MyClass.static_method("hello world!")
static method: hello world!
#类实例化后调用
>>>mc.static_method("hello world!")
static method: hello world!

使用场景

  • @classmethod使用场景

由于Python本身是不支持函数的重载(顶多只能实现函数的取代。。。)但是classmethod可以实现类似重载的功能(尽管我个人认为还是比较丑陋)

class Date(object):

    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def today(self):
        print self.year + '-' + self.month + '-' + self.day

date = Date('2016', '05', '29')
date.today()
#2016-05-29

假设现在Date实例化的参数需要支持一个list类型:[2016, 05, 29],再看看下面这段代码:

class Date(object):

    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def in_as_list(self, day_as_list):
        assert isinstance(day_as_list, list)
        (year, month, day) = (day_as_list[0], day_as_list[1], day_as_list[2])
        return Date(year, month, day)

    def today(self):
        print self.year + '-' + self.month + '-' + self.day

date = Date.in_as_list(['2016', '05', '29'])
date.today()
#2016-05-29
  • @staticmethod使用场景

被@staticmethod修饰的方法一般用于:跟类有关系的功能但在运行时又不需要实例和类参与的情况,比如更改环境变量或者修改其他类的属性等能用到静态方法。这种情况可以直接用函数解决,但这样同样会扩散类内部的代码,造成维护困难。比如Tornado代码中类的单例化:

    @staticmethod
    def instance():
        """Returns a global `IOLoop` instance.

        Most applications have a single, global `IOLoop` running on the
        main thread.  Use this method to get this instance from
        another thread.  In most other cases, it is better to use `current()`
        to get the current thread's `IOLoop`.
        """
        if not hasattr(IOLoop, "_instance"):
            with IOLoop._instance_lock:
                if not hasattr(IOLoop, "_instance"):
                    # New instance after double check
                    IOLoop._instance = IOLoop()
        return IOLoop._instance

总结

@classmethod: 被装饰的方法被调用时,第一个实参是类名而不是类的实例,这就意味着可以用类直接调用被装饰的方法而不强依赖类实例化

@staticmethod: 被装饰的方法被调用时,不会传递类的实例或者类命,意味着可以把一个函数放在类里,但是在这个函数里是不能访问类的实例的,在函数实现的功能和类实例无关时候会比较有用。

标签:Python