Python的类提供了面向对象编程的所有基本功能:
Object类是所有类的父类(不需要明确指定);
类允许继承多个基类(使用逗号分割);
派生类可覆盖基类中任何方法;
类基础
类支持两种操作:
实例化: inst = clsName(…);
属性引用: 使用实例对象或类(类属性)引用对象;
类定义
类通过class定义,里面有属性与方法。
class ClassName:
def funs(self, arg):
# self is the instance of class
@classmethod
def clsFuns(cls, arg):
# cls is the class
@staticmethod
def staticFun(arg):
# 与普通函数类似
类继承
Python支持多继承,多个父类之间通过逗号分割。若是父类中有相同的方法名,而在子类使用时未指定,Python从左到右依次查找父类中是否包含方法(即优先使用排在前面的父类中的方法)。
在多重继承(特别是有菱形继承时),查找关系会更复杂;具体是通过MRO(Method Resolution Order)来解决方法调用二义性问题的;而MRO又是使用的C3算法(拓扑排序+深度优先搜索:依次取出入度为0的节点,取出节点后即把其相关的边去掉,然后递归处理)处理顺序的。
子类中要调用基类中同名方法需:baseCls.fun或super().fun;
class DerivedClassName(Base1, Base2, Base3):
.
变量与可访问性
变量(属性)分类:
类变量:直接定义在类中,为所有类对象共享;通过类名访问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方法。