Python面试题系列之13: 类方法、实例方法、静态方法
Question
请简述下Python 中类方法
、实例方法
、静态方法
有何区别?
知识点详解
在Python的类中,实例方法(instance method),类方法(class method)与静态方法(static method)经常容易混淆,本文中通过几个例子逐一介绍下。
实例方法·instance method
Python的实例方法
,用得最多也最为常见。先来看这个例子。
class MyCar:
def __init__(self, color):
self.color = color
def car_color(self):
print(self.color)
mycar1 = MyCar('red')
调用一下看看
>>> mycar1.instance_m()
red
上述例子中,car_color为一个实例方法。实例方法第一个参数为self,当使用mycar1.car_color()调用实例方法时,实例mycar1会传递给self参数,这样self参数就可以引用当前正在调用实例方法的实例。
类方法·class method
Python 的类方法
,采用装饰器@classmethod
来定义,我们来看这个例子。
class MyCar:
num = 0
def __init__(self):
MyCar.num += 1
@classmethod
def car_num(cls):
return cls.num
car1 = MyCar()
car2 = MyCar()
我们调用一下看看
>>> MyCar.car_num()
2
这个例子中我们来统计类MyCar实例的个数。于是定义了一个类变量num来存放,然后我们通过装饰器@classmethod的使用,方法car_num被定义成一个类方法。在调用类方法时,Python会将类(class MyCar)传递给cls,这样在car_num内部就可以引用类变量num。
由于在调用类方法时,只需要将类型本身传递给类方法,因此,我们既可以通过类、也可以通过实例,来调用类方法。
静态方法·static method
Python中还有一种方法,采用装饰器@staticmethod
来定义,我们称之为静态方法
。先来看看这个例子。
我们定义一个“三角形”的类,通过传入3条边的长度来构造出三角形,并提供计算周长的方法。
from math import sqrt
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def perimeter(self):
return self._a + self._b + self._c
但是小学数字老师告诉我们,任意给的三条边长未必能构造出三角形。三条边是需要满足一定条件的:
(a + b > c) and (b + c > a) and (a + c > b)
所以我们可以先写一个方法,来验证三条边长是否可以构成三角形。
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),即这个方法应属于三角形类,但并不属于三角形对象的。这就用到了我们的静态方法。具体代码如下:
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
return self._a + self._b + self._c
然后我们给定三个边长,并计算由其组成的三角形的周长。
a, b, c = 6, 7, 8
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
else:
print('无法组成三角形')
我们介绍完这三种方法定义及使用场景的区别,接下来我们看看它们在调用时的差异。
调用上的区别
还是通过一个例子来说明,这里定义一个MyClass类,同时定义了实例、类、静态三种方法。
class MyClass(object):
def instance_m(self, x):
print(f'executing instance_m({self}, {x})')
@classmethod
def class_m(cls, x):
print(f'executing class_m({cls}, {x})')
@staticmethod
def static_m(x):
print(f'executing static_m({x})')
mc = MyClass()
接下来我们调用这三种实例,分析下输出的结果
# 调用实例方法
mc.instance_m(666)
print(mc.instance_m)
print('-----------------------------------------')
# 调用类方法
mc.instance_m(666)
MyClass.class_m(666)
print(mc.class_m)
print('-----------------------------------------')
# 调用静态方法
mc.static_m(666)
MyClass.static_m('人人都是Pythonista')
print(mc.static_m)
以下是执行结果
executing instance_m(<__main__.MyClass object at 0x0000023DA63E7E48>, 666)
<bound method MyClass.instance_m of <__main__.MyClass object at 0x0000023DA63E7E48>>
-----------------------------------------
executing class_m(<class '__main__.MyClass'>, 666)
executing class_m(<class '__main__.MyClass'>, 666)
<bound method MyClass.class_m of <class '__main__.MyClass'>>
-----------------------------------------
executing static_m(666)
executing static_m(人人都是Pythonista)
<function MyClass.static_m at 0x0000023DA63E6048>
对于实例方法,调用时会把实例mc
作为第一个参数传递给self
参数。因此,调用mc.instance_m(666)时输出了实例mc的地址。
对于类方法,调用时会把类MyClass
作为第一个参数传递给cls
参数。因此,调用mc.instance_m(666)时输出了MyClass类型信息。 前面提到,可以通过类也可以通过实例来调用类方法,在上述代码中,我们再一次进行了验证。
对于静态方法,调用时并不需要传递类或者实例。其实,静态方法很像我们在类外定义的函数,只不过静态方法可以通过类或者实例来调用而已。
上面这个例子,我们发现类方法、静态方法都可以通过类直接调用,那么实例方法呢?
>>> MyClass.instance_m(666)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-23-082125d2908b> in <module>
----> 1 MyClass.instance_m(666)
TypeError: instance_m() missing 1 required positional argument: 'x'
实例方法不能通过类直接调用,它需要一个实例绑定,比如写成如下形式:
>>> MyClass.instance_m(mc, 666)
executing instance_m(<__main__.MyClass object at 0x0000023DA63E7E48>, 666)
从上面的例子,我们还可以看到:
实例方法instance_m与实例mc进行了绑定。
<bound method MyClass.instance_m of <__main__.MyClass object at 0x0000023DA63E7E48>>
类方法class_m与类MyClass进行了绑定。
<bound method MyClass.class_m of <class '__main__.MyClass'>>
静态方法static_m并不会与类MyClass或者实例mc绑定。
<function MyClass.static_m at 0x0000023DA63E6048>
Answer
实例方法,Instance methods,需要一个类实例,并且可以通过self
访问实例。
只有实例对象可以调用。
类方法,Class methods,类方法不是绑定到对象上,而是绑定在类上的方法。不需要类实例。他们无法访问实例,但它们可以通过cls访问类本身。
在定义时需在上方使用@classmethod
进行装饰,类对象和实例对象都可调用。
静态方法,Static methods,在类中无需实例参与即可调用,无权访问cls
或self
。它们像常规函数一样工作,但属于类的命名空间。
在定义时需在其上方使用@staticmethod
进行装饰,类对象和实例对象都可调用。
后记
以上就是Python中的实例方法、类方法与静态方法的区别,在面试时可以从定义及调用时差异入手,来解释这三种方法。欢迎大家在评论区留言说出自己的看法。好了,以上就是本篇全部内容。
备注:本篇首发于知识星球「人人都是Pythonista」。