首先应该明确,在面向对象程序设计中,函数和方法这两个概念是有本质区别的。方法一般指与特定实例绑定的函数,通过对象调用方法时,对象本身将被作为第一个参数传递过去,普通函数并不具备这个特点。
>>> class Demo:
pass
>>> t = Demo()
>>> def test(self, v):
self.value = v
>>> t.test = test #动态增加普通函数
>>> t.test
<function test at 0x00000000034B7EA0>
>>> t.test(t, 3)
>>> print(t.value)
3
>>> import types
>>> t.test = types.MethodType(test, t) #动态增加绑定的方法
>>> t.test
<bound method test of <__main__.Demo object at 0x000000000074F9E8>>
>>> t.test(5)
>>> print(t.value)
5
Python类的成员方法常用的类型有公有方法、私有方法、静态方法、类方法和抽象方法等等。公有方法、私有方法和抽象方法一般是指属于对象的实例方法,私有方法的名字以两个下划线“__”开始,而抽象方法一般定义在抽象类中并且要求派生类必须重新实现。每个对象都有自己的公有方法和私有方法,在这两类方法中都可以访问属于类和对象的成员。公有方法通过对象名直接调用,私有方法不能通过对象名直接调用,只能在其他实例方法中通过前缀self进行调用或在外部通过特殊的形式来调用。另外,Python中的类还支持大量的特殊方法,这些方法的两侧各有两个下划线“__”,往往与某个运算符和内置函数相对应。
所有实例方法(包括公有方法、私有方法、抽象方法和某些特殊方法)都必须至少有一个名为self的参数,并且必须是方法的第一个形参(如果有多个形参的话),self参数代表对象自身。在类的实例方法中访问实例属性时需要以self为前缀,但在外部通过对象名调用对象方法时并不需要传递这个参数。如果在外部通过类名调用属于对象的公有方法,需要显式为该方法的self参数传递一个对象名,用来明确指定访问哪个对象的数据成员。
静态方法和类方法都可以通过类名和对象名调用,但不能直接访问属于对象的成员,只能访问属于类的成员。另外,静态方法和类方法不属于任何实例,也不需要绑定到实例,也不依赖与实例的状态,与实例方法相比能够减少很多开销。一般以cls作为类方法的第一个参数表示该类自身,在调用类方法时不需要为该参数传递值,而静态方法则可以不接收任何参数。例如下面的代码所演示:
>>> class Root:
__total = 0
def __init__(self, v): #构造方法
self.__value = v
Root.__total += 1
def show(self): #普通实例方法
print('self.__value:', self.__value)
print('Root.__total:', Root.__total)
@classmethod #修饰器,声明类方法
def classShowTotal(cls): #类方法,以cls作为第一个参数的名字
print(cls.__total)
@staticmethod #修饰器,声明静态方法
def staticShowTotal(): #静态方法,可以没有参数
print(Root.__total)
>>> r = Root(3)
>>> r.classShowTotal() #通过对象来调用类方法
1
>>> r.staticShowTotal() #通过对象来调用静态方法
1
>>> rr = Root(5)
>>> Root.classShowTotal() #通过类名调用类方法
2
>>> Root.staticShowTotal() #通过类名调用静态方法
2
>>> Root.show() #试图通过类名直接调用实例方法,失败
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
Root.show()
TypeError: unbound method show() must be called with Root instance as first argument (got nothing instead)
>>> Root.show(r) #可以通过这种方法来调用方法并访问实例成员
self.__value: 3
Root.__total: 2
>>> r.show()
self.__value: 3
Root.__total: 2
抽象方法一般在抽象类中定义,并且要求在派生类中必须重新实现,否则不允许派生类创建实例。
import abc
class Foo(metaclass=abc.ABCMeta): #抽象类
def f1(self): #普通实例方法
print(123)
def f2(self): #普通实例方法
print(456)
@abc.abstractmethod #抽象方法
def f3(self):
raise Exception('You musr reimplement this method.')
class Bar(Foo):
def f3(self): #必须重新实现基类中的抽象方法
print(33333)
b = Bar()
b.f3()