就凭这3点,可以完全理解Python的类方法与静态方法

开发 后端
如果只是描述类的一般的动作,而且类的不同实例,动作的表现可能还不同,那么就用成员方法,例如,move(移动)、fly(飞)、getAge(如不同Person类的实例,可能年龄是不同的)等。

[[386004]]

在Python语言中有如下3种方法:

  • 成员方法
  • 类方法(classmethod)
  • 静态方法(staticmethod)

可能很多同学不清楚这3种方法,尤其是后两类方法到底有什么不同。为此,本文将对这3种方法做一次敲骨沥髓的深度剖析。

先说一下这3种方法的差异,了解差异后,就自然了解他们的区别了。

这3种方法有如下3点差异:

  • 方法定义
  • 调用方式
  • 方法归属

1. 方法定义

这3种方法在定义上有如下2点不同。

(1)是否使用装饰器

成员方法不需要使用任何装饰器,直接使用def关键字定义方法即可,代码如下:

  1. def method(self, a, b, c): 
  2.     pass 

类方法必须使用@classmethod装饰器修饰,代码如下:

  1. @classmethod 
  2. def method(cls, a, b, c): 
  3.     pass 

静态方法必须使用@staticmethod装饰器修饰,代码如下:

  1. @staticmethod 
  2. def method(a, b, c): 
  3.     pass 

(2)参数不同

成员方法与类方法,除正常的方法参数外,都必须多加一个参数,这个参数必须是方法的第1个参数。参数可以是任意名,但通常成员方法的第1个参数名是self,类方法的第1个参数名是cls。而静态方法不需要加额外的参数。见前面代码中的method方法。

self和cls分别表示类实例和类本身,这一点在后面会详细介绍。

下面看一个完整定义这3种方法的代码:

  1. class MyClass(object): 
  2.     # 成员方法    
  3.     def foo(self, x): 
  4.         print("executing foo(%s, %s)" % (self, x)) 
  5.     # 类方法 
  6.     @classmethod 
  7.     def class_foo(cls, x): 
  8.         print("executing class_foo(%s, %s)" % (cls, x)) 
  9.     # 静态方法  
  10.     @staticmethod 
  11.     def static_foo(x): 
  12.         print("executing static_foo(%s)" % x) 

2. 调用方式

(1)调用成员方法

成员方法只能通过类实例调用,代码如下:

  1. my = MyClass() 
  2.  
  3. my.foo(20) 

在定义成员方法时,第一个参数是表示类实例的self,这个参数并不需要在调用时显式指定,而是由Python运行时自动处理。对于上面的调用代码,Python运行时会自动将表示MyClass实例的my传入foo方法。所以my就是foo方法中第一个参数self的值。通过self,在方法内部可以引用MyClass实例的其他成员。

执行这段代码,会输出如下内容。很明显,self是一个对象,首地址是0x7f7f1003df70

  1. executing foo(<__main__.MyClass object at 0x7f7f1003df70>, 20) 

(2)调用类方法

类方法可以通过类实例调用,也可以直接通过类本身调用,代码如下:

  1. my = MyClass() 
  2. # 通过类实例调用 
  3. my.class_foo(20) 
  4. # 通过类本身调用 
  5. MyClass.class_foo(20) 

执行这段代码,会输出如下内容:

  1. executing class_foo(<class '__main__.MyClass'>, 20) 
  2. executing class_foo(<class '__main__.MyClass'>, 20) 

很明显,class_foo方法的cls参数不再是类的实例(因为没有对象地址),而是MyClass类本身。所以不管使用哪一种方式调用类方法,传入class_foo方法第1个参数的值都是类本身。所以通过类方法,可以获取类的静态资源,与直接引用MyClass是一样的。

(3)调用静态方法

调用静态方法与调用类方法一样,都可以通过类实例或类本身调用,从这一点看不出来哪一个是类方法,哪一个是静态方法,代码如下:

  1. my = MyClass() 
  2. MyClass.static_foo(20) 
  3. my.static_foo('hello'

执行这段代码,会输出如下内容:

  1. executing static_foo(20) 
  2. executing static_foo(hello) 

由于在定义静态方法时并没有指定任何额外的参数,所以静态方法并没有与类或类实例绑定,当然,在静态方法中,仍然可以通过MyClass引用类中的静态成员。

3. 方法归属

方法归属是这3种方法的重要区别,可以分别将这3种方法作为属性输出,看看是什么结果。

  1. my = MyClass()) 
  2. # 输出成员方法 
  3. print(my.foo) 
  4. # 输出类方法 
  5. print(my.class_foo) 
  6. # 输出静态方法 
  7. print(my.static_foo) 

执行这段代码,会输出如下内容:

  1. <bound method MyClass.foo of <__main__.MyClass object at 0x7f7f1003df70>> 
  2. <bound method MyClass.class_foo of <class '__main__.MyClass'>> 
  3. <function MyClass.static_foo at 0x7f7f1003ad30> 

从输出结果可以看到,成员方法绑定到了类实例中(该方法属于类实例),类方法与类本身绑定,而静态方法就是一个独立的对象(因为有对象首地址),不属于任何类或实例。

从以上3个方法我们已经可以得出classmethod方法与staticmethod的区别,下面总结一下:

4. 总结

(1)共同点

classmethod方法与staticmethod方法的共同点只有一个,就是调用时,既可以使用类实例,也可以直接用类本身调用。所以从调用上,根本分不出是类方法,还是静态方法。

(2)差异

类方法顾名思义,是与类绑定的,相当于下面的调用方式:

  1. def process(cls, x): 
  2.     print(cls,x) 
  3. MyClass.process = process 
  4. # 调用process方法时直接传入了MyClass 
  5. MyClass.process(MyClass, 20) 

只是类方法在调用时自动传入了MyClass,而上面的代码是显式传入MyClass的,但最终效果是完全一样的。

而静态方法其实就是一个寄居蟹,完全不属于它的宿主。只是寄居在类中。换句话说,直接将静态方法从类中移出来作为独立的函数,完全不需要修改一行代码就可以直接运行。因为静态方法不会访问类中的任何成员,当然,可能访问类的静态成员,但也是使用类本身(如MyClass),这种访问方式,独立的函数同样可以。

其实Python提供静态方法倒不是非常必要,不过Java就很有必要了。由于Python支持独立的函数形式,所以不使用静态方法,也可以使用独立的函数。通常独立的函数可以全局访问(在一个模块访问另外一个模块中的函数)。而Java是纯面向对象语言,并不支持独立函数。所以为了实现这种全局调用的效果,Java类提供了静态方法,可以通过MyClass.process(...)的形式在其他类访问MyClass中的process方法。

不过Python中的静态方法到是有一个作用,就是分组。如果模块中有大量的独立函数,而且这些独立函数的功能可能完全不同,就显得比较乱,所以通常的做法是将这些独立函数作为Python类的静态方法,将同一类型的独立函数放到一个类中,这样就会让整个代码结构显得更有调理。就像将文件存放在硬盘上一样,如果将所有的文件都放在一个目录中,找文件会很费劲。所以需要将同一类文件放到特定的目录中,这样看起来目录结构更清晰。所以静态方法与Python类,就相当于文件与目录的关系,主要就是起到分类的作用。

(3)使用场景

如果只是描述类的一般的动作,而且类的不同实例,动作的表现可能还不同,那么就用成员方法,例如,move(移动)、fly(飞)、getAge(如不同Person类的实例,可能年龄是不同的)等。

类方法与静态方法大多数时候可以互换,但如果想让方法保持独立,应该使用静态方法,因为静态方法不需要多余的参数接收类或类实例。

本文转载自微信公众号「极客起源」,可以通过以下二维码关注。转载本文请联系极客起源公众号。

 

责任编辑:武晓燕 来源: 极客起源
相关推荐

2021-04-21 10:01:53

Python类方法静态方法

2009-08-28 12:41:49

静态方法与非静态方法

2018-04-26 09:00:00

2022-08-13 13:00:43

SQL语言

2021-04-14 10:59:28

JavaScript 前端this

2019-08-20 14:40:35

Redis数据库

2018-11-15 10:04:02

机柜方法布线

2015-11-18 11:56:23

Linux

2015-09-18 11:14:54

2021-09-13 07:53:30

安全

2021-10-08 20:11:40

类方法静态

2018-05-04 11:22:21

APP运营pushapp卸载

2010-11-09 16:14:52

卸载SQL Serve

2011-11-11 10:35:04

2018-08-10 00:03:08

网速网络连接Wi-Fi

2011-09-06 14:08:22

IIS

2010-08-17 14:47:49

FirefoxJavascript

2024-04-09 08:47:34

PandasRollingPython

2018-09-12 15:16:19

数据中心网络机房

2021-08-16 09:35:34

Collections Java开发
点赞
收藏

51CTO技术栈公众号