目录

类基础

类定义

类继承

变量与可访问性

类专有方法

类进阶

类方法

类属性


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.funsuper().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方法。