0%

python 面向对象编程

Python 面向对象编程和高级编程

面对对象编程

  1. 面向对象的设计思想是抽象出 Class,根据 Class 创建 Instance

  2. 面向对象的抽象程度又比函数要高,因为一个 Class 既包含数据,又包含操作数据的方法 Method

  3. 数据封装、继承和多态是面向对象的三大特点

    class Student(object):
    
     def __init__(self, name, score):
         self.name = name
         self.score = score
    
     def print_score(self):
         print('%s: %s' % (self.name, self.score))

类和实例

  1. 面向对象最重要的概念就是类 Class 和实例 Instance
  2. 类是抽象的模板
  3. 实例是根据类创建出来的一个个具体的 对象
  4. 定义类是通过 class 关键字
    class Student(object):
     pass

    class 后面紧接着是类名,即 Student,类名通常是大写开头的单词,紧接着是 (object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用 object 类,这是所有类最终都会继承的类

  5. 创建实例是通过 类名 +() 实现的
    bart = Student()
  6. 通过方法 __init__ 绑定一些属性
    def __init__(self, name, score):
     self.name = name
     self.score = score
  7. 创建实例的时候,必须传入与 __init__方法 匹配的参数,但 self 不需要传
    bart = Student('Bart Simpson', 59)

访问限制

  1. 属性名前加上 __ 将属性编程私有变量,只有内部可以访问,外部不能访问

    有些时候会看到以一个下划线开头的实例变量名,比如 _name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,虽然我可以被访问,但是,请把我视为私有变量,不要随意访问

继承和多态

  1. 从某个现有的 class 继承,新的 class 称为子类 Subclass,而被继承的 class 称为基类、父类或超类 Base class、Super class,继承使子类获得了父类的全部功能
  2. 当子类和父类都存在相同的方法时,子类的方法覆盖了父类的方法,在代码运行的时候,总是会调用子类的方法。即:多态
  3. 在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行

多态示例

获取对象信息

  1. 基本类型都可以用 type() 判断
  2. class 类型使用 isinstance() 判断

实例属性和类属性

  1. 实例和类都可以绑定属性
  2. 实例绑定的属性只有这个实例可以访问
  3. 类绑定的属性,所有实例都可以访问
  4. 如果实例与类绑定的属性名一样,类属性会将实例属性覆盖掉
    >>> class Student(object):
    ...     name = 'Student'
    ...
    >>> s = Student() # 创建实例 s
    >>> print(s.name) # 打印 name 属性,因为实例并没有 name 属性,所以会继续查找 class 的 name 属性
    Student
    >>> print(Student.name) # 打印类的 name 属性
    Student
    >>> s.name = 'Michael' # 给实例绑定 name 属性
    >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的 name 属性
    Michael
    >>> print(Student.name) # 但是类属性并未消失,用 Student.name 仍然可以访问
    Student
    >>> del s.name # 如果删除实例的 name 属性
    >>> print(s.name) # 再次调用 s.name,由于实例的 name 属性没有找到,类的 name 属性就显示出来了
    Student

面对对象高级编程

使用 __slots__

python 作为动态语言可以动态对实例或类添加属性或方法,为了限制这种特性,可以使用 __slots__

class Student(object):
    __slots__ = ('name', 'age') # 用 tuple 定义允许绑定的属性名称

使用 __slots__ 要注意, __slots__ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:

>>> class GraduateStudent(Student):
...     pass
...
>>> g = GraduateStudent()
>>> g.score = 9999

除非在子类中也定义 __slots__,这样,子类实例允许定义的属性就是自身的 __slots__ 加上父类的 __slots__

使用 @property

python 内置的 @property 装饰器把一个方法变成属性调用的:

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

可以定义只读属性

多重继承

在设计类的继承关系时,通常,主线都是单一继承下来的,例如, Ostrich 继承自 Bird。但是,如果需要 混入 额外的功能,通过多重继承就可以实现,比如,让 Ostrich 除了继承自 Bird 外,再同时继承 Runnable。这种设计通常称之为 MixIn

class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass

这样一来,我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。

定制类

枚举类

python 提供 Enum 来定义枚举类

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

value 属性则是自动赋给成员的 int 常量,默认从 1 开始计数

如果需要更精确地控制枚举类型,可以从 Enum 派生出自定义类:

from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun 的 value 被设定为 0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

@unique 装饰器可以帮助我们检查保证没有重复值

元类