静态方法、类方法和实例方法

python类中方法有三种:静态方法(staticmethod)、类方法(classmethod)、实列方法。

使用(fake)

class TestFuc(object):
    def instance_fuc(self, x):
        print('instance_fuc(%s,%s)' % (self, x))

    @classmethod
    def class_fuc(cls,x):
        print('class_fuc(%s,%s)' % (cls,x))

    @staticmethod
    def static_fuc(x):
        print('static_fuc(%s)' % x)

test_fuc = TestFuc()

# 实例方法
test_fuc.instance_fuc(1)
# 类方法
test_fuc.class_fuc(1)
TestFuc.class_fuc(1)

# 静态方法
test_fuc.static_fuc(1)
TestFuc.static_fuc(1)

应用

脱离了实际的应用场景,谈使用就是是耍流氓。上文介绍的”使用”仅仅展示如何定义(进入)和伪使用,真正的场景不会这样用的。

静态方法(staticmethod)和类方法(classmethod)并不常用。我喜欢在stackoverflow:What is the advantage of using static methods in Python?的一句话:”So they aren’t useful for day-to-day methods”。尽管如此,我们依然要学习,并熟悉使用(原因:语言特性的完整、特殊场景的使用)。

目前,我看到网上介绍比较多应用用它们作为构造函数。

# staticmethod实现
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)

继承类中的区别

子类的实例继承了父类的static_method静态方法,调用该方法,还是调用的父类的方法和类属性。 子类的实例继承了父类的class_method类方法,调用该方法,调用的是子类的方法和子类的类属性。

class Foo(object):
    X = 1
    Y = 2

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / 1

    @staticmethod
    def static_method():
        return Foo.averag(Foo.X, Foo.Y)

    @classmethod
    def class_method(cls):
        return cls.averag(cls.X, cls.Y)

class Son(Foo):
    X = 3
    Y = 5

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / 2

p = Son()
print(p.static_method())
print(p.class_method())

实例方法

实例方法,除静态方法与类方法外,类的其他方法都属于实例方法。

实例方法需要将类实例化后调用,如果使用类直接调用实例方法,需要显式地将实例作为参数传入。

最左侧传入的参数self,是实例本身。

class ClassA(object):

    def func_a(self):
        print('Hello Python')

if __name__ == '__main__':
    # 使用实例调用实例方法
    ca = ClassA()
    ca.func_a()
    # 如果使用类直接调用实例方法,需要显式地将实例作为参数传入
    ClassA.func_a(ca)

理解 if __name__ == ‘__main__’

有句话经典的概括了这段代码的意义:

“Make a script both importable and executable”

意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可执行(可以被调用,也可被单独执行)。

这句话,可能一开始听的还不是很懂。下面举例说明:

先写一个模块:


# module.py
def main(): 
    if __name__ == '__main__': 
        print "we are in %s"%__name__   
main()

这个函数定义了一个 main 函数,我们执行一下该py文件发现结果是打印出 we are in __main__,说明我们的 if 语句中的内容被执行了,调用了 main()

但是如果我们从另我一个模块导入该模块,并调用一次main()函数会是怎样的结果呢?

# anothermodle.py
from module import main
main()

其执行的结果是:we are in module

但是没有显示 we are in __main__,也就是说模块 __name__ = ‘__main__’ 下面的函数没有执行。

这样既可以让“模块”文件运行,也可以被其他模块引入,而且不会执行函数2次。这才是关键。

总结一下:

如果我们是直接执行某个 .py 文件的时候,该文件中那么 __name__ == ‘__main__’True,但是我们如果从另外一个 .py 文件通过 import 导入该文件的时候,这时 __name__ 的值就是我们这个 py 文件的名字而不是 __main__

这个功能还有一个用处:调试代码的时候,在 if __name__ == ‘__main__’ 中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!

from numpy import * 和直接 import numpy 的区别是,前者直接将包中的所有模块内容(函数

类、变量等)导入程序上下文,而后者仅仅是导入了这个模块,使用模块内的内容时,还需要使用模块名向下查找,所以我们一般会写成 import numpy as np 的形式,以方便后续的引用。不太推荐使用 from numpy imort * 的写法,直接使用模块内的变量,对于程序后续维护和理解不友好。