1. 在python类中有两个装饰器,staticmethod和classmethod, 它们的作用似乎明确又模糊。由于缺乏使用场景,我们对这两个装饰器(功能)的理解总是不够深入,不够全面。本文试图对它们进行一次全方位的考查,进而对它们的使用有一些更好的体会。
本人水平比较渣,以上良好的愿望应该只能是个愿望,这篇文章有很多不完善的地方,请各位读者不吝指出。
2. 鸭子测试(Duck Test)是对一种归纳推理方式的幽默的说法,它可以解释为:
如果它看起像鸭子,叫起来像鸭子,游起来像鸭子,那么它可能就是鸭子。
这个测试表明人能够通过观察未知事物的明显外在特征来推断该事物的本质。有时被用来解决看似深奥但其实并不深奥的问题。
很多时候,我们知道有一个事物,我们知道它的的名字,知道它出现在一些场合,但是我们并不明白它到底是怎么回事。这往往是由于我们对它的特征不清楚。通过观察这个事物的行为特征,或许我们就能逐渐了解它。
3. 实例方法。通常,在python类中定义的普通方法就是实例方法。它的语法如下:
实例方法的参数中有一个必不可少的参数self,它必须位于参数列表的第一个位置,它相当于该方法所属的类在实例化后的实例本身(Instance),表示传入该方法所属类的一个实例(Python2中必须是该类的实例,Python3中可以是任何东西)。实例方法被调用时,参数self被隐式地传入方法中,无需显式指定。
既然叫做实例方法,那么它必须使用该类的实例才能调用( C().f() ),直接使用类来调用该方法是无效的( C.f() )。
4. staticmethod(静态方法),需要一个装饰器语法(@staticmethod)将一个普通方法(实例方法)转换为静态方法:
staticmethod 方法的参数是自定义的任何参数,当然也可以没有参数。它不需要一个隐含参数,像self这样的。这是它与实例方法和类方法之间在形式上的一个主要不同之处。
staticmethod即可以在类上调用(C.f())也可以在实例上调用(C().f()). 在实例上调用时,实例被忽略而类被使用---实际上还是类调用。
staticmethod不需要自身对象self和自身类的cls参数,没有对类或实例的依赖,它处理与类无关的信息,就像恰好放在类中的一个局部函数,但是它的功能又与类有某种关联。
如果需要在staticmethod中调用类的属性和方法,可以直接用类名.属性名/类名.方法名。
5. classmethod(类方法),需要一个装饰器语法(@classmethod)将一个普通方法(实例方法)转换为类方法:
classmethod需要接收一个隐含参数作为第一个参数,不同于实例方法的self, 类方法的隐含参数叫做cls(其实可以叫任何别的名字,这里主要是因为命名惯例), 表示传入该方法所属的类。
classmethod即可以在类上调用(C.f())也可以在实例上调用(C().f()). 在实例上调用时,实例被忽略而类被使用---实际上还是类调用。
如果classmethod是在派生类中被调用的,被隐式传入cls参数的是那个子类对象,而不是父类。
6. 应用举例。下面通过代码列举了staticmethod和classmethod的一些使用场合。
6.1 基本使用。定义一个实例方法,静态方法和类方法,并分别进行调用。
调用过程如下:
6.2 在派生类中使用父类的类方法。
调用如下:
6.3 将类方法用于工厂方法。这时 classmethod 的作用实际上是创建了类的实例, 而在这之前可以做一些预处理,比如设置变量信息。在下面的例子中,如果使用 @staticmethod代替,那我们不得不硬编码Pizza类名在函数中,这使得任何继承Pizza的类都不能使用我们这个工厂方法给它自己用。