目录
Python的类提供了面向对象编程的所有基本功能:
- Object类是所有类的父类(不需要明确指定);
- 类允许继承多个基类(使用逗号分割);
- 派生类可覆盖基类中任何方法;
类基础
类支持两种操作:
- 实例化:
inst = clsName(…)
; - 属性引用: 使用实例对象或类(类属性)引用对象;
类定义
类通过class定义,里面有属性与方法。
class ClassName:
<statements>
def funs(self, arg):
# self is the instance of class
<fun statements>
@classmethod
def clsFuns(cls, arg):
# cls is the class
<statements>
@staticmethod
def staticFun(arg):
# 与普通函数类似
<statements>
类继承
Python支持多继承,多个父类之间通过逗号分割。若是父类中有相同的方法名,而在子类使用时未指定,Python从左到右依次查找父类中是否包含方法(即优先使用排在前面的父类中的方法)。
在多重继承(特别是有菱形继承时),查找关系会更复杂;具体是通过MRO(Method Resolution Order)
来解决方法调用二义性问题的;而MRO又是使用的C3算法(拓扑排序+深度优先搜索:依次取出入度为0的节点,取出节点后即把其相关的边去掉,然后递归处理)处理顺序的。
子类中要调用基类中同名方法需:baseCls.fun
或super().fun
;
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
<statement-N>
变量与可访问性
变量(属性)分类:
- 类变量:直接定义在类中,为所有类对象共享;通过类名访问
clsName.var
; - 实例变量:每个实例独有的数据(在
__init__
方法中定义、初始化);通过实例对象访问inst.var
;
Python中的可访问性是通过约定来实现的:
- 私有属性:以两个下划线开始的,
__var
; - 保护属性:以一个下划线开始的,
_var
;只能自身与子类可访问; - 普通属性:以字母等开始的。
类专有方法
Python中通过约定一些专有的方法来增强类的功能:
__init__
:构造函数,在生成对象时调用(实例变量也在此函数中定义);__del__
:析构函数,释放对象时使用;__repr__
:打印(若有__str__
,则先尝试str),转换;__setitem__
:按照索引赋值;__getitem__
:按照索引取值;__len__
:获取长度,内置函数len()
使用;__cmp__
:比较运算;__call__
:函数调用(对象看作一个算子);__add__
:加运算;__sub__
:减运算;__mul__
:乘运算;__div__
:除运算;__mod__
:求余运算;__pow__
:乘方运算;
repr与str:repr()与str()为内置函数,对应类中的__repr__
与__str__
来处理字符串:
- repr对Python(程序员)友好,生成的字符串应可通过eval()重构对象;
- str为用户友好,方便用户理解的输出;
- print时先查看
__str__
,若未定义,再查看__repr__
;
类进阶
类方法
Python类中有三种方式定义方法:
- 常规方式:定义实例方法,第一个参数表示实例(一般约定为self);
@classmethod
修饰方式:定义类方法,第一个参数表示类(一般约定为cls);@staticmethod
修饰方式:定义静态方法;
以下是三种函数的定义及调用方式:
class MethodTest:
index = 0
def __init__(self):
self.index = -1
def instFun(self, index):
print('instance function:', index, self.index, MethodTest.index)
self.index = index
@classmethod
def classFun(cls, index):
print('class function:', index, cls.index)
cls.index = index
@staticmethod
def staticFun(index):
print('static function:', index, MethodTest.index)
if __name__=="__main__":
mtest = MethodTest()
mtest.instFun(1) # instance function: -1 0
mtest.classFun(2) # class function: 0 0
mtest.staticFun(3) # static function: 3 2
MethodTest.classFun(4) # class function: 2 2
MethodTest.instFun(mtest, 5) # instance function: 1 4
MethodTest.staticFun(6) # static function: 6 4
通过实例对象可以直接调用三类方法;通过类名可以直接调用静态方法与类方法,若要调用实例方法,还需传递一个实例对象。
类属性
通过get/set
属性,可以增强对变量的控制,便于后续修改与扩展。但类中的属性(get/set
)方法不要手动去实现,应使用@propery
来修饰实现:
- 把一个get方法变成属性,只需增加
@propery
修饰即可; @propery
本身又创建了另一个装饰器,负责把一个setter方法变成属性赋值:若不增加setter方法,则属性将是只读的。
以下是属性定义的示例:通过@propery
定义了count的get属性,然后就可以通过@count.setter
来定义set属性了。
class CountProp:
def __init__(self):
self._count=0
@property
def count(self):
return self._count
@count.setter
def count(self, newCount):
self._count = newCount
def __getattr__(self, name):
print('call __getattr__:', name)
value = '{} value for {}'.format(self._index, name)
self._index += 1
setattr(self, name, value)
return value
def __getattribute__(self, name):
print('call __getattribute__:', name)
try:
return super().__getattribute__(name)
except AttributeError:
# setattr(self, name, value)
raise
cPro = CountProp()
print(cPro.count) # 0
cPro.count = 10
print(cPro.count) # 10
类中的属性除在类定义(或初始化时)定义外,还可以在任何时候绑定(在给不存在的属性赋值时会自动绑定)。属性的获取与赋值规则为:
- 先调用
getattribute
:无论属性是否存在(若属性不存在,需抛出AttributeError异常),因此不能在此方法内引用属性,否则会引起循环递归。因此若要访问属性,则调用super().XXX来避免递归。 - 属性不存在时(对象的实例字典中查询不到属性时),若类定义了
getattr
则调用此方法; - 当给属性赋值或调用setattr函数时,都会触发
setattr
方法。